Merge "Fix for setting Toolbar content descriptions" into lmp-preview-dev
diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java
index d08978bd..5e4ddd0 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -15,14 +15,23 @@
  */
 package android.app;
 
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
 import android.graphics.Rect;
+import android.graphics.drawable.BitmapDrawable;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.ResultReceiver;
 import android.transition.Transition;
 import android.transition.TransitionSet;
 import android.util.ArrayMap;
+import android.util.Pair;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
 import android.view.Window;
 import android.widget.ImageView;
 
@@ -181,6 +190,11 @@
      */
     public static final int MSG_CANCEL = 106;
 
+    /**
+     * When returning, this is the destination location for the shared element.
+     */
+    public static final int MSG_SHARED_ELEMENT_DESTINATION = 107;
+
     final private Window mWindow;
     final protected ArrayList<String> mAllSharedElementNames;
     final protected ArrayList<View> mSharedElements = new ArrayList<View>();
@@ -334,6 +348,210 @@
 
     protected abstract Transition getViewsTransition();
 
+    private static void setSharedElementState(View view, String name, Bundle transitionArgs,
+            int[] parentLoc) {
+        Bundle sharedElementBundle = transitionArgs.getBundle(name);
+        if (sharedElementBundle == null) {
+            return;
+        }
+
+        if (view instanceof ImageView) {
+            int scaleTypeInt = sharedElementBundle.getInt(KEY_SCALE_TYPE, -1);
+            if (scaleTypeInt >= 0) {
+                ImageView imageView = (ImageView) view;
+                ImageView.ScaleType scaleType = SCALE_TYPE_VALUES[scaleTypeInt];
+                imageView.setScaleType(scaleType);
+                if (scaleType == ImageView.ScaleType.MATRIX) {
+                    float[] matrixValues = sharedElementBundle.getFloatArray(KEY_IMAGE_MATRIX);
+                    Matrix matrix = new Matrix();
+                    matrix.setValues(matrixValues);
+                    imageView.setImageMatrix(matrix);
+                }
+            }
+        }
+
+        float z = sharedElementBundle.getFloat(KEY_TRANSLATION_Z);
+        view.setTranslationZ(z);
+
+        int x = sharedElementBundle.getInt(KEY_SCREEN_X);
+        int y = sharedElementBundle.getInt(KEY_SCREEN_Y);
+        int width = sharedElementBundle.getInt(KEY_WIDTH);
+        int height = sharedElementBundle.getInt(KEY_HEIGHT);
+
+        int widthSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
+        int heightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY);
+        view.measure(widthSpec, heightSpec);
+
+        int left = x - parentLoc[0];
+        int top = y - parentLoc[1];
+        int right = left + width;
+        int bottom = top + height;
+        view.layout(left, top, right, bottom);
+    }
+
+    protected ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> setSharedElementState(
+            Bundle sharedElementState, final ArrayList<View> snapshots) {
+        ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalImageState =
+                new ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>>();
+        if (sharedElementState != null) {
+            int[] tempLoc = new int[2];
+            for (int i = 0; i < mSharedElementNames.size(); i++) {
+                View sharedElement = mSharedElements.get(i);
+                String name = mSharedElementNames.get(i);
+                Pair<ImageView.ScaleType, Matrix> originalState = getOldImageState(sharedElement,
+                        name, sharedElementState);
+                if (originalState != null) {
+                    originalImageState.put((ImageView) sharedElement, originalState);
+                }
+                View parent = (View) sharedElement.getParent();
+                parent.getLocationOnScreen(tempLoc);
+                setSharedElementState(sharedElement, name, sharedElementState, tempLoc);
+            }
+        }
+        mListener.setSharedElementStart(mSharedElementNames, mSharedElements, snapshots);
+
+        getDecor().getViewTreeObserver().addOnPreDrawListener(
+                new ViewTreeObserver.OnPreDrawListener() {
+                    @Override
+                    public boolean onPreDraw() {
+                        getDecor().getViewTreeObserver().removeOnPreDrawListener(this);
+                        mListener.setSharedElementEnd(mSharedElementNames, mSharedElements,
+                                snapshots);
+                        return true;
+                    }
+                }
+        );
+        return originalImageState;
+    }
+
+    private static Pair<ImageView.ScaleType, Matrix> getOldImageState(View view, String name,
+            Bundle transitionArgs) {
+        if (!(view instanceof ImageView)) {
+            return null;
+        }
+        Bundle bundle = transitionArgs.getBundle(name);
+        if (bundle == null) {
+            return null;
+        }
+        int scaleTypeInt = bundle.getInt(KEY_SCALE_TYPE, -1);
+        if (scaleTypeInt < 0) {
+            return null;
+        }
+
+        ImageView imageView = (ImageView) view;
+        ImageView.ScaleType originalScaleType = imageView.getScaleType();
+
+        Matrix originalMatrix = null;
+        if (originalScaleType == ImageView.ScaleType.MATRIX) {
+            originalMatrix = new Matrix(imageView.getImageMatrix());
+        }
+
+        return Pair.create(originalScaleType, originalMatrix);
+    }
+
+    protected ArrayList<View> createSnapshots(Bundle state, Collection<String> names) {
+        int numSharedElements = names.size();
+        if (numSharedElements == 0) {
+            return null;
+        }
+        ArrayList<View> snapshots = new ArrayList<View>(numSharedElements);
+        Context context = getWindow().getContext();
+        int[] parentLoc = new int[2];
+        getDecor().getLocationOnScreen(parentLoc);
+        for (String name: names) {
+            Bundle sharedElementBundle = state.getBundle(name);
+            if (sharedElementBundle != null) {
+                Bitmap bitmap = sharedElementBundle.getParcelable(KEY_BITMAP);
+                View snapshot = new View(context);
+                Resources resources = getWindow().getContext().getResources();
+                if (bitmap != null) {
+                    snapshot.setBackground(new BitmapDrawable(resources, bitmap));
+                }
+                snapshot.setViewName(name);
+                setSharedElementState(snapshot, name, state, parentLoc);
+                snapshots.add(snapshot);
+            }
+        }
+        return snapshots;
+    }
+
+    protected static void setOriginalImageViewState(
+            ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalState) {
+        for (int i = 0; i < originalState.size(); i++) {
+            ImageView imageView = originalState.keyAt(i);
+            Pair<ImageView.ScaleType, Matrix> state = originalState.valueAt(i);
+            imageView.setScaleType(state.first);
+            imageView.setImageMatrix(state.second);
+        }
+    }
+
+    protected Bundle captureSharedElementState() {
+        Bundle bundle = new Bundle();
+        int[] tempLoc = new int[2];
+        for (int i = 0; i < mSharedElementNames.size(); i++) {
+            View sharedElement = mSharedElements.get(i);
+            String name = mSharedElementNames.get(i);
+            captureSharedElementState(sharedElement, name, bundle, tempLoc);
+        }
+        return bundle;
+    }
+
+    /**
+     * Captures placement information for Views with a shared element name for
+     * Activity Transitions.
+     *
+     * @param view           The View to capture the placement information for.
+     * @param name           The shared element name in the target Activity to apply the placement
+     *                       information for.
+     * @param transitionArgs Bundle to store shared element placement information.
+     * @param tempLoc        A temporary int[2] for capturing the current location of views.
+     */
+    private static void captureSharedElementState(View view, String name, Bundle transitionArgs,
+            int[] tempLoc) {
+        Bundle sharedElementBundle = new Bundle();
+        view.getLocationOnScreen(tempLoc);
+        float scaleX = view.getScaleX();
+        sharedElementBundle.putInt(KEY_SCREEN_X, tempLoc[0]);
+        int width = Math.round(view.getWidth() * scaleX);
+        sharedElementBundle.putInt(KEY_WIDTH, width);
+
+        float scaleY = view.getScaleY();
+        sharedElementBundle.putInt(KEY_SCREEN_Y, tempLoc[1]);
+        int height = Math.round(view.getHeight() * scaleY);
+        sharedElementBundle.putInt(KEY_HEIGHT, height);
+
+        sharedElementBundle.putFloat(KEY_TRANSLATION_Z, view.getTranslationZ());
+
+        if (width > 0 && height > 0) {
+            Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+            Canvas canvas = new Canvas(bitmap);
+            view.draw(canvas);
+            sharedElementBundle.putParcelable(KEY_BITMAP, bitmap);
+        }
+
+        if (view instanceof ImageView) {
+            ImageView imageView = (ImageView) view;
+            int scaleTypeInt = scaleTypeToInt(imageView.getScaleType());
+            sharedElementBundle.putInt(KEY_SCALE_TYPE, scaleTypeInt);
+            if (imageView.getScaleType() == ImageView.ScaleType.MATRIX) {
+                float[] matrix = new float[9];
+                imageView.getImageMatrix().getValues(matrix);
+                sharedElementBundle.putFloatArray(KEY_IMAGE_MATRIX, matrix);
+            }
+        }
+
+        transitionArgs.putBundle(name, sharedElementBundle);
+    }
+
+    private static int scaleTypeToInt(ImageView.ScaleType scaleType) {
+        for (int i = 0; i < SCALE_TYPE_VALUES.length; i++) {
+            if (scaleType == SCALE_TYPE_VALUES[i]) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
     private static class FixedEpicenterCallback extends Transition.EpicenterCallback {
         private Rect mEpicenter;
 
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index bc97852..a8617b8 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -18,11 +18,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
-import android.content.Context;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
 import android.graphics.Matrix;
-import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
 import android.os.Handler;
@@ -38,7 +34,6 @@
 import android.widget.ImageView;
 
 import java.util.ArrayList;
-import java.util.Collection;
 
 /**
  * This ActivityTransitionCoordinator is created by the Activity to manage
@@ -56,6 +51,7 @@
     private Handler mHandler;
     private boolean mIsCanceled;
     private ObjectAnimator mBackgroundAnimator;
+    private boolean mIsExitTransitionComplete;
 
     public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver,
             ArrayList<String> sharedElementNames,
@@ -76,6 +72,8 @@
                 }
             };
             mHandler.sendEmptyMessageDelayed(MSG_CANCEL, MAX_WAIT_MS);
+            Bundle state = captureSharedElementState();
+            mResultReceiver.send(MSG_SHARED_ELEMENT_DESTINATION, state);
         }
     }
 
@@ -98,9 +96,8 @@
                 break;
             case MSG_EXIT_TRANSITION_COMPLETE:
                 if (!mIsCanceled) {
-                    if (!mSharedElementTransitionStarted) {
-                        send(resultCode, resultData);
-                    } else {
+                    mIsExitTransitionComplete = true;
+                    if (mSharedElementTransitionStarted) {
                         onRemoteExitTransitionComplete();
                     }
                 }
@@ -183,6 +180,7 @@
         setViewVisibility(mSharedElements, View.VISIBLE);
         ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalImageViewState =
                 setSharedElementState(sharedElementState, sharedElementSnapshots);
+        requestLayoutForSharedElements();
 
         boolean startEnterTransition = allowOverlappingTransitions();
         boolean startSharedElementTransition = true;
@@ -200,6 +198,13 @@
         mResultReceiver = null; // all done sending messages.
     }
 
+    private void requestLayoutForSharedElements() {
+        int numSharedElements = mSharedElements.size();
+        for (int i = 0; i < numSharedElements; i++) {
+            mSharedElements.get(i).requestLayout();
+        }
+    }
+
     private Transition beginTransition(boolean startEnterTransition,
             boolean startSharedElementTransition) {
         Transition sharedElementTransition = null;
@@ -213,6 +218,19 @@
         }
 
         Transition transition = mergeTransitions(sharedElementTransition, viewsTransition);
+        if (startSharedElementTransition) {
+            if (transition == null) {
+                sharedElementTransitionStarted();
+            } else {
+                transition.addListener(new Transition.TransitionListenerAdapter() {
+                    @Override
+                    public void onTransitionStart(Transition transition) {
+                        transition.removeListener(this);
+                        sharedElementTransitionStarted();
+                    }
+                });
+            }
+        }
         if (transition != null) {
             TransitionManager.beginDelayedTransition(getDecor(), transition);
             if (startSharedElementTransition && !mSharedElementNames.isEmpty()) {
@@ -224,6 +242,13 @@
         return transition;
     }
 
+    private void sharedElementTransitionStarted() {
+        mSharedElementTransitionStarted = true;
+        if (mIsExitTransitionComplete) {
+            send(MSG_EXIT_TRANSITION_COMPLETE, null);
+        }
+    }
+
     private void startEnterTransition(Transition transition) {
         setViewVisibility(mTransitioningViews, View.VISIBLE);
         if (!mIsReturning) {
@@ -310,142 +335,4 @@
             startEnterTransition(transition);
         }
     }
-
-    private ArrayList<View> createSnapshots(Bundle state, Collection<String> names) {
-        int numSharedElements = names.size();
-        if (numSharedElements == 0) {
-            return null;
-        }
-        ArrayList<View> snapshots = new ArrayList<View>(numSharedElements);
-        Context context = getWindow().getContext();
-        int[] parentLoc = new int[2];
-        getDecor().getLocationOnScreen(parentLoc);
-        for (String name: names) {
-            Bundle sharedElementBundle = state.getBundle(name);
-            if (sharedElementBundle != null) {
-                Bitmap bitmap = sharedElementBundle.getParcelable(KEY_BITMAP);
-                View snapshot = new View(context);
-                Resources resources = getWindow().getContext().getResources();
-                snapshot.setBackground(new BitmapDrawable(resources, bitmap));
-                snapshot.setViewName(name);
-                setSharedElementState(snapshot, name, state, parentLoc);
-                snapshots.add(snapshot);
-            }
-        }
-        return snapshots;
-    }
-
-    private static void setSharedElementState(View view, String name, Bundle transitionArgs,
-            int[] parentLoc) {
-        Bundle sharedElementBundle = transitionArgs.getBundle(name);
-        if (sharedElementBundle == null) {
-            return;
-        }
-
-        if (view instanceof ImageView) {
-            int scaleTypeInt = sharedElementBundle.getInt(KEY_SCALE_TYPE, -1);
-            if (scaleTypeInt >= 0) {
-                ImageView imageView = (ImageView) view;
-                ImageView.ScaleType scaleType = SCALE_TYPE_VALUES[scaleTypeInt];
-                imageView.setScaleType(scaleType);
-                if (scaleType == ImageView.ScaleType.MATRIX) {
-                    float[] matrixValues = sharedElementBundle.getFloatArray(KEY_IMAGE_MATRIX);
-                    Matrix matrix = new Matrix();
-                    matrix.setValues(matrixValues);
-                    imageView.setImageMatrix(matrix);
-                }
-            }
-        }
-
-        float z = sharedElementBundle.getFloat(KEY_TRANSLATION_Z);
-        view.setTranslationZ(z);
-
-        int x = sharedElementBundle.getInt(KEY_SCREEN_X);
-        int y = sharedElementBundle.getInt(KEY_SCREEN_Y);
-        int width = sharedElementBundle.getInt(KEY_WIDTH);
-        int height = sharedElementBundle.getInt(KEY_HEIGHT);
-
-        int widthSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
-        int heightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY);
-        view.measure(widthSpec, heightSpec);
-
-        int left = x - parentLoc[0];
-        int top = y - parentLoc[1];
-        int right = left + width;
-        int bottom = top + height;
-        view.layout(left, top, right, bottom);
-    }
-
-    private ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> setSharedElementState(
-            Bundle sharedElementState, final ArrayList<View> snapshots) {
-        ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalImageState =
-                new ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>>();
-        if (sharedElementState != null) {
-            int[] tempLoc = new int[2];
-            for (int i = 0; i < mSharedElementNames.size(); i++) {
-                View sharedElement = mSharedElements.get(i);
-                String name = mSharedElementNames.get(i);
-                Pair<ImageView.ScaleType, Matrix> originalState = getOldImageState(sharedElement,
-                        name, sharedElementState);
-                if (originalState != null) {
-                    originalImageState.put((ImageView) sharedElement, originalState);
-                }
-                View parent = (View) sharedElement.getParent();
-                parent.getLocationOnScreen(tempLoc);
-                setSharedElementState(sharedElement, name, sharedElementState, tempLoc);
-                sharedElement.requestLayout();
-            }
-        }
-        mListener.setSharedElementStart(mSharedElementNames, mSharedElements, snapshots);
-
-        getDecor().getViewTreeObserver().addOnPreDrawListener(
-                new ViewTreeObserver.OnPreDrawListener() {
-                    @Override
-                    public boolean onPreDraw() {
-                        getDecor().getViewTreeObserver().removeOnPreDrawListener(this);
-                        mListener.setSharedElementEnd(mSharedElementNames, mSharedElements,
-                                snapshots);
-                        mSharedElementTransitionStarted = true;
-                        return true;
-                    }
-                }
-        );
-        return originalImageState;
-    }
-
-    private static Pair<ImageView.ScaleType, Matrix> getOldImageState(View view, String name,
-            Bundle transitionArgs) {
-        if (!(view instanceof ImageView)) {
-            return null;
-        }
-        Bundle bundle = transitionArgs.getBundle(name);
-        if (bundle == null) {
-            return null;
-        }
-        int scaleTypeInt = bundle.getInt(KEY_SCALE_TYPE, -1);
-        if (scaleTypeInt < 0) {
-            return null;
-        }
-
-        ImageView imageView = (ImageView) view;
-        ImageView.ScaleType originalScaleType = imageView.getScaleType();
-
-        Matrix originalMatrix = null;
-        if (originalScaleType == ImageView.ScaleType.MATRIX) {
-            originalMatrix = new Matrix(imageView.getImageMatrix());
-        }
-
-        return Pair.create(originalScaleType, originalMatrix);
-    }
-
-    private static void setOriginalImageViewState(
-            ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalState) {
-        for (int i = 0; i < originalState.size(); i++) {
-            ImageView imageView = originalState.keyAt(i);
-            Pair<ImageView.ScaleType, Matrix> state = originalState.valueAt(i);
-            imageView.setScaleType(state.first);
-            imageView.setImageMatrix(state.second);
-        }
-    }
-
 }
diff --git a/core/java/android/app/ExitTransitionCoordinator.java b/core/java/android/app/ExitTransitionCoordinator.java
index 93eb53e..a71d649 100644
--- a/core/java/android/app/ExitTransitionCoordinator.java
+++ b/core/java/android/app/ExitTransitionCoordinator.java
@@ -19,8 +19,6 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
 import android.content.Intent;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Bundle;
@@ -29,7 +27,7 @@
 import android.transition.Transition;
 import android.transition.TransitionManager;
 import android.view.View;
-import android.widget.ImageView;
+import android.view.ViewTreeObserver;
 
 import java.util.ArrayList;
 
@@ -62,6 +60,10 @@
 
     private boolean mIsHidden;
 
+    private boolean mExitTransitionStarted;
+
+    private Bundle mExitSharedElementBundle;
+
     public ExitTransitionCoordinator(Activity activity, ArrayList<String> names,
             ArrayList<String> accepted, ArrayList<String> mapped, boolean isReturning) {
         super(activity.getWindow(), names, accepted, mapped, getListener(activity, isReturning),
@@ -102,15 +104,32 @@
                 setViewVisibility(mSharedElements, View.VISIBLE);
                 mIsHidden = true;
                 break;
+            case MSG_SHARED_ELEMENT_DESTINATION:
+                mExitSharedElementBundle = resultData;
+                if (mExitTransitionStarted) {
+                    startSharedElementExit();
+                }
+                break;
+        }
+    }
+
+    private void startSharedElementExit() {
+        if (!mSharedElements.isEmpty() && getSharedElementTransition() != null) {
+            Transition transition = getSharedElementExitTransition();
+            TransitionManager.beginDelayedTransition(getDecor(), transition);
+            ArrayList<View> sharedElementSnapshots = createSnapshots(mExitSharedElementBundle,
+                    mSharedElementNames);
+            setSharedElementState(mExitSharedElementBundle, sharedElementSnapshots);
         }
     }
 
     private void hideSharedElements() {
         setViewVisibility(mSharedElements, View.INVISIBLE);
+        finishIfNecessary();
     }
 
     public void startExit() {
-        beginTransition();
+        beginTransitions();
         setViewVisibility(mTransitioningViews, View.INVISIBLE);
     }
 
@@ -140,7 +159,30 @@
                 }
             }
         }, options);
-        startExit();
+        Transition sharedElementTransition = mSharedElements.isEmpty()
+                ? null : getSharedElementTransition();
+        if (sharedElementTransition == null) {
+            sharedElementTransitionComplete();
+        }
+        Transition transition = mergeTransitions(sharedElementTransition, getExitTransition());
+        if (transition == null) {
+            mExitTransitionStarted = true;
+        } else {
+            TransitionManager.beginDelayedTransition(getDecor(), transition);
+            setViewVisibility(mTransitioningViews, View.INVISIBLE);
+            getDecor().getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
+                @Override
+                public boolean onPreDraw() {
+                    getDecor().getViewTreeObserver().removeOnPreDrawListener(this);
+                    mExitTransitionStarted = true;
+                    if (mExitSharedElementBundle != null) {
+                        startSharedElementExit();
+                    }
+                    notifyComplete();
+                    return true;
+                }
+            });
+        }
     }
 
     private void fadeOutBackground() {
@@ -162,24 +204,13 @@
         }
     }
 
-    private void beginTransition() {
-        Transition sharedElementTransition = configureTransition(getSharedElementTransition());
-        Transition viewsTransition = configureTransition(getViewsTransition());
-        viewsTransition = addTargets(viewsTransition, mTransitioningViews);
-        if (sharedElementTransition == null || mSharedElements.isEmpty()) {
-            sharedElementTransitionComplete();
-            sharedElementTransition = null;
-        } else {
-            sharedElementTransition.addListener(new Transition.TransitionListenerAdapter() {
-                @Override
-                public void onTransitionEnd(Transition transition) {
-                    sharedElementTransitionComplete();
-                }
-            });
+    private Transition getExitTransition() {
+        Transition viewsTransition = null;
+        if (!mTransitioningViews.isEmpty()) {
+            viewsTransition = configureTransition(getViewsTransition());
         }
-        if (viewsTransition == null || mTransitioningViews.isEmpty()) {
+        if (viewsTransition == null) {
             exitTransitionComplete();
-            viewsTransition = null;
         } else {
             viewsTransition.addListener(new Transition.TransitionListenerAdapter() {
                 @Override
@@ -189,13 +220,46 @@
                         setViewVisibility(mTransitioningViews, View.VISIBLE);
                     }
                 }
+
+                @Override
+                public void onTransitionCancel(Transition transition) {
+                    super.onTransitionCancel(transition);
+                }
             });
         }
+        return viewsTransition;
+    }
+
+    private Transition getSharedElementExitTransition() {
+        Transition sharedElementTransition = null;
+        if (!mSharedElements.isEmpty()) {
+            sharedElementTransition = configureTransition(getSharedElementTransition());
+        }
+        if (sharedElementTransition == null) {
+            sharedElementTransitionComplete();
+        } else {
+            sharedElementTransition.addListener(new Transition.TransitionListenerAdapter() {
+                @Override
+                public void onTransitionEnd(Transition transition) {
+                    sharedElementTransitionComplete();
+                    if (mIsHidden) {
+                        setViewVisibility(mSharedElements, View.VISIBLE);
+                    }
+                }
+            });
+            mSharedElements.get(0).invalidate();
+        }
+        return sharedElementTransition;
+    }
+
+    private void beginTransitions() {
+        Transition sharedElementTransition = getSharedElementExitTransition();
+        Transition viewsTransition = getExitTransition();
 
         Transition transition = mergeTransitions(sharedElementTransition, viewsTransition);
-        TransitionManager.beginDelayedTransition(getDecor(), transition);
-        if (viewsTransition == null && sharedElementTransition != null) {
-            mSharedElements.get(0).requestLayout();
+        mExitTransitionStarted = true;
+        if (transition != null) {
+            TransitionManager.beginDelayedTransition(getDecor(), transition);
         }
     }
 
@@ -205,18 +269,12 @@
     }
 
     protected boolean isReadyToNotify() {
-        return mSharedElementBundle != null && mResultReceiver != null && mIsBackgroundReady;
+        return mSharedElementBundle != null && mResultReceiver != null && mIsBackgroundReady
+                && mExitTransitionStarted;
     }
 
     private void sharedElementTransitionComplete() {
-        Bundle bundle = new Bundle();
-        int[] tempLoc = new int[2];
-        for (int i = 0; i < mSharedElementNames.size(); i++) {
-            View sharedElement = mSharedElements.get(i);
-            String name = mSharedElementNames.get(i);
-            captureSharedElementState(sharedElement, name, bundle, tempLoc);
-        }
-        mSharedElementBundle = bundle;
+        mSharedElementBundle = captureSharedElementState();
         notifyComplete();
     }
 
@@ -230,15 +288,23 @@
                 mExitNotified = true;
                 mResultReceiver.send(MSG_EXIT_TRANSITION_COMPLETE, null);
                 mResultReceiver = null; // done talking
-                if (mIsReturning) {
-                    mActivity.finish();
-                    mActivity.overridePendingTransition(0, 0);
-                }
-                mActivity = null;
+                finishIfNecessary();
             }
         }
     }
 
+    private void finishIfNecessary() {
+        if (mIsReturning && mExitNotified && (mSharedElements.isEmpty()
+                || mSharedElements.get(0).getVisibility() == View.INVISIBLE)) {
+            mActivity.finish();
+            mActivity.overridePendingTransition(0, 0);
+            mActivity = null;
+        }
+        if (!mIsReturning && mExitNotified) {
+            mActivity = null; // don't need it anymore
+        }
+    }
+
     @Override
     protected Transition getViewsTransition() {
         if (mIsReturning) {
@@ -255,58 +321,4 @@
             return getWindow().getSharedElementExitTransition();
         }
     }
-
-    /**
-     * Captures placement information for Views with a shared element name for
-     * Activity Transitions.
-     *
-     * @param view           The View to capture the placement information for.
-     * @param name           The shared element name in the target Activity to apply the placement
-     *                       information for.
-     * @param transitionArgs Bundle to store shared element placement information.
-     * @param tempLoc        A temporary int[2] for capturing the current location of views.
-     */
-    private static void captureSharedElementState(View view, String name, Bundle transitionArgs,
-            int[] tempLoc) {
-        Bundle sharedElementBundle = new Bundle();
-        view.getLocationOnScreen(tempLoc);
-        float scaleX = view.getScaleX();
-        sharedElementBundle.putInt(KEY_SCREEN_X, tempLoc[0]);
-        int width = Math.round(view.getWidth() * scaleX);
-        sharedElementBundle.putInt(KEY_WIDTH, width);
-
-        float scaleY = view.getScaleY();
-        sharedElementBundle.putInt(KEY_SCREEN_Y, tempLoc[1]);
-        int height = Math.round(view.getHeight() * scaleY);
-        sharedElementBundle.putInt(KEY_HEIGHT, height);
-
-        sharedElementBundle.putFloat(KEY_TRANSLATION_Z, view.getTranslationZ());
-
-        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
-        Canvas canvas = new Canvas(bitmap);
-        view.draw(canvas);
-        sharedElementBundle.putParcelable(KEY_BITMAP, bitmap);
-
-        if (view instanceof ImageView) {
-            ImageView imageView = (ImageView) view;
-            int scaleTypeInt = scaleTypeToInt(imageView.getScaleType());
-            sharedElementBundle.putInt(KEY_SCALE_TYPE, scaleTypeInt);
-            if (imageView.getScaleType() == ImageView.ScaleType.MATRIX) {
-                float[] matrix = new float[9];
-                imageView.getImageMatrix().getValues(matrix);
-                sharedElementBundle.putFloatArray(KEY_IMAGE_MATRIX, matrix);
-            }
-        }
-
-        transitionArgs.putBundle(name, sharedElementBundle);
-    }
-
-    private static int scaleTypeToInt(ImageView.ScaleType scaleType) {
-        for (int i = 0; i < SCALE_TYPE_VALUES.length; i++) {
-            if (scaleType == SCALE_TYPE_VALUES[i]) {
-                return i;
-            }
-        }
-        return -1;
-    }
 }
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index e0d69e3..e489e05 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -16,10 +16,13 @@
 
 package android.net;
 
+import android.net.NetworkUtils;
 import android.os.Parcelable;
 import android.os.Parcel;
 
+import java.io.IOException;
 import java.net.InetAddress;
+import java.net.InetSocketAddress;
 import java.net.Socket;
 import java.net.UnknownHostException;
 import javax.net.SocketFactory;
@@ -38,6 +41,8 @@
      */
     public final int netId;
 
+    private NetworkBoundSocketFactory mNetworkBoundSocketFactory = null;
+
     /**
      * @hide
      */
@@ -79,6 +84,59 @@
     }
 
     /**
+     * A {@code SocketFactory} that produces {@code Socket}'s bound to this network.
+     */
+    private class NetworkBoundSocketFactory extends SocketFactory {
+        private final int mNetId;
+
+        public NetworkBoundSocketFactory(int netId) {
+            super();
+            mNetId = netId;
+        }
+
+        @Override
+        public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
+            Socket socket = createSocket();
+            socket.bind(new InetSocketAddress(localHost, localPort));
+            socket.connect(new InetSocketAddress(host, port));
+            return socket;
+        }
+
+        @Override
+        public Socket createSocket(InetAddress address, int port, InetAddress localAddress,
+                int localPort) throws IOException {
+            Socket socket = createSocket();
+            socket.bind(new InetSocketAddress(localAddress, localPort));
+            socket.connect(new InetSocketAddress(address, port));
+            return socket;
+        }
+
+        @Override
+        public Socket createSocket(InetAddress host, int port) throws IOException {
+            Socket socket = createSocket();
+            socket.connect(new InetSocketAddress(host, port));
+            return socket;
+        }
+
+        @Override
+        public Socket createSocket(String host, int port) throws IOException {
+            Socket socket = createSocket();
+            socket.connect(new InetSocketAddress(host, port));
+            return socket;
+        }
+
+        @Override
+        public Socket createSocket() throws IOException {
+            Socket socket = new Socket();
+            // Query a property of the underlying socket to ensure the underlying
+            // socket exists so a file descriptor is available to bind to a network.
+            socket.getReuseAddress();
+            NetworkUtils.bindSocketToNetwork(socket.getFileDescriptor$().getInt$(), mNetId);
+            return socket;
+        }
+    }
+
+    /**
      * Returns a {@link SocketFactory} bound to this network.  Any {@link Socket} created by
      * this factory will have its traffic sent over this {@code Network}.  Note that if this
      * {@code Network} ever disconnects, this factory and any {@link Socket} it produced in the
@@ -88,7 +146,10 @@
      *         {@code Network}.
      */
     public SocketFactory socketFactory() {
-        return null;
+        if (mNetworkBoundSocketFactory == null) {
+            mNetworkBoundSocketFactory = new NetworkBoundSocketFactory(netId);
+        }
+        return mNetworkBoundSocketFactory;
     }
 
     /**
@@ -99,6 +160,29 @@
      * doesn't accidentally use sockets it thinks are still bound to a particular {@code Network}.
      */
     public void bindProcess() {
+        NetworkUtils.bindProcessToNetwork(netId);
+    }
+
+    /**
+     * Binds host resolutions performed by this process to this network.  {@link #bindProcess}
+     * takes precedence over this setting.
+     *
+     * @hide
+     * @deprecated This is strictly for legacy usage to support startUsingNetworkFeature().
+     */
+    public void bindProcessForHostResolution() {
+        NetworkUtils.bindProcessToNetworkForHostResolution(netId);
+    }
+
+    /**
+     * Clears any process specific {@link Network} binding for host resolution.  This does
+     * not clear bindings enacted via {@link #bindProcess}.
+     *
+     * @hide
+     * @deprecated This is strictly for legacy usage to support startUsingNetworkFeature().
+     */
+    public void unbindProcessForHostResolution() {
+        NetworkUtils.unbindProcessToNetworkForHostResolution();
     }
 
     /**
@@ -107,7 +191,7 @@
      * @return {@code Network} to which this process is bound.
      */
     public static Network getProcessBoundNetwork() {
-        return null;
+        return new Network(NetworkUtils.getNetworkBoundToProcess());
     }
 
     /**
@@ -115,6 +199,7 @@
      * {@link Network#bindProcess}.
      */
     public static void unbindProcess() {
+        NetworkUtils.unbindProcessToNetwork();
     }
 
     // implement the Parcelable interface
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index b24d396..edb3286 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -109,6 +109,50 @@
     public native static void markSocket(int socketfd, int mark);
 
     /**
+     * Binds the current process to the network designated by {@code netId}.  All sockets created
+     * in the future (and not explicitly bound via a bound {@link SocketFactory} (see
+     * {@link Network#socketFactory}) will be bound to this network.  Note that if this
+     * {@code Network} ever disconnects all sockets created in this way will cease to work.  This
+     * is by design so an application doesn't accidentally use sockets it thinks are still bound to
+     * a particular {@code Network}.
+     */
+    public native static void bindProcessToNetwork(int netId);
+
+    /**
+     * Clear any process specific {@code Network} binding.  This reverts a call to
+     * {@link #bindProcessToNetwork}.
+     */
+    public native static void unbindProcessToNetwork();
+
+    /**
+     * Return the netId last passed to {@link #bindProcessToNetwork}, or NETID_UNSET if
+     * {@link #unbindProcessToNetwork} has been called since {@link #bindProcessToNetwork}.
+     */
+    public native static int getNetworkBoundToProcess();
+
+    /**
+     * Binds host resolutions performed by this process to the network designated by {@code netId}.
+     * {@link #bindProcessToNetwork} takes precedence over this setting.
+     *
+     * @deprecated This is strictly for legacy usage to support startUsingNetworkFeature().
+     */
+    public native static void bindProcessToNetworkForHostResolution(int netId);
+
+    /**
+     * Clears any process specific {@link Network} binding for host resolution.  This does
+     * not clear bindings enacted via {@link #bindProcessToNetwork}.
+     *
+     * @deprecated This is strictly for legacy usage to support startUsingNetworkFeature().
+     */
+    public native static void unbindProcessToNetworkForHostResolution();
+
+    /**
+     * Explicitly binds {@code socketfd} to the network designated by {@code netId}.  This
+     * overrides any binding via {@link #bindProcessToNetwork}.
+     */
+    public native static void bindSocketToNetwork(int socketfd, int netId);
+
+    /**
      * Convert a IPv4 address from an integer to an InetAddress.
      * @param hostAddress an int corresponding to the IPv4 address in network byte order
      */
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index a159715..f446c3a 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -173,8 +173,10 @@
 	$(call include-path-for, bluedroid) \
 	$(call include-path-for, libhardware)/hardware \
 	$(call include-path-for, libhardware_legacy)/hardware_legacy \
+	$(TOP)/bionic/libc/dns/include \
 	$(TOP)/frameworks/av/include \
 	$(TOP)/system/media/camera/include \
+	$(TOP)/system/netd/include \
 	external/pdfium/core/include/fpdfapi \
 	external/pdfium/core/include/fpdfdoc \
 	external/pdfium/fpdfsdk/include \
@@ -233,6 +235,7 @@
 	libaudioutils \
 	libpdfium \
 	libimg_utils \
+	libnetd_client \
 
 ifeq ($(USE_OPENGL_RENDERER),true)
 	LOCAL_SHARED_LIBRARIES += libhwui
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 6d23c32..bc5e1b3 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -18,6 +18,8 @@
 
 #include "jni.h"
 #include "JNIHelp.h"
+#include "NetdClient.h"
+#include "resolv_netid.h"
 #include <utils/misc.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/Log.h>
@@ -250,6 +252,36 @@
     }
 }
 
+static void android_net_utils_bindProcessToNetwork(JNIEnv *env, jobject thiz, jint netId)
+{
+    setNetworkForProcess(netId);
+}
+
+static void android_net_utils_unbindProcessToNetwork(JNIEnv *env, jobject thiz)
+{
+    setNetworkForProcess(NETID_UNSET);
+}
+
+static jint android_net_utils_getNetworkBoundToProcess(JNIEnv *env, jobject thiz)
+{
+    return getNetworkForProcess();
+}
+
+static void android_net_utils_bindProcessToNetworkForHostResolution(JNIEnv *env, jobject thiz, jint netId)
+{
+    setNetworkForResolv(netId);
+}
+
+static void android_net_utils_unbindProcessToNetworkForHostResolution(JNIEnv *env, jobject thiz)
+{
+    setNetworkForResolv(NETID_UNSET);
+}
+
+static void android_net_utils_bindSocketToNetwork(JNIEnv *env, jobject thiz, jint socket, jint netId)
+{
+    setNetworkForSocket(netId, socket);
+}
+
 // ----------------------------------------------------------------------------
 
 /*
@@ -267,6 +299,12 @@
     { "releaseDhcpLease", "(Ljava/lang/String;)Z",  (void *)android_net_utils_releaseDhcpLease },
     { "getDhcpError", "()Ljava/lang/String;", (void*) android_net_utils_getDhcpError },
     { "markSocket", "(II)V", (void*) android_net_utils_markSocket },
+    { "bindProcessToNetwork", "(I)V", (void*) android_net_utils_bindProcessToNetwork },
+    { "getNetworkBoundToProcess", "()I", (void*) android_net_utils_getNetworkBoundToProcess },
+    { "unbindProcessToNetwork", "()V", (void*) android_net_utils_unbindProcessToNetwork },
+    { "bindProcessToNetworkForHostResolution", "(I)V", (void*) android_net_utils_bindProcessToNetworkForHostResolution },
+    { "unbindProcessToNetworkForHostResolution", "()V", (void*) android_net_utils_unbindProcessToNetworkForHostResolution },
+    { "bindSocketToNetwork", "(II)V", (void*) android_net_utils_bindSocketToNetwork },
 };
 
 int register_android_net_NetworkUtils(JNIEnv* env)
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
index fe51215..afc2931 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
@@ -1032,11 +1032,14 @@
         float[] redOut = mMetadata.get(CaptureResult.TONEMAP_CURVE_RED);
         float[] greenOut = mMetadata.get(CaptureResult.TONEMAP_CURVE_GREEN);
         float[] blueOut = mMetadata.get(CaptureResult.TONEMAP_CURVE_BLUE);
-        assertTrue("Input and output tonemap curve should match", Arrays.equals(red, redOut));
-        assertTrue("Input and output tonemap curve should match", Arrays.equals(green, greenOut));
-        assertTrue("Input and output tonemap curve should match", Arrays.equals(blue, blueOut));
+        assertArrayEquals(red, redOut);
+        assertArrayEquals(green, greenOut);
+        assertArrayEquals(blue, blueOut);
         TonemapCurve tcOut = mMetadata.get(CaptureResult.TONEMAP_CURVE);
-        assertTrue("Input and output tonemap curve should match", tcIn.equals(tcOut));
+        assertEquals(tcIn, tcOut);
+        mMetadata.set(CaptureResult.TONEMAP_CURVE_GREEN, null);
+        // If any of channel has null curve, return a null TonemapCurve
+        assertNull(mMetadata.get(CaptureResult.TONEMAP_CURVE));
     }
 
     /**
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index e1a9719..3bf2b20 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -439,6 +439,10 @@
     }
 
     @Override
+    public void keyguardGoingAway() throws RemoteException {
+    }
+
+    @Override
     public void lockNow(Bundle options) {
         // TODO Auto-generated method stub
     }