Merge "Import translations. DO NOT MERGE"
diff --git a/api/current.txt b/api/current.txt
index 9d87336..2282a33 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -825,6 +825,7 @@
     field public static final int persistent = 16842765; // 0x101000d
     field public static final int persistentDrawingCache = 16842990; // 0x10100ee
     field public static final deprecated int phoneNumber = 16843111; // 0x1010167
+    field public static final int pinned = 16843776; // 0x1010400
     field public static final int pivotX = 16843189; // 0x10101b5
     field public static final int pivotY = 16843190; // 0x10101b6
     field public static final int popupAnimationStyle = 16843465; // 0x10102c9
@@ -2196,6 +2197,8 @@
     field public static final int Widget_Quantum_Button_Borderless = 16974393; // 0x1030239
     field public static final int Widget_Quantum_Button_Borderless_Small = 16974394; // 0x103023a
     field public static final int Widget_Quantum_Button_Inset = 16974395; // 0x103023b
+    field public static final int Widget_Quantum_Button_Paper = 16974503; // 0x10302a7
+    field public static final int Widget_Quantum_Button_Paper_Color = 16974504; // 0x10302a8
     field public static final int Widget_Quantum_Button_Small = 16974396; // 0x103023c
     field public static final int Widget_Quantum_Button_Toggle = 16974397; // 0x103023d
     field public static final int Widget_Quantum_CalendarView = 16974398; // 0x103023e
@@ -2232,6 +2235,8 @@
     field public static final int Widget_Quantum_Light_Button = 16974452; // 0x1030274
     field public static final int Widget_Quantum_Light_Button_Borderless_Small = 16974453; // 0x1030275
     field public static final int Widget_Quantum_Light_Button_Inset = 16974454; // 0x1030276
+    field public static final int Widget_Quantum_Light_Button_Paper = 16974505; // 0x10302a9
+    field public static final int Widget_Quantum_Light_Button_Paper_Color = 16974506; // 0x10302aa
     field public static final int Widget_Quantum_Light_Button_Small = 16974455; // 0x1030277
     field public static final int Widget_Quantum_Light_Button_Toggle = 16974456; // 0x1030278
     field public static final int Widget_Quantum_Light_CalendarView = 16974457; // 0x1030279
@@ -13422,6 +13427,7 @@
 
   public static final class MediaMuxer.OutputFormat {
     field public static final int MUXER_OUTPUT_MPEG_4 = 0; // 0x0
+    field public static final int MUXER_OUTPUT_WEBM = 1; // 0x1
   }
 
   public class MediaPlayer {
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 9d0ab3a..02c850b 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -987,7 +987,7 @@
      * Implement this to handle requests to delete one or more rows.
      * The implementation should apply the selection clause when performing
      * deletion, allowing the operation to affect multiple rows in a directory.
-     * As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyDelete()}
+     * As a courtesy, call {@link ContentResolver#notifyChange(android.net.Uri ,android.database.ContentObserver) notifyChange()}
      * after deleting.
      * This method can be called from multiple threads, as described in
      * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index 8c23129..5f2b5f0 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -1359,7 +1359,7 @@
         float q2 = rotationVector[1];
         float q3 = rotationVector[2];
 
-        if (rotationVector.length == 4) {
+        if (rotationVector.length >= 4) {
             q0 = rotationVector[3];
         } else {
             q0 = 1 - q1*q1 - q2*q2 - q3*q3;
@@ -1416,7 +1416,7 @@
      *  @param Q an array of floats in which to store the computed quaternion
      */
     public static void getQuaternionFromVector(float[] Q, float[] rv) {
-        if (rv.length == 4) {
+        if (rv.length >= 4) {
             Q[0] = rv[3];
         } else {
             Q[0] = 1 - rv[0]*rv[0] - rv[1]*rv[1] - rv[2]*rv[2];
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index f1e7e98..465d142 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -19,6 +19,7 @@
 import android.hardware.input.InputDeviceIdentifier;
 import android.hardware.input.KeyboardLayout;
 import android.hardware.input.IInputDevicesChangedListener;
+import android.hardware.input.TouchCalibration;
 import android.os.IBinder;
 import android.view.InputDevice;
 import android.view.InputEvent;
@@ -39,6 +40,11 @@
     // applications, the caller must have the INJECT_EVENTS permission.
     boolean injectInputEvent(in InputEvent ev, int mode);
 
+    // Calibrate input device position
+    TouchCalibration getTouchCalibrationForInputDevice(String inputDeviceDescriptor, int rotation);
+    void setTouchCalibrationForInputDevice(String inputDeviceDescriptor, int rotation,
+            in TouchCalibration calibration);
+
     // Keyboard layouts configuration.
     KeyboardLayout[] getKeyboardLayouts();
     KeyboardLayout getKeyboardLayout(String keyboardLayoutDescriptor);
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index a2aeafb..e3a3830 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -500,6 +500,45 @@
     }
 
     /**
+     * Gets the TouchCalibration applied to the specified input device's coordinates.
+     *
+     * @param inputDeviceDescriptor The input device descriptor.
+     * @return The TouchCalibration currently assigned for use with the given
+     * input device. If none is set, an identity TouchCalibration is returned.
+     *
+     * @hide
+     */
+    public TouchCalibration getTouchCalibration(String inputDeviceDescriptor, int surfaceRotation) {
+        try {
+            return mIm.getTouchCalibrationForInputDevice(inputDeviceDescriptor, surfaceRotation);
+        } catch (RemoteException ex) {
+            Log.w(TAG, "Could not get calibration matrix for input device.", ex);
+            return TouchCalibration.IDENTITY;
+        }
+    }
+
+    /**
+     * Sets the TouchCalibration to apply to the specified input device's coordinates.
+     * <p>
+     * This method may have the side-effect of causing the input device in question
+     * to be reconfigured. Requires {@link android.Manifest.permissions.SET_INPUT_CALIBRATION}.
+     * </p>
+     *
+     * @param inputDeviceDescriptor The input device descriptor.
+     * @param calibration The calibration to be applied
+     *
+     * @hide
+     */
+    public void setTouchCalibration(String inputDeviceDescriptor, int surfaceRotation,
+            TouchCalibration calibration) {
+        try {
+            mIm.setTouchCalibrationForInputDevice(inputDeviceDescriptor, surfaceRotation, calibration);
+        } catch (RemoteException ex) {
+            Log.w(TAG, "Could not set calibration matrix for input device.", ex);
+        }
+    }
+
+    /**
      * Gets the mouse pointer speed.
      * <p>
      * Only returns the permanent mouse pointer speed.  Ignores any temporary pointer
diff --git a/core/java/android/hardware/input/TouchCalibration.aidl b/core/java/android/hardware/input/TouchCalibration.aidl
new file mode 100644
index 0000000..2c28774
--- /dev/null
+++ b/core/java/android/hardware/input/TouchCalibration.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.input;
+
+parcelable TouchCalibration;
diff --git a/core/java/android/hardware/input/TouchCalibration.java b/core/java/android/hardware/input/TouchCalibration.java
new file mode 100644
index 0000000..025fad0
--- /dev/null
+++ b/core/java/android/hardware/input/TouchCalibration.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.input;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Encapsulates calibration data for input devices.
+ *
+ * @hide
+ */
+public class TouchCalibration implements Parcelable {
+
+    public static final TouchCalibration IDENTITY = new TouchCalibration();
+
+    public static final Parcelable.Creator<TouchCalibration> CREATOR
+            = new Parcelable.Creator<TouchCalibration>() {
+        public TouchCalibration createFromParcel(Parcel in) {
+            return new TouchCalibration(in);
+        }
+
+        public TouchCalibration[] newArray(int size) {
+            return new TouchCalibration[size];
+        }
+    };
+
+    private final float mXScale, mXYMix, mXOffset;
+    private final float mYXMix, mYScale, mYOffset;
+
+    /**
+     * Create a new TouchCalibration initialized to the identity transformation.
+     */
+    public TouchCalibration() {
+        this(1,0,0,0,1,0);
+    }
+
+    /**
+     * Create a new TouchCalibration from affine transformation paramters.
+     * @param xScale   Influence of input x-axis value on output x-axis value.
+     * @param xyMix    Influence of input y-axis value on output x-axis value.
+     * @param xOffset  Constant offset to be applied to output x-axis value.
+     * @param yXMix    Influence of input x-axis value on output y-axis value.
+     * @param yScale   Influence of input y-axis value on output y-axis value.
+     * @param yOffset  Constant offset to be applied to output y-axis value.
+     */
+    public TouchCalibration(float xScale, float xyMix, float xOffset,
+            float yxMix, float yScale, float yOffset) {
+        mXScale  = xScale;
+        mXYMix   = xyMix;
+        mXOffset = xOffset;
+        mYXMix   = yxMix;
+        mYScale  = yScale;
+        mYOffset = yOffset;
+    }
+
+    public TouchCalibration(Parcel in) {
+        mXScale  = in.readFloat();
+        mXYMix   = in.readFloat();
+        mXOffset = in.readFloat();
+        mYXMix   = in.readFloat();
+        mYScale  = in.readFloat();
+        mYOffset = in.readFloat();
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeFloat(mXScale);
+        dest.writeFloat(mXYMix);
+        dest.writeFloat(mXOffset);
+        dest.writeFloat(mYXMix);
+        dest.writeFloat(mYScale);
+        dest.writeFloat(mYOffset);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    public float[] getAffineTransform() {
+        return new float[] { mXScale, mXYMix, mXOffset, mYXMix, mYScale, mYOffset };
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        } else if (obj instanceof TouchCalibration) {
+            TouchCalibration cal = (TouchCalibration)obj;
+
+            return (cal.mXScale  == mXScale)  &&
+                   (cal.mXYMix   == mXYMix)   &&
+                   (cal.mXOffset == mXOffset) &&
+                   (cal.mYXMix   == mYXMix)   &&
+                   (cal.mYScale  == mYScale)  &&
+                   (cal.mYOffset == mYOffset);
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return Float.floatToIntBits(mXScale)  ^
+               Float.floatToIntBits(mXYMix)   ^
+               Float.floatToIntBits(mXOffset) ^
+               Float.floatToIntBits(mYXMix)   ^
+               Float.floatToIntBits(mYScale)  ^
+               Float.floatToIntBits(mYOffset);
+    }
+}
diff --git a/core/java/android/util/Patterns.java b/core/java/android/util/Patterns.java
index 9522112..0f8da44 100644
--- a/core/java/android/util/Patterns.java
+++ b/core/java/android/util/Patterns.java
@@ -191,8 +191,6 @@
         for (int i = 1; i <= numGroups; i++) {
             String s = matcher.group(i);
 
-            System.err.println("Group(" + i + ") : " + s);
-
             if (s != null) {
                 b.append(s);
             }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 904ec44..d72f810 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -10541,7 +10541,7 @@
      * by the layout system and should not generally be called otherwise, because the property
      * may be changed at any time by the layout.
      *
-     * @param left The bottom of this view, in pixels.
+     * @param left The left of this view, in pixels.
      */
     public final void setLeft(int left) {
         if (left != mLeft) {
@@ -10608,7 +10608,7 @@
      * by the layout system and should not generally be called otherwise, because the property
      * may be changed at any time by the layout.
      *
-     * @param right The bottom of this view, in pixels.
+     * @param right The right of this view, in pixels.
      */
     public final void setRight(int right) {
         if (right != mRight) {
@@ -17235,7 +17235,7 @@
             } else {
                 long value = mMeasureCache.valueAt(cacheIndex);
                 // Casting a long to int drops the high 32 bits, no mask needed
-                setMeasuredDimension((int) (value >> 32), (int) value);
+                setMeasuredDimensionRaw((int) (value >> 32), (int) value);
                 mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
             }
 
@@ -17330,6 +17330,22 @@
             measuredWidth  += optical ? opticalWidth  : -opticalWidth;
             measuredHeight += optical ? opticalHeight : -opticalHeight;
         }
+        setMeasuredDimensionRaw(measuredWidth, measuredHeight);
+    }
+
+    /**
+     * Sets the measured dimension without extra processing for things like optical bounds.
+     * Useful for reapplying consistent values that have already been cooked with adjustments
+     * for optical bounds, etc. such as those from the measurement cache.
+     *
+     * @param measuredWidth The measured width of this view.  May be a complex
+     * bit mask as defined by {@link #MEASURED_SIZE_MASK} and
+     * {@link #MEASURED_STATE_TOO_SMALL}.
+     * @param measuredHeight The measured height of this view.  May be a complex
+     * bit mask as defined by {@link #MEASURED_SIZE_MASK} and
+     * {@link #MEASURED_STATE_TOO_SMALL}.
+     */
+    private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
         mMeasuredWidth = measuredWidth;
         mMeasuredHeight = measuredHeight;
 
@@ -19142,7 +19158,18 @@
         }
 
         static int adjust(int measureSpec, int delta) {
-            return makeMeasureSpec(getSize(measureSpec + delta), getMode(measureSpec));
+            final int mode = getMode(measureSpec);
+            if (mode == UNSPECIFIED) {
+                // No need to adjust size for UNSPECIFIED mode.
+                return makeMeasureSpec(0, UNSPECIFIED);
+            }
+            int size = getSize(measureSpec) + delta;
+            if (size < 0) {
+                Log.e(VIEW_LOG_TAG, "MeasureSpec.adjust: new size would be negative! (" + size +
+                        ") spec: " + toString(measureSpec) + " delta: " + delta);
+                size = 0;
+            }
+            return makeMeasureSpec(size, mode);
         }
 
         /**
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index 941cdd0..438a9da 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -296,7 +296,7 @@
         // The extra space for the thumb to move on the track
         available += mThumbOffset * 2;
 
-        int thumbPos = (int) (scale * available);
+        int thumbPos = (int) (scale * available + 0.5f);
 
         int topBound, bottomBound;
         if (gap == Integer.MIN_VALUE) {
diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
index bc051ce..03a053c 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java
@@ -504,6 +504,7 @@
 
         private String mEnabledInputMethodsStrCache;
         private int mCurrentUserId;
+        private int[] mRelatedUserIds = new int[0];
 
         private static void buildEnabledInputMethodsSettingString(
                 StringBuilder builder, Pair<String, ArrayList<String>> pair) {
@@ -536,6 +537,22 @@
             mCurrentUserId = userId;
         }
 
+        public void setRelatedUserIds(int[] relatedUserIds) {
+            synchronized (this) {
+                mRelatedUserIds = relatedUserIds;
+            }
+        }
+
+        public boolean isRelatedToOrCurrentUser(int userId) {
+            synchronized (this) {
+                if (userId == mCurrentUserId) return true;
+                for (int i = 0; i < mRelatedUserIds.length; i++) {
+                    if (userId == mRelatedUserIds[i]) return true;
+                }
+                return false;
+            }
+        }
+
         public List<InputMethodInfo> getEnabledInputMethodListLocked() {
             return createEnabledInputMethodListLocked(
                     getEnabledInputMethodsAndSubtypeListLocked());
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index e50c1d8..94c3f44 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -818,13 +818,6 @@
       mOptions.add(opt);
     }
 
-    /*
-     * We don't have /tmp on the device, but we often have an SD card.  Apps
-     * shouldn't use this, but some test suites might want to exercise it.
-     */
-    opt.optionString = "-Djava.io.tmpdir=/sdcard";
-    mOptions.add(opt);
-
     initArgs.version = JNI_VERSION_1_4;
     initArgs.options = mOptions.editArray();
     initArgs.nOptions = mOptions.size();
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index fc587c0..2b9a5c4 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -115,7 +115,7 @@
         }
     }
 
-    int32_t* yDivs = chunk->getXDivs();
+    int32_t* yDivs = chunk->getYDivs();
     for (int i = 0; i < chunk->numYDivs; i++) {
         yDivs[i] = int32_t(yDivs[i] * scale + 0.5f);
         if (i > 0 && yDivs[i] == yDivs[i - 1]) {
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 7e958e7..a64d3ba 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -17,10 +17,12 @@
 
 #define LOG_TAG "AudioTrack-JNI"
 
-#include <jni.h>
 #include <JNIHelp.h>
+#include <JniConstants.h>
 #include <android_runtime/AndroidRuntime.h>
 
+#include "ScopedBytes.h"
+
 #include <utils/Log.h>
 #include <media/AudioSystem.h>
 #include <media/AudioTrack.h>
@@ -503,13 +505,13 @@
 }
 
 // ----------------------------------------------------------------------------
-jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, jbyte* data,
-                  jint offsetInBytes, jint sizeInBytes) {
+jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const jbyte* data,
+                  jint offsetInBytes, jint sizeInBytes, bool blocking = true) {
     // give the data to the native AudioTrack object (the data starts at the offset)
     ssize_t written = 0;
     // regular write() or copy the data to the AudioTrack's shared memory?
     if (track->sharedBuffer() == 0) {
-        written = track->write(data + offsetInBytes, sizeInBytes);
+        written = track->write(data + offsetInBytes, sizeInBytes, blocking);
         // for compatibility with earlier behavior of write(), return 0 in this case
         if (written == (ssize_t) WOULD_BLOCK) {
             written = 0;
@@ -563,7 +565,8 @@
 static jint android_media_AudioTrack_write_byte(JNIEnv *env,  jobject thiz,
                                                   jbyteArray javaAudioData,
                                                   jint offsetInBytes, jint sizeInBytes,
-                                                  jint javaAudioFormat) {
+                                                  jint javaAudioFormat,
+                                                  jboolean isWriteBlocking) {
     //ALOGV("android_media_AudioTrack_write_byte(offset=%d, sizeInBytes=%d) called",
     //    offsetInBytes, sizeInBytes);
     sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
@@ -590,7 +593,8 @@
         return 0;
     }
 
-    jint written = writeToTrack(lpTrack, javaAudioFormat, cAudioData, offsetInBytes, sizeInBytes);
+    jint written = writeToTrack(lpTrack, javaAudioFormat, cAudioData, offsetInBytes, sizeInBytes,
+            isWriteBlocking == JNI_TRUE /* blocking */);
 
     env->ReleaseByteArrayElements(javaAudioData, cAudioData, 0);
 
@@ -601,6 +605,31 @@
 
 
 // ----------------------------------------------------------------------------
+static jint android_media_AudioTrack_write_native_bytes(JNIEnv *env,  jobject thiz,
+        jbyteArray javaBytes, jint byteOffset, jint offsetInBytes, jint sizeInBytes,
+        jint javaAudioFormat, jboolean isWriteBlocking) {
+    //ALOGV("android_media_AudioTrack_write_native_bytes(offset=%d, sizeInBytes=%d) called",
+    //    offsetInBytes, sizeInBytes);
+    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
+    if (lpTrack == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+                "Unable to retrieve AudioTrack pointer for write()");
+        return 0;
+    }
+
+    ScopedBytesRO bytes(env, javaBytes);
+    if (bytes.get() == NULL) {
+        ALOGE("Error retrieving source of audio data to play, can't play");
+        return AUDIOTRACK_ERROR_BAD_VALUE;
+    }
+
+    jint written = writeToTrack(lpTrack, javaAudioFormat, bytes.get() + byteOffset, offsetInBytes,
+            sizeInBytes, isWriteBlocking == JNI_TRUE /* blocking */);
+
+    return written;
+}
+
+// ----------------------------------------------------------------------------
 static jint android_media_AudioTrack_write_short(JNIEnv *env,  jobject thiz,
                                                   jshortArray javaAudioData,
                                                   jint offsetInShorts, jint sizeInShorts,
@@ -608,7 +637,8 @@
     jint written = android_media_AudioTrack_write_byte(env, thiz,
                                                  (jbyteArray) javaAudioData,
                                                  offsetInShorts*2, sizeInShorts*2,
-                                                 javaAudioFormat);
+                                                 javaAudioFormat,
+                                                 JNI_TRUE /*blocking write, legacy behavior*/);
     if (written > 0) {
         written /= 2;
     }
@@ -890,7 +920,10 @@
                                          (void *)android_media_AudioTrack_setup},
     {"native_finalize",      "()V",      (void *)android_media_AudioTrack_finalize},
     {"native_release",       "()V",      (void *)android_media_AudioTrack_release},
-    {"native_write_byte",    "([BIII)I", (void *)android_media_AudioTrack_write_byte},
+    {"native_write_byte",    "([BIIIZ)I",(void *)android_media_AudioTrack_write_byte},
+    {"native_write_native_bytes",
+                             "(Ljava/lang/Object;IIIIZ)I",
+                                         (void *)android_media_AudioTrack_write_native_bytes},
     {"native_write_short",   "([SIII)I", (void *)android_media_AudioTrack_write_short},
     {"native_setVolume",     "(FF)V",    (void *)android_media_AudioTrack_set_volume},
     {"native_get_native_frame_count",
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 4c0ddeb..b99cb90 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2069,6 +2069,14 @@
         android:description="@string/permdesc_setPointerSpeed"
         android:protectionLevel="signature" />
 
+    <!-- Allows low-level access to setting input device calibration.
+         <p>Not for use by normal applications.
+         @hide -->
+    <permission android:name="android.permission.SET_INPUT_CALIBRATION"
+        android:label="@string/permlab_setInputCalibration"
+        android:description="@string/permdesc_setInputCalibration"
+        android:protectionLevel="signature" />
+
     <!-- Allows low-level access to setting the keyboard layout.
          <p>Not for use by third-party applications.
          @hide -->
diff --git a/core/res/res/drawable/btn_borderless_quantum_dark.xml b/core/res/res/drawable/btn_borderless_quantum_dark.xml
index e1bff4f..2c0d3b7 100644
--- a/core/res/res/drawable/btn_borderless_quantum_dark.xml
+++ b/core/res/res/drawable/btn_borderless_quantum_dark.xml
@@ -14,10 +14,6 @@
      limitations under the License.
 -->
 
-<reveal xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:drawable="@color/transparent" />
-    <item>
-        <nine-patch android:src="@drawable/btn_qntm_alpha"
-            android:tint="@color/btn_default_pressed_quantum_dark" />
-    </item>
-</reveal>
+<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
+    android:tint="@color/btn_default_pressed_quantum_dark"
+    android:mask="@drawable/btn_qntm_alpha" />
diff --git a/core/res/res/drawable/btn_borderless_quantum_light.xml b/core/res/res/drawable/btn_borderless_quantum_light.xml
index e7a95b1..2faec20 100644
--- a/core/res/res/drawable/btn_borderless_quantum_light.xml
+++ b/core/res/res/drawable/btn_borderless_quantum_light.xml
@@ -14,10 +14,6 @@
      limitations under the License.
 -->
 
-<reveal xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:drawable="@color/transparent" />
-    <item>
-        <nine-patch android:src="@drawable/btn_qntm_alpha"
-            android:tint="@color/btn_default_pressed_quantum_light" />
-    </item>
-</reveal>
+<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
+    android:tint="@color/btn_default_pressed_quantum_light"
+    android:mask="@drawable/btn_qntm_alpha" />
diff --git a/core/res/res/drawable/btn_color_quantum_dark.xml b/core/res/res/drawable/btn_color_quantum_dark.xml
index 5e44a78..0507755 100644
--- a/core/res/res/drawable/btn_color_quantum_dark.xml
+++ b/core/res/res/drawable/btn_color_quantum_dark.xml
@@ -14,21 +14,16 @@
      limitations under the License.
 -->
 
-<reveal xmlns:android="http://schemas.android.com/apk/res/android">
-    <item>
-        <selector>
-            <item android:state_enabled="false">
-                <nine-patch android:src="@drawable/btn_qntm_alpha"
-                    android:tint="@color/btn_default_normal_quantum_light" />
-            </item>
-            <item>
-                <nine-patch android:src="@drawable/btn_qntm_alpha"
-                    android:tint="@color/theme_color_500" />
-            </item>
-        </selector>
-    </item>
-    <item>
-        <nine-patch android:src="@drawable/btn_qntm_alpha"
-            android:tint="@color/theme_color_300" />
-    </item>
-</reveal>
+<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
+    android:tint="@color/theme_color_300">
+    <selector>
+        <item android:state_enabled="false">
+            <nine-patch android:src="@drawable/btn_qntm_alpha"
+                android:tint="@color/btn_default_normal_quantum_dark" />
+        </item>
+        <item>
+            <nine-patch android:src="@drawable/btn_qntm_alpha"
+                android:tint="@color/theme_color_500" />
+        </item>
+    </selector>
+</touch-feedback>
diff --git a/core/res/res/drawable/btn_color_quantum_light.xml b/core/res/res/drawable/btn_color_quantum_light.xml
index d6be958..9166b8d 100644
--- a/core/res/res/drawable/btn_color_quantum_light.xml
+++ b/core/res/res/drawable/btn_color_quantum_light.xml
@@ -14,21 +14,16 @@
      limitations under the License.
 -->
 
-<reveal xmlns:android="http://schemas.android.com/apk/res/android">
-    <item>
-        <selector>
-            <item android:state_enabled="false">
-                <nine-patch android:src="@drawable/btn_qntm_alpha"
-                    android:tint="@color/btn_default_normal_quantum_dark" />
-            </item>
-            <item>
-                <nine-patch android:src="@drawable/btn_qntm_alpha"
-                    android:tint="@color/theme_color_500" />
-            </item>
-        </selector>
-    </item>
-    <item>
-        <nine-patch android:src="@drawable/btn_qntm_alpha"
-            android:tint="@color/theme_color_700" />
-    </item>
-</reveal>
+<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
+    android:tint="@color/theme_color_700">
+    <selector>
+        <item android:state_enabled="false">
+            <nine-patch android:src="@drawable/btn_qntm_alpha"
+                android:tint="@color/btn_default_normal_quantum_dark" />
+        </item>
+        <item>
+            <nine-patch android:src="@drawable/btn_qntm_alpha"
+                android:tint="@color/theme_color_500" />
+        </item>
+    </selector>
+</touch-feedback>
diff --git a/core/res/res/drawable/btn_default_quantum_dark.xml b/core/res/res/drawable/btn_default_quantum_dark.xml
index 7f0cca8..29c3e24 100644
--- a/core/res/res/drawable/btn_default_quantum_dark.xml
+++ b/core/res/res/drawable/btn_default_quantum_dark.xml
@@ -14,13 +14,8 @@
      limitations under the License.
 -->
 
-<reveal xmlns:android="http://schemas.android.com/apk/res/android">
-    <item>
-        <nine-patch android:src="@drawable/btn_qntm_alpha"
-            android:tint="@color/btn_default_normal_quantum_dark" />
-    </item>
-    <item>
-        <nine-patch android:src="@drawable/btn_qntm_alpha"
-            android:tint="@color/btn_default_pressed_quantum_dark" />
-    </item>
-</reveal>
+<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
+    android:tint="@color/btn_default_pressed_quantum_dark">
+    <nine-patch android:src="@drawable/btn_qntm_alpha"
+        android:tint="@color/btn_default_normal_quantum_dark" />
+</touch-feedback>
diff --git a/core/res/res/drawable/btn_default_quantum_light.xml b/core/res/res/drawable/btn_default_quantum_light.xml
index e391a80..c6e828c 100644
--- a/core/res/res/drawable/btn_default_quantum_light.xml
+++ b/core/res/res/drawable/btn_default_quantum_light.xml
@@ -14,13 +14,8 @@
      limitations under the License.
 -->
 
-<reveal xmlns:android="http://schemas.android.com/apk/res/android">
-    <item>
-        <nine-patch android:src="@drawable/btn_qntm_alpha"
-            android:tint="@color/btn_default_normal_quantum_light" />
-    </item>
-    <item>
-        <nine-patch android:src="@drawable/btn_qntm_alpha"
-            android:tint="@color/btn_default_pressed_quantum_light" />
-    </item>
-</reveal>
+<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
+    android:tint="@color/btn_default_pressed_quantum_light">
+    <nine-patch android:src="@drawable/btn_qntm_alpha"
+        android:tint="@color/btn_default_normal_quantum_light" />
+</touch-feedback>
diff --git a/core/res/res/drawable/item_background_borderless_quantum_dark.xml b/core/res/res/drawable/item_background_borderless_quantum_dark.xml
index 1caee4e..3f32850 100644
--- a/core/res/res/drawable/item_background_borderless_quantum_dark.xml
+++ b/core/res/res/drawable/item_background_borderless_quantum_dark.xml
@@ -15,4 +15,4 @@
 -->
 
 <touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/lighter_gray" />
+    android:tint="@color/lighter_gray" />
diff --git a/core/res/res/drawable/item_background_borderless_quantum_light.xml b/core/res/res/drawable/item_background_borderless_quantum_light.xml
index ecf7dfb..09f6ae9 100644
--- a/core/res/res/drawable/item_background_borderless_quantum_light.xml
+++ b/core/res/res/drawable/item_background_borderless_quantum_light.xml
@@ -15,4 +15,4 @@
 -->
 
 <touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
-    android:color="@color/darker_gray" />
+    android:tint="@color/darker_gray" />
diff --git a/core/res/res/drawable/item_background_quantum_dark.xml b/core/res/res/drawable/item_background_quantum_dark.xml
index 5ccaa8e..3f32850 100644
--- a/core/res/res/drawable/item_background_quantum_dark.xml
+++ b/core/res/res/drawable/item_background_quantum_dark.xml
@@ -14,7 +14,5 @@
      limitations under the License.
 -->
 
-<reveal xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:drawable="@color/transparent" />
-    <item android:drawable="@color/lighter_gray" />
-</reveal>
+<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
+    android:tint="@color/lighter_gray" />
diff --git a/core/res/res/drawable/item_background_quantum_light.xml b/core/res/res/drawable/item_background_quantum_light.xml
index f1453c5..09f6ae9 100644
--- a/core/res/res/drawable/item_background_quantum_light.xml
+++ b/core/res/res/drawable/item_background_quantum_light.xml
@@ -14,7 +14,5 @@
      limitations under the License.
 -->
 
-<reveal xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:drawable="@color/transparent" />
-    <item android:drawable="@color/darker_gray" />
-</reveal>
+<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
+    android:tint="@color/darker_gray" />
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index f85b193..2043960 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -4429,6 +4429,20 @@
         <attr name="color" />
     </declare-styleable>
 
+    <!-- Drawable used to show animated touch feedback. -->
+    <declare-styleable name="TouchFeedbackDrawable">
+        <!-- The tint to use for feedback ripples. This attribute is mandatory. -->
+        <attr name="tint" />
+        <!-- Specifies the Porter-Duff blending mode used to apply the tint. The default vlaue is src_atop, which draws over the opaque parts of the drawable. -->
+        <attr name="tintMode" />
+        <!-- Whether to pin feedback ripples to the center of the drawable. Default value is false. -->
+        <attr name="pinned" format="boolean" />
+        <!-- Optional drawable used to mask ripple bounds before projection. -->
+        <attr name="mask" format="reference" />
+        <!-- Optional drawable onto which ripples are projected. -->
+        <attr name="drawable" />
+    </declare-styleable>
+
     <declare-styleable name="ScaleDrawable">
         <!-- Scale width, expressed as a percentage of the drawable's bound. The value's
              format is XX%. For instance: 100%, 12.5%, etc.-->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 3ab3e5d..c9d203f 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2088,7 +2088,7 @@
   <public type="style" name="Theme.DeviceDefault.Light.NoActionBar.TranslucentDecor" id="0x010301e4" />
 
 <!-- ===============================================================
-     Resources added in version 20 of the platform
+     Resources added in version 21 of the platform
      =============================================================== -->
   <eat-comment />
 
@@ -2106,6 +2106,7 @@
   <public type="attr" name="transitionGroup" />
   <public type="attr" name="castsShadow" />
   <public type="attr" name="requiredForProfile"/>
+  <public type="attr" name="pinned" />
 
   <public type="id" name="shared_element_name" />
 
@@ -2311,4 +2312,10 @@
   <public type="style" name="Quantum.Light.ButtonBar.AlertDialog" />
   <public type="style" name="Quantum.Light.ButtonBar" />
   <public type="style" name="Quantum.Light.SegmentedButton" />
+
+  <public type="style" name="Widget.Quantum.Button.Paper" />
+  <public type="style" name="Widget.Quantum.Button.Paper.Color" />
+
+  <public type="style" name="Widget.Quantum.Light.Button.Paper" />
+  <public type="style" name="Widget.Quantum.Light.Button.Paper.Color" />
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index afb7085..f1bcf65 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2012,6 +2012,10 @@
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_accessNetworkConditions">Allows an application to listen for observations on network conditions. Should never be needed for normal apps.</string>
 
+    <string name="permlab_setInputCalibration">change input device calibration</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_setInputCalibration">Allows the app to modify the calibration parameters of the touch screen. Should never be needed for normal apps.</string>
+
     <!-- Policy administration -->
 
     <!-- Title of policy access to limiting the user's password choices -->
diff --git a/graphics/java/android/graphics/drawable/DrawableWrapper.java b/graphics/java/android/graphics/drawable/DrawableWrapper.java
new file mode 100644
index 0000000..e6a755f
--- /dev/null
+++ b/graphics/java/android/graphics/drawable/DrawableWrapper.java
@@ -0,0 +1,317 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics.drawable;
+
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Rect;
+import android.graphics.Xfermode;
+
+/**
+ * A Drawable that wraps another Drawable.
+ */
+class DrawableWrapper extends Drawable implements Drawable.Callback {
+    private WrapperState mWrapperState;
+
+    /** Local drawable backed by its own constant state. */
+    private Drawable mWrappedDrawable;
+
+    private boolean mMutated;
+
+    /** @hide */
+    @Override
+    public boolean isProjected() {
+        return mWrappedDrawable.isProjected();
+    }
+
+    @Override
+    public void setAutoMirrored(boolean mirrored) {
+        mWrappedDrawable.setAutoMirrored(mirrored);
+    }
+
+    @Override
+    public boolean isAutoMirrored() {
+        return mWrappedDrawable.isAutoMirrored();
+    }
+
+    @Override
+    public int getMinimumWidth() {
+        return mWrappedDrawable.getMinimumWidth();
+    }
+
+    @Override
+    public int getMinimumHeight() {
+        return mWrappedDrawable.getMinimumHeight();
+    }
+
+    @Override
+    public int getIntrinsicWidth() {
+        return mWrappedDrawable.getIntrinsicWidth();
+    }
+
+    @Override
+    public int getIntrinsicHeight() {
+        return mWrappedDrawable.getIntrinsicHeight();
+    }
+
+    @Override
+    public Drawable getCurrent() {
+        return mWrappedDrawable.getCurrent();
+    }
+
+    @Override
+    public void invalidateDrawable(Drawable who) {
+        final Callback callback = getCallback();
+        if (callback != null) {
+            callback.invalidateDrawable(this);
+        }
+    }
+
+    @Override
+    public void scheduleDrawable(Drawable who, Runnable what, long when) {
+        final Callback callback = getCallback();
+        if (callback != null) {
+            callback.scheduleDrawable(this, what, when);
+        }
+    }
+
+    @Override
+    public void unscheduleDrawable(Drawable who, Runnable what) {
+        final Callback callback = getCallback();
+        if (callback != null) {
+            callback.unscheduleDrawable(this, what);
+        }
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        mWrappedDrawable.draw(canvas);
+    }
+
+    @Override
+    public int getChangingConfigurations() {
+        return mWrappedDrawable.getChangingConfigurations();
+    }
+
+    @Override
+    public boolean getPadding(Rect padding) {
+        return mWrappedDrawable.getPadding(padding);
+    }
+
+    @Override
+    public Rect getDirtyBounds() {
+        return mWrappedDrawable.getDirtyBounds();
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public boolean supportsHotspots() {
+        return mWrappedDrawable.supportsHotspots();
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void setHotspot(int id, float x, float y) {
+        mWrappedDrawable.setHotspot(id, x, y);
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void removeHotspot(int id) {
+        mWrappedDrawable.removeHotspot(id);
+    }
+
+    /**
+     * @hide
+     */
+    @Override
+    public void clearHotspots() {
+        mWrappedDrawable.clearHotspots();
+    }
+
+    @Override
+    public boolean setVisible(boolean visible, boolean restart) {
+        // Must call through to super().
+        super.setVisible(visible, restart);
+        return mWrappedDrawable.setVisible(visible, restart);
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+        mWrappedDrawable.setAlpha(alpha);
+    }
+
+    @Override
+    public int getAlpha() {
+        return mWrappedDrawable.getAlpha();
+    }
+
+    /** {@hide} */
+    @Override
+    public void setLayoutDirection(int layoutDirection) {
+        mWrappedDrawable.setLayoutDirection(layoutDirection);
+    }
+
+    /** {@hide} */
+    @Override
+    public int getLayoutDirection() {
+        return mWrappedDrawable.getLayoutDirection();
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter cf) {
+        mWrappedDrawable.setColorFilter(cf);
+    }
+
+    @Override
+    public ColorFilter getColorFilter() {
+        return mWrappedDrawable.getColorFilter();
+    }
+
+    @Override
+    public void setFilterBitmap(boolean filter) {
+        mWrappedDrawable.setFilterBitmap(filter);
+    }
+
+    @Override
+    public void setXfermode(Xfermode mode) {
+        mWrappedDrawable.setXfermode(mode);
+    }
+
+    @Override
+    public int getOpacity() {
+        return mWrappedDrawable.getOpacity();
+    }
+
+    @Override
+    public boolean isStateful() {
+        return mWrappedDrawable.isStateful();
+    }
+    
+    @Override
+    public final boolean setState(int[] stateSet) {
+        return super.setState(stateSet);
+    }
+
+    @Override
+    public final int[] getState() {
+        return super.getState();
+    }
+
+    @Override
+    protected boolean onStateChange(int[] state) {
+        // Don't override setState(), getState().
+        return mWrappedDrawable.setState(state);
+    }
+
+    @Override
+    protected boolean onLevelChange(int level) {
+        // Don't override setLevel(), getLevel().
+        return mWrappedDrawable.setLevel(level);
+    }
+    
+    @Override
+    public final void setBounds(int left, int top, int right, int bottom) {
+        super.setBounds(left, top, right, bottom);
+    }
+    
+    @Override
+    public final void setBounds(Rect bounds) {
+        super.setBounds(bounds);
+    }
+
+    @Override
+    protected void onBoundsChange(Rect bounds) {
+        // Don't override setBounds(), getBounds().
+        mWrappedDrawable.setBounds(bounds);
+    }
+
+    protected void setConstantState(WrapperState wrapperState, Resources res) {
+        mWrapperState = wrapperState;
+
+        // Load a new drawable from the constant state.
+        if (wrapperState == null || wrapperState.mWrappedConstantState == null) {
+            mWrappedDrawable = null;
+        } else if (res != null) {
+            mWrappedDrawable = wrapperState.mWrappedConstantState.newDrawable(res);
+        } else {
+            mWrappedDrawable = wrapperState.mWrappedConstantState.newDrawable();
+        }
+    }
+
+    @Override
+    public ConstantState getConstantState() {
+        return mWrapperState;
+    }
+
+    @Override
+    public Drawable mutate() {
+        if (!mMutated) {
+            mWrappedDrawable = mWrappedDrawable.mutate();
+            mMutated = true;
+        }
+        return this;
+    }
+
+    /**
+     * Sets the wrapped drawable and update the constant state.
+     *
+     * @param drawable
+     * @param res
+     */
+    protected final void setDrawable(Drawable drawable, Resources res) {
+        if (mWrappedDrawable != null) {
+            mWrappedDrawable.setCallback(null);
+        }
+
+        mWrappedDrawable = drawable;
+
+        if (drawable != null) {
+            drawable.setCallback(this);
+
+            mWrapperState.mWrappedConstantState = drawable.getConstantState();
+        } else {
+            mWrapperState.mWrappedConstantState = null;
+        }
+    }
+
+    protected final Drawable getDrawable() {
+        return mWrappedDrawable;
+    }
+
+    static abstract class WrapperState extends ConstantState {
+        ConstantState mWrappedConstantState;
+
+        WrapperState(WrapperState orig) {
+            if (orig != null) {
+                mWrappedConstantState = orig.mWrappedConstantState;
+            }
+        }
+
+        @Override
+        public int getChangingConfigurations() {
+            return mWrappedConstantState.getChangingConfigurations();
+        }
+    }
+}
diff --git a/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java b/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java
index 6fbcb53..f4f545c 100644
--- a/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java
+++ b/graphics/java/android/graphics/drawable/TouchFeedbackDrawable.java
@@ -21,15 +21,18 @@
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.Color;
-import android.graphics.ColorFilter;
 import android.graphics.Paint;
 import android.graphics.PixelFormat;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
 import android.os.SystemClock;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.SparseArray;
 
+import com.android.internal.R;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -37,23 +40,9 @@
 import java.util.ArrayList;
 
 /**
- * An extension of LayerDrawable that is intended to react to touch hotspots
- * and reveal the second layer atop the first.
- * <p>
- * It can be defined in an XML file with the <code>&lt;reveal&gt;</code> element.
- * Each Drawable in the transition is defined in a nested <code>&lt;item&gt;</code>.
- * For more information, see the guide to <a href="{@docRoot}
- * guide/topics/resources/drawable-resource.html">Drawable Resources</a>.
- *
- * @attr ref android.R.styleable#LayerDrawableItem_left
- * @attr ref android.R.styleable#LayerDrawableItem_top
- * @attr ref android.R.styleable#LayerDrawableItem_right
- * @attr ref android.R.styleable#LayerDrawableItem_bottom
- * @attr ref android.R.styleable#LayerDrawableItem_drawable
- * @attr ref android.R.styleable#LayerDrawableItem_id
  * @hide
  */
-public class TouchFeedbackDrawable extends Drawable {
+public class TouchFeedbackDrawable extends DrawableWrapper {
     private final Rect mTempRect = new Rect();
     private final Rect mPaddingRect = new Rect();
 
@@ -77,32 +66,43 @@
     /** Paint used to control appearance of ripples. */
     private Paint mRipplePaint;
 
+    /** Paint used to control reveal layer masking. */
+    private Paint mMaskingPaint;
+
     /** Target density of the display into which ripples are drawn. */
     private float mDensity = 1.0f;
 
     /** Whether the animation runnable has been posted. */
     private boolean mAnimating;
 
-    TouchFeedbackDrawable() {
-        this(new TouchFeedbackState(null), null);
+    /** The drawable to use as the mask. */
+    private Drawable mMask;
+
+    /* package */TouchFeedbackDrawable() {
+        this(null, null);
     }
 
     TouchFeedbackDrawable(TouchFeedbackState state, Resources res) {
+        mState = new TouchFeedbackState(state);
+        
+        setConstantState(mState, res);
+
         if (res != null) {
             mDensity = res.getDisplayMetrics().density;
         }
-
-        mState = state;
     }
+    
+    private void setConstantState(TouchFeedbackState wrapperState, Resources res) {
+        super.setConstantState(wrapperState, res);
 
-    @Override
-    public void setColorFilter(ColorFilter cf) {
-        // Not supported.
-    }
-
-    @Override
-    public void setAlpha(int alpha) {
-        // Not supported.
+        // Load a new mask drawable from the constant state.
+        if (wrapperState == null || wrapperState.mMaskState == null) {
+            mMask = null;
+        } else if (res != null) {
+            mMask = wrapperState.mMaskState.newDrawable(res);
+        } else {
+            mMask = wrapperState.mMaskState.newDrawable();
+        }
     }
 
     @Override
@@ -112,9 +112,20 @@
     }
 
     @Override
+    protected void onBoundsChange(Rect bounds) {
+        super.onBoundsChange(bounds);
+
+        if (mMask != null) {
+            mMask.setBounds(bounds);
+        }
+    }
+
+    @Override
     protected boolean onStateChange(int[] stateSet) {
-        final ColorStateList stateList = mState.mColorStateList;
-        if (stateList != null && mRipplePaint != null) {
+        super.onStateChange(stateSet);
+
+        if (mRipplePaint != null) {
+            final ColorStateList stateList = mState.mTint;
             final int newColor = stateList.getColorForState(stateSet, 0);
             final int oldColor = mRipplePaint.getColor();
             if (oldColor != newColor) {
@@ -132,12 +143,12 @@
      */
     @Override
     public boolean isProjected() {
-        return true;
+        return mState.mProjected;
     }
 
     @Override
     public boolean isStateful() {
-        return mState.mColorStateList != null && mState.mColorStateList.isStateful();
+        return super.isStateful() || mState.mTint.isStateful();
     }
 
     @Override
@@ -145,16 +156,78 @@
             throws XmlPullParserException, IOException {
         super.inflate(r, parser, attrs);
 
-        final TypedArray a = r.obtainAttributes(
-                attrs, com.android.internal.R.styleable.ColorDrawable);
-        mState.mColorStateList = a.getColorStateList(
-                com.android.internal.R.styleable.ColorDrawable_color);
+        final TypedArray a = r.obtainAttributes(attrs, R.styleable.TouchFeedbackDrawable);
+
+        mState.mTint = a.getColorStateList(R.styleable.TouchFeedbackDrawable_tint);
+        mState.mTintMode = Drawable.parseTintMode(
+                a.getInt(R.styleable.TouchFeedbackDrawable_tintMode, -1), Mode.SRC_ATOP);
+        mState.mPinned = a.getBoolean(R.styleable.TouchFeedbackDrawable_pinned, false);
+
+        if (mState.mTint == null) {
+            throw new XmlPullParserException(parser.getPositionDescription()
+                    + ": <touch-feedback> tag requires a 'tint' attribute");
+        }
+        
+        Drawable mask = a.getDrawable(R.styleable.TouchFeedbackDrawable_mask);
+        final int drawableRes = a.getResourceId(R.styleable.TouchFeedbackDrawable_drawable, 0);
         a.recycle();
 
+        final Drawable dr;
+        if (drawableRes != 0) {
+            dr = r.getDrawable(drawableRes);
+        } else {
+            int type;
+            while ((type = parser.next()) == XmlPullParser.TEXT) {
+                // Find the next non-text element.
+            }
+
+            if (type == XmlPullParser.START_TAG) {
+                dr = Drawable.createFromXmlInner(r, parser, attrs);
+            } else {
+                dr = null;
+            }
+        }
+
+        // If no mask is set, implicitly use the lower drawable.
+        if (mask == null) {
+            mask = dr;
+        }
+
+        // If neither a mask not a bottom layer was specified, assume we're
+        // projecting onto a parent surface.
+        mState.mProjected = mask == null && dr == null;
+
+        if (dr != null) {
+            setDrawable(dr, r);
+        } else {
+            // For now at least, we MUST have a wrapped drawable.
+            setDrawable(new ColorDrawable(Color.TRANSPARENT), r);
+        }
+
+        setMaskDrawable(mask, r);
         setTargetDensity(r.getDisplayMetrics());
     }
 
     /**
+     * Sets the wrapped drawable and update the constant state.
+     *
+     * @param drawable
+     * @param res
+     */
+    void setMaskDrawable(Drawable drawable, Resources res) {
+        mMask = drawable;
+
+        if (drawable != null) {
+            // Nobody cares if the mask has a callback.
+            drawable.setCallback(null);
+
+            mState.mMaskState = drawable.getConstantState();
+        } else {
+            mState.mMaskState = null;
+        }
+    }
+
+    /**
      * Set the density at which this drawable will be rendered.
      *
      * @param metrics The display metrics for this drawable.
@@ -175,6 +248,9 @@
     }
 
     /**
+     * TODO: Maybe we should set hotspots for state/id combinations? So touch
+     * would be state_pressed and the pointer ID.
+     *
      * @hide until hotspot APIs are finalized
      */
     @Override
@@ -186,19 +262,22 @@
 
         final Ripple ripple = mTouchedRipples.get(id);
         if (ripple == null) {
+            final Rect bounds = getBounds();
             final Rect padding = mPaddingRect;
             getPadding(padding);
 
-            final Rect bounds = getBounds();
-            final Ripple newRipple = new Ripple(bounds, padding, bounds.exactCenterX(),
-                    bounds.exactCenterY(), mDensity);
+            if (mState.mPinned) {
+                x = bounds.exactCenterX();
+                y = bounds.exactCenterY();
+            }
+
+            final Ripple newRipple = new Ripple(bounds, padding, x, y, mDensity);
             newRipple.enter();
 
             mActiveRipples.add(newRipple);
             mTouchedRipples.put(id, newRipple);
-        } else {
-            // TODO: How do we want to respond to movement?
-            //ripple.move(x, y);
+        } else if (!mState.mPinned) {
+            ripple.move(x, y);
         }
 
         scheduleAnimation();
@@ -254,7 +333,7 @@
 
             if (mAnimationRunnable == null) {
                 mAnimationRunnable = new Runnable() {
-                    @Override
+                        @Override
                     public void run() {
                         mAnimating = false;
                         scheduleAnimation();
@@ -269,47 +348,77 @@
 
     @Override
     public void draw(Canvas canvas) {
+        // The lower layer always draws normally.
+        super.draw(canvas);
+
+        if (mActiveRipples == null || mActiveRipples.size() == 0) {
+            // No ripples to draw.
+            return;
+        }
+
         final ArrayList<Ripple> activeRipples = mActiveRipples;
-        if (activeRipples == null || activeRipples.isEmpty()) {
-            // Nothing to draw, we're done here.
-            return;
-        }
+        final Drawable mask = mMask;
+        final Rect bounds = mask == null ? null : mask.getBounds();
 
-        final ColorStateList stateList = mState.mColorStateList;
-        if (stateList == null) {
-            // No color, we're done here.
-            return;
-        }
-
-        final int color = stateList.getColorForState(getState(), Color.TRANSPARENT);
-        if (color == Color.TRANSPARENT) {
-            // No color, we're done here.
-            return;
-        }
-
-        if (mRipplePaint == null) {
-            mRipplePaint = new Paint();
-            mRipplePaint.setAntiAlias(true);
-        }
-
-        mRipplePaint.setColor(color);
-
-        final int restoreCount = canvas.save();
-
-        // Draw ripples directly onto the canvas.
+        // Draw ripples into a layer that merges using SRC_IN.
+        boolean hasRipples = false;
+        int rippleRestoreCount = -1;
         int n = activeRipples.size();
         for (int i = 0; i < n; i++) {
             final Ripple ripple = activeRipples.get(i);
             if (!ripple.active()) {
+                // TODO: Mark and sweep is more efficient.
                 activeRipples.remove(i);
                 i--;
                 n--;
             } else {
-                ripple.draw(canvas, mRipplePaint);
+                // If we're masking the ripple layer, make sure we have a layer first.
+                if (mask != null && rippleRestoreCount < 0) {
+                    rippleRestoreCount = canvas.saveLayer(bounds.left, bounds.top,
+                            bounds.right, bounds.bottom, getMaskingPaint(SRC_ATOP), 0);
+                    canvas.clipRect(bounds);
+                }
+
+                hasRipples |= ripple.draw(canvas, getRipplePaint());
             }
         }
 
-        canvas.restoreToCount(restoreCount);
+        // If we have ripples, mask them.
+        if (mask != null && hasRipples) {
+            canvas.saveLayer(bounds.left, bounds.top, bounds.right,
+                    bounds.bottom, getMaskingPaint(DST_IN), 0);
+            mask.draw(canvas);
+        }
+
+        // Composite the layers if needed:
+        // 1. Mask     DST_IN
+        // 2. Ripples  SRC_ATOP
+        // 3. Lower    n/a
+        if (rippleRestoreCount > 0) {
+            canvas.restoreToCount(rippleRestoreCount);
+        }
+    }
+
+    private Paint getRipplePaint() {
+        if (mRipplePaint == null) {
+            mRipplePaint = new Paint();
+            mRipplePaint.setAntiAlias(true);
+            
+            final int color = mState.mTint.getColorForState(getState(), Color.TRANSPARENT);
+            mRipplePaint.setColor(color);
+        }
+        return mRipplePaint;
+    }
+    
+    private static final PorterDuffXfermode SRC_ATOP = new PorterDuffXfermode(Mode.SRC_ATOP);
+    private static final PorterDuffXfermode DST_IN = new PorterDuffXfermode(Mode.DST_IN);
+
+    private Paint getMaskingPaint(PorterDuffXfermode mode) {
+        if (mMaskingPaint == null) {
+            mMaskingPaint = new Paint();
+        }
+        mMaskingPaint.setXfermode(mode);
+        return mMaskingPaint;
     }
 
     @Override
@@ -322,39 +431,46 @@
         final Rect rippleBounds = mTempRect;
         final ArrayList<Ripple> activeRipples = mActiveRipples;
         if (activeRipples != null) {
-           final int N = activeRipples.size();
-           for (int i = 0; i < N; i++) {
-               activeRipples.get(i).getBounds(rippleBounds);
-               drawingBounds.union(rippleBounds);
-           }
+            final int N = activeRipples.size();
+            for (int i = 0; i < N; i++) {
+                activeRipples.get(i).getBounds(rippleBounds);
+                drawingBounds.union(rippleBounds);
+            }
         }
 
         dirtyBounds.union(drawingBounds);
+        dirtyBounds.union(super.getDirtyBounds());
         return dirtyBounds;
     }
 
     @Override
     public ConstantState getConstantState() {
+        // TODO: Can we just rely on super.getConstantState()?
         return mState;
     }
 
-    static class TouchFeedbackState extends ConstantState {
-        ColorStateList mColorStateList;
+    static class TouchFeedbackState extends WrapperState {
+        ConstantState mMaskState;
+        ColorStateList mTint;
+        Mode mTintMode;
+        boolean mPinned;
+        boolean mProjected;
 
         public TouchFeedbackState(TouchFeedbackState orig) {
+            super(orig);
+
             if (orig != null) {
-                mColorStateList = orig.mColorStateList;
+                mTint = orig.mTint;
+                mTintMode = orig.mTintMode;
+                mMaskState = orig.mMaskState;
+                mPinned = orig.mPinned;
+                mProjected = orig.mProjected;
             }
         }
 
         @Override
-        public int getChangingConfigurations() {
-            return 0;
-        }
-
-        @Override
         public Drawable newDrawable() {
-            return newDrawable(null);
+            return new TouchFeedbackDrawable(this, null);
         }
 
         @Override
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 3759108..5611efb 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -16,8 +16,13 @@
 
 package android.media;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
+import java.nio.ByteBuffer;
+import java.nio.NioUtils;
 
+import android.annotation.IntDef;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
@@ -145,6 +150,28 @@
     private final static String TAG = "android.media.AudioTrack";
 
 
+    /** @hide */
+    @IntDef({
+        WRITE_BLOCKING,
+        WRITE_NON_BLOCKING
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface WriteMode {}
+
+    /**
+     * @hide CANDIDATE FOR PUBLIC API
+     * The write mode indicating the write operation will block until all data has been written,
+     * to be used in {@link #write(ByteBuffer, int, int, int)}.
+     */
+    public final static int WRITE_BLOCKING = 0;
+    /**
+     * @hide CANDIDATE FOR PUBLIC API
+     * The write mode indicating the write operation will return immediately after
+     * queuing as much audio data for playback as possible without blocking, to be used in
+     * {@link #write(ByteBuffer, int, int, int)}.
+     */
+    public final static int WRITE_NON_BLOCKING = 1;
+
     //--------------------------------------------------------------------------
     // Member variables
     //--------------------
@@ -1084,7 +1111,8 @@
             return ERROR_BAD_VALUE;
         }
 
-        int ret = native_write_byte(audioData, offsetInBytes, sizeInBytes, mAudioFormat);
+        int ret = native_write_byte(audioData, offsetInBytes, sizeInBytes, mAudioFormat,
+                true /*isBlocking*/);
 
         if ((mDataLoadMode == MODE_STATIC)
                 && (mState == STATE_NO_STATIC_DATA)
@@ -1141,6 +1169,75 @@
 
 
     /**
+     * @hide CANDIDATE FOR PUBLIC API
+     * Writes the audio data to the audio sink for playback (streaming mode),
+     * or copies audio data for later playback (static buffer mode).
+     * In static buffer mode, copies the data to the buffer starting at its 0 offset, and the write
+     * mode is ignored.
+     * In streaming mode, the blocking behavior will depend on the write mode.
+     * @param audioData the buffer that holds the data to play, starting at the position reported
+     *     by <code>audioData.position()</code>.
+     *     <BR>Note that this method will not update the position in this buffer, therefore when
+     *     writing a loop to write all the data in the buffer, you should increment the
+     *     <code>offsetInBytes</code> parameter at each pass by the amount that was previously
+     *     written for this buffer.
+     * @param offsetInBytes offset to read from in bytes (note this differs from
+     *     <code>audioData.position()</code>).
+     * @param sizeInBytes number of bytes to read (note this differs from
+     *     <code>audioData.remaining()</code>).
+     * @param writeMode one of {@link #WRITE_BLOCKING}, {@link #WRITE_NON_BLOCKING}. It has no
+     *     effect in static mode.
+     *     <BR>With {@link #WRITE_BLOCKING}, the write will block until all data has been written
+     *         to the audio sink.
+     *     <BR>With {@link #WRITE_NON_BLOCKING}, the write will return immediately after
+     *     queuing as much audio data for playback as possible without blocking.
+     * @return 0 or a positive number of bytes that were written, or
+     *     {@link #ERROR_BAD_VALUE}, {@link #ERROR_INVALID_OPERATION}
+     */
+    public int write(ByteBuffer audioData, int offsetInBytes, int sizeInBytes,
+            @WriteMode int writeMode) {
+
+        if (mState == STATE_UNINITIALIZED) {
+            Log.e(TAG, "AudioTrack.write() called in invalid state STATE_UNINITIALIZED");
+            return ERROR_INVALID_OPERATION;
+        }
+
+        if ((writeMode != WRITE_BLOCKING) && (writeMode != WRITE_NON_BLOCKING)) {
+            Log.e(TAG, "AudioTrack.write() called with invalid blocking mode");
+            return ERROR_BAD_VALUE;
+        }
+
+        if ( (audioData == null) || (offsetInBytes < 0 ) || (sizeInBytes < 0)
+                || (offsetInBytes + sizeInBytes < 0)    // detect integer overflow
+                || (offsetInBytes + sizeInBytes > audioData.remaining())) {
+            Log.e(TAG, "AudioTrack.write() called with invalid size/offset values");
+            return ERROR_BAD_VALUE;
+        }
+
+        int ret = 0;
+        if (audioData.isDirect()) {
+            ret = native_write_native_bytes(audioData,
+                    audioData.position(),
+                    offsetInBytes, sizeInBytes, mAudioFormat,
+                    writeMode == WRITE_BLOCKING);
+        } else {
+            ret = native_write_byte(NioUtils.unsafeArray(audioData),
+                    NioUtils.unsafeArrayOffset(audioData) + audioData.position() + offsetInBytes,
+                    sizeInBytes, mAudioFormat,
+                    writeMode == WRITE_BLOCKING);
+        }
+
+        if ((mDataLoadMode == MODE_STATIC)
+                && (mState == STATE_NO_STATIC_DATA)
+                && (ret > 0)) {
+            // benign race with respect to other APIs that read mState
+            mState = STATE_INITIALIZED;
+        }
+
+        return ret;
+    }
+
+    /**
      * Notifies the native resource to reuse the audio data already loaded in the native
      * layer, that is to rewind to start of buffer.
      * The track's creation mode must be {@link #MODE_STATIC}.
@@ -1339,11 +1436,15 @@
     private native final void native_flush();
 
     private native final int native_write_byte(byte[] audioData,
-                                               int offsetInBytes, int sizeInBytes, int format);
+                                               int offsetInBytes, int sizeInBytes, int format,
+                                               boolean isBlocking);
 
     private native final int native_write_short(short[] audioData,
                                                 int offsetInShorts, int sizeInShorts, int format);
 
+    private native final int native_write_native_bytes(Object audioData,
+            int positionInBytes, int offsetInBytes, int sizeInBytes, int format, boolean blocking);
+
     private native final int native_reload_static();
 
     private native final int native_get_native_frame_count();
diff --git a/media/java/android/media/MediaMuxer.java b/media/java/android/media/MediaMuxer.java
index e5c97e7..f518ab2 100644
--- a/media/java/android/media/MediaMuxer.java
+++ b/media/java/android/media/MediaMuxer.java
@@ -17,13 +17,11 @@
 package android.media;
 
 import android.media.MediaCodec.BufferInfo;
-
 import dalvik.system.CloseGuard;
 
-import java.io.File;
 import java.io.FileDescriptor;
-import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.RandomAccessFile;
 import java.nio.ByteBuffer;
 import java.util.Map;
 
@@ -79,6 +77,7 @@
         private OutputFormat() {}
         /** MPEG4 media file format*/
         public static final int MUXER_OUTPUT_MPEG_4 = 0;
+        public static final int MUXER_OUTPUT_WEBM   = 1;
     };
 
     // All the native functions are listed here.
@@ -120,20 +119,22 @@
         if (path == null) {
             throw new IllegalArgumentException("path must not be null");
         }
-        if (format != OutputFormat.MUXER_OUTPUT_MPEG_4) {
+        if (format != OutputFormat.MUXER_OUTPUT_MPEG_4 &&
+                format != OutputFormat.MUXER_OUTPUT_WEBM) {
             throw new IllegalArgumentException("format is invalid");
         }
-        FileOutputStream fos = null;
+        // Use RandomAccessFile so we can open the file with RW access;
+        // RW access allows the native writer to memory map the output file.
+        RandomAccessFile file = null;
         try {
-            File file = new File(path);
-            fos = new FileOutputStream(file);
-            FileDescriptor fd = fos.getFD();
+            file = new RandomAccessFile(path, "rws");
+            FileDescriptor fd = file.getFD();
             mNativeObject = nativeSetup(fd, format);
             mState = MUXER_STATE_INITIALIZED;
             mCloseGuard.open("release");
         } finally {
-            if (fos != null) {
-                fos.close();
+            if (file != null) {
+                file.close();
             }
         }
     }
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 2806bff..e20a4af 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -3069,7 +3069,7 @@
                 if (refreshTime ||
                         nanoTime >= mLastNanoTime + MAX_NS_WITHOUT_POSITION_CHECK) {
                     try {
-                        mLastTimeUs = mPlayer.getCurrentPosition() * 1000;
+                        mLastTimeUs = mPlayer.getCurrentPosition() * 1000L;
                         mPaused = !mPlayer.isPlaying();
                         if (DEBUG) Log.v(TAG, (mPaused ? "paused" : "playing") + " at " + mLastTimeUs);
                     } catch (IllegalStateException e) {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
index 0fa27c1..14de6dd 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardPatternView.java
@@ -58,8 +58,8 @@
     // how many cells the user has to cross before we poke the wakelock
     private static final int MIN_PATTERN_BEFORE_POKE_WAKELOCK = 2;
 
-    private int mFailedPatternAttemptsSinceLastTimeout = 0;
-    private int mTotalFailedPatternAttempts = 0;
+    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+
     private CountDownTimer mCountdownTimer = null;
     private LockPatternUtils mLockPatternUtils;
     private LockPatternView mLockPatternView;
@@ -100,6 +100,7 @@
 
     public KeyguardPatternView(Context context, AttributeSet attrs) {
         super(context, attrs);
+        mKeyguardUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
     }
 
     public void setKeyguardCallback(KeyguardSecurityCallback callback) {
@@ -202,7 +203,8 @@
         if (mCallback.isVerifyUnlockOnly()) {
             updateFooter(FooterMode.VerifyUnlocked);
         } else if (mEnableFallback &&
-                (mTotalFailedPatternAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) {
+                (mKeyguardUpdateMonitor.getFailedUnlockAttempts()
+                        >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) {
             updateFooter(FooterMode.ForgotLockPattern);
         } else {
             updateFooter(FooterMode.Normal);
@@ -211,7 +213,7 @@
     }
 
     private void displayDefaultSecurityMessage() {
-        if (KeyguardUpdateMonitor.getInstance(mContext).getMaxBiometricUnlockAttemptsReached()) {
+        if (mKeyguardUpdateMonitor.getMaxBiometricUnlockAttemptsReached()) {
             mSecurityMessageDisplay.setMessage(R.string.faceunlock_multiple_failures, true);
         } else {
             mSecurityMessageDisplay.setMessage(R.string.kg_pattern_instructions, false);
@@ -262,20 +264,20 @@
             if (mLockPatternUtils.checkPattern(pattern)) {
                 mCallback.reportUnlockAttempt(true);
                 mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Correct);
-                mTotalFailedPatternAttempts = 0;
                 mCallback.dismiss(true);
             } else {
                 if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) {
                     mCallback.userActivity(UNLOCK_PATTERN_WAKE_INTERVAL_MS);
                 }
                 mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong);
-                if (pattern.size() >= LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
-                    mTotalFailedPatternAttempts++;
-                    mFailedPatternAttemptsSinceLastTimeout++;
+                boolean registeredAttempt =
+                        pattern.size() >= LockPatternUtils.MIN_PATTERN_REGISTER_FAIL;
+                if (registeredAttempt) {
                     mCallback.reportUnlockAttempt(false);
                 }
-                if (mFailedPatternAttemptsSinceLastTimeout
-                        >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) {
+                int attempts = mKeyguardUpdateMonitor.getFailedUnlockAttempts();
+                if (registeredAttempt &&
+                        0 == (attempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT)) {
                     long deadline = mLockPatternUtils.setLockoutAttemptDeadline();
                     handleAttemptLockout(deadline);
                 } else {
@@ -363,7 +365,6 @@
                 mLockPatternView.setEnabled(true);
                 displayDefaultSecurityMessage();
                 // TODO mUnlockIcon.setVisibility(View.VISIBLE);
-                mFailedPatternAttemptsSinceLastTimeout = 0;
                 if (mEnableFallback) {
                     updateFooter(FooterMode.ForgotLockPattern);
                 } else {
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index 1e6011c..f0db449 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -198,7 +198,7 @@
     <string name="quick_settings_wifi_not_connected" msgid="7171904845345573431">"Ikke forbundet"</string>
     <string name="quick_settings_wifi_no_network" msgid="2221993077220856376">"Intet netværk"</string>
     <string name="quick_settings_wifi_off_label" msgid="7558778100843885864">"Wi-Fi slået fra"</string>
-    <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Cast-skærm"</string>
+    <string name="quick_settings_remote_display_no_connection_label" msgid="372107699274391290">"Cast skærm"</string>
     <string name="quick_settings_brightness_dialog_title" msgid="8599674057673605368">"Lysstyrke"</string>
     <string name="quick_settings_brightness_dialog_auto_brightness_label" msgid="5064982743784071218">"AUTO"</string>
     <string name="quick_settings_inversion_label" msgid="1666358784283020762">"Farveinverteringstilstand"</string>
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 9ecb917..15a68ef 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -3988,7 +3988,7 @@
                                 telephonyService.silenceRinger();
                             } else if ((mIncallPowerBehavior
                                     & Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0
-                                    && telephonyService.isOffhook()) {
+                                    && telephonyService.isOffhook() && isScreenOn) {
                                 // Otherwise, if "Power button ends call" is enabled,
                                 // the Power button will hang up any current active call.
                                 hungUp = telephonyService.endCall();
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 0b3eb12..6827b3f 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -59,6 +59,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
@@ -78,6 +79,7 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.text.style.SuggestionSpan;
@@ -441,6 +443,10 @@
                 hideInputMethodMenu();
                 // No need to updateActive
                 return;
+            } else if (Intent.ACTION_USER_ADDED.equals(action)
+                    || Intent.ACTION_USER_REMOVED.equals(action)) {
+                updateRelatedUserIds();
+                return;
             } else {
                 Slog.w(TAG, "Unexpected intent " + intent);
             }
@@ -642,6 +648,8 @@
         broadcastFilter.addAction(Intent.ACTION_SCREEN_ON);
         broadcastFilter.addAction(Intent.ACTION_SCREEN_OFF);
         broadcastFilter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
+        broadcastFilter.addAction(Intent.ACTION_USER_ADDED);
+        broadcastFilter.addAction(Intent.ACTION_USER_REMOVED);
         mContext.registerReceiver(new ImmsBroadcastReceiver(), broadcastFilter);
 
         mNotificationShown = false;
@@ -675,6 +683,7 @@
         // mSettings should be created before buildInputMethodListLocked
         mSettings = new InputMethodSettings(
                 mRes, context.getContentResolver(), mMethodMap, mMethodList, userId);
+        updateRelatedUserIds();
         mFileManager = new InputMethodFileManager(mMethodMap, userId);
         mSwitchingController = new InputMethodSubtypeSwitchingController(mSettings);
         mSwitchingController.resetCircularListLocked(context);
@@ -790,6 +799,7 @@
 
     private void switchUserLocked(int newUserId) {
         mSettings.setCurrentUserId(newUserId);
+        updateRelatedUserIds();
         // InputMethodFileManager should be reset when the user is changed
         mFileManager = new InputMethodFileManager(mMethodMap, newUserId);
         final String defaultImiId = mSettings.getSelectedInputMethod();
@@ -810,6 +820,16 @@
         }
     }
 
+    void updateRelatedUserIds() {
+        List<UserInfo> relatedUsers =
+                UserManager.get(mContext).getRelatedUsers(mSettings.getCurrentUserId());
+        int[] relatedUserIds = new int[relatedUsers.size()]; // relatedUsers will not be null
+        for (int i = 0; i < relatedUserIds.length; i++) {
+            relatedUserIds[i] = relatedUsers.get(i).id;
+        }
+        mSettings.setRelatedUserIds(relatedUserIds);
+    }
+
     @Override
     public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
             throws RemoteException {
@@ -905,7 +925,7 @@
                     + mSettings.getCurrentUserId() + ", calling pid = " + Binder.getCallingPid()
                     + InputMethodUtils.getApiCallStack());
         }
-        if (uid == Process.SYSTEM_UID || userId == mSettings.getCurrentUserId()) {
+        if (uid == Process.SYSTEM_UID || mSettings.isRelatedToOrCurrentUser(userId)) {
             return true;
         }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d910861..864211d 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1015,6 +1015,7 @@
     final ActivityThread mSystemThread;
 
     int mCurrentUserId = 0;
+    int[] mRelatedUserIds = new int[0]; // Accessed by ActivityStack
     private UserManagerService mUserManager;
 
     private final class AppDeathRecipient implements IBinder.DeathRecipient {
@@ -16127,6 +16128,20 @@
         return startUser(userId, /* foreground */ false);
     }
 
+    /**
+     * Refreshes the list of users related to the current user when either a
+     * user switch happens or when a new related user is started in the
+     * background.
+     */
+    private void updateRelatedUserIdsLocked() {
+        final List<UserInfo> relatedUsers = getUserManagerLocked().getRelatedUsers(mCurrentUserId);
+        int[] relatedUserIds = new int[relatedUsers.size()]; // relatedUsers will not be null
+        for (int i = 0; i < relatedUserIds.length; i++) {
+            relatedUserIds[i] = relatedUsers.get(i).id;
+        }
+        mRelatedUserIds = relatedUserIds;
+    }
+
     @Override
     public boolean switchUser(final int userId) {
         return startUser(userId, /* foregound */ true);
@@ -16180,12 +16195,15 @@
 
                 if (foreground) {
                     mCurrentUserId = userId;
-                    mWindowManager.setCurrentUser(userId);
+                    updateRelatedUserIdsLocked();
+                    mWindowManager.setCurrentUser(userId, mRelatedUserIds);
                     // Once the internal notion of the active user has switched, we lock the device
                     // with the option to show the user switcher on the keyguard.
                     mWindowManager.lockNow(null);
                 } else {
                     final Integer currentUserIdInt = Integer.valueOf(mCurrentUserId);
+                    updateRelatedUserIdsLocked();
+                    mWindowManager.updateRelatedUserIds(mRelatedUserIds);
                     mUserLru.remove(currentUserIdInt);
                     mUserLru.add(currentUserIdInt);
                 }
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 922cef4..f3ccdd6 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -341,8 +341,19 @@
         mCurrentUser = mService.mCurrentUserId;
     }
 
-    boolean okToShow(ActivityRecord r) {
-        return r.userId == mCurrentUser
+    /**
+     * Checks whether the userid is either the current user or a related user.
+     */
+    private boolean isRelatedToOrCurrentUserLocked(int userId) {
+        if (mCurrentUser == userId) return true;
+        for (int i = 0; i < mService.mRelatedUserIds.length; i++) {
+            if (mService.mRelatedUserIds[i] == userId) return true;
+        }
+        return false;
+    }
+
+    boolean okToShowLocked(ActivityRecord r) {
+        return isRelatedToOrCurrentUserLocked(r.userId)
                 || (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0;
     }
 
@@ -362,7 +373,7 @@
             final ArrayList<ActivityRecord> activities = task.mActivities;
             for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
                 ActivityRecord r = activities.get(activityNdx);
-                if (!r.finishing && !r.delayedResume && r != notTop && okToShow(r)) {
+                if (!r.finishing && !r.delayedResume && r != notTop && okToShowLocked(r)) {
                     return r;
                 }
             }
@@ -389,7 +400,7 @@
             for (int i = activities.size() - 1; i >= 0; --i) {
                 final ActivityRecord r = activities.get(i);
                 // Note: the taskId check depends on real taskId fields being non-zero
-                if (!r.finishing && (token != r.appToken) && okToShow(r)) {
+                if (!r.finishing && (token != r.appToken) && okToShowLocked(r)) {
                     return r;
                 }
             }
@@ -542,7 +553,7 @@
 
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
             TaskRecord task = mTaskHistory.get(taskNdx);
-            if (task.userId != mCurrentUser) {
+            if (!isRelatedToOrCurrentUserLocked(task.userId)) {
                 return null;
             }
             final ArrayList<ActivityRecord> activities = task.mActivities;
@@ -573,7 +584,7 @@
         int index = mTaskHistory.size();
         for (int i = 0; i < index; ) {
             TaskRecord task = mTaskHistory.get(i);
-            if (task.userId == userId) {
+            if (isRelatedToOrCurrentUserLocked(task.userId)) {
                 if (DEBUG_TASKS) Slog.d(TAG, "switchUserLocked: stack=" + getStackId() +
                         " moving " + task + " to top");
                 mTaskHistory.remove(i);
@@ -1728,10 +1739,10 @@
         mTaskHistory.remove(task);
         // Now put task at top.
         int stackNdx = mTaskHistory.size();
-        if (task.userId != mCurrentUser) {
+        if (!isRelatedToOrCurrentUserLocked(task.userId)) {
             // Put non-current user tasks below current user tasks.
             while (--stackNdx >= 0) {
-                if (mTaskHistory.get(stackNdx).userId != mCurrentUser) {
+                if (!isRelatedToOrCurrentUserLocked(mTaskHistory.get(stackNdx).userId)) {
                     break;
                 }
             }
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 9740812..3a43521 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -153,7 +153,7 @@
     ActivityRecord topRunningActivityLocked(ActivityRecord notTop) {
         for (int activityNdx = mActivities.size() - 1; activityNdx >= 0; --activityNdx) {
             ActivityRecord r = mActivities.get(activityNdx);
-            if (!r.finishing && r != notTop && stack.okToShow(r)) {
+            if (!r.finishing && r != notTop && stack.okToShowLocked(r)) {
                 return r;
             }
         }
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index e49382e..316bd57 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -52,6 +52,7 @@
 import android.hardware.input.InputManager;
 import android.hardware.input.InputManagerInternal;
 import android.hardware.input.KeyboardLayout;
+import android.hardware.input.TouchCalibration;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Environment;
@@ -76,6 +77,7 @@
 import android.view.InputEvent;
 import android.view.KeyEvent;
 import android.view.PointerIcon;
+import android.view.Surface;
 import android.view.ViewConfiguration;
 import android.view.WindowManagerPolicy;
 import android.widget.Toast;
@@ -183,6 +185,7 @@
             InputChannel fromChannel, InputChannel toChannel);
     private static native void nativeSetPointerSpeed(long ptr, int speed);
     private static native void nativeSetShowTouches(long ptr, boolean enabled);
+    private static native void nativeReloadCalibration(long ptr);
     private static native void nativeVibrate(long ptr, int deviceId, long[] pattern,
             int repeat, int token);
     private static native void nativeCancelVibrate(long ptr, int deviceId, int token);
@@ -700,6 +703,47 @@
         mTempFullKeyboards.clear();
     }
 
+    @Override // Binder call & native callback
+    public TouchCalibration getTouchCalibrationForInputDevice(String inputDeviceDescriptor,
+            int surfaceRotation) {
+        if (inputDeviceDescriptor == null) {
+            throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
+        }
+
+        synchronized (mDataStore) {
+            return mDataStore.getTouchCalibration(inputDeviceDescriptor, surfaceRotation);
+        }
+    }
+
+    @Override // Binder call
+    public void setTouchCalibrationForInputDevice(String inputDeviceDescriptor, int surfaceRotation,
+            TouchCalibration calibration) {
+        if (!checkCallingPermission(android.Manifest.permission.SET_INPUT_CALIBRATION,
+                "setTouchCalibrationForInputDevice()")) {
+            throw new SecurityException("Requires SET_INPUT_CALIBRATION permission");
+        }
+        if (inputDeviceDescriptor == null) {
+            throw new IllegalArgumentException("inputDeviceDescriptor must not be null");
+        }
+        if (calibration == null) {
+            throw new IllegalArgumentException("calibration must not be null");
+        }
+        if (surfaceRotation < Surface.ROTATION_0 || surfaceRotation > Surface.ROTATION_270) {
+            throw new IllegalArgumentException("surfaceRotation value out of bounds");
+        }
+
+        synchronized (mDataStore) {
+            try {
+                if (mDataStore.setTouchCalibration(inputDeviceDescriptor, surfaceRotation,
+                        calibration)) {
+                    nativeReloadCalibration(mPtr);
+                }
+            } finally {
+                mDataStore.saveIfNeeded();
+            }
+        }
+    }
+
     // Must be called on handler.
     private void showMissingKeyboardLayoutNotification() {
         if (!mKeyboardLayoutNotificationShown) {
diff --git a/services/core/java/com/android/server/input/PersistentDataStore.java b/services/core/java/com/android/server/input/PersistentDataStore.java
index 71de776..92fa813 100644
--- a/services/core/java/com/android/server/input/PersistentDataStore.java
+++ b/services/core/java/com/android/server/input/PersistentDataStore.java
@@ -24,6 +24,8 @@
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
 
+import android.view.Surface;
+import android.hardware.input.TouchCalibration;
 import android.util.AtomicFile;
 import android.util.Slog;
 import android.util.Xml;
@@ -82,6 +84,30 @@
         }
     }
 
+    public TouchCalibration getTouchCalibration(String inputDeviceDescriptor, int surfaceRotation) {
+        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false);
+        if (state == null) {
+            return TouchCalibration.IDENTITY;
+        }
+
+        TouchCalibration cal = state.getTouchCalibration(surfaceRotation);
+        if (cal == null) {
+            return TouchCalibration.IDENTITY;
+        }
+        return cal;
+    }
+
+    public boolean setTouchCalibration(String inputDeviceDescriptor, int surfaceRotation, TouchCalibration calibration) {
+        InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, true);
+
+        if (state.setTouchCalibration(surfaceRotation, calibration)) {
+            setDirty();
+            return true;
+        }
+
+        return false;
+    }
+
     public String getCurrentKeyboardLayout(String inputDeviceDescriptor) {
         InputDeviceState state = getInputDeviceState(inputDeviceDescriptor, false);
         return state != null ? state.getCurrentKeyboardLayout() : null;
@@ -275,9 +301,35 @@
     }
 
     private static final class InputDeviceState {
+        private static final String[] CALIBRATION_NAME = { "x_scale",
+                "x_ymix", "x_offset", "y_xmix", "y_scale", "y_offset" };
+
+        private TouchCalibration[] mTouchCalibration = new TouchCalibration[4];
         private String mCurrentKeyboardLayout;
         private ArrayList<String> mKeyboardLayouts = new ArrayList<String>();
 
+        public TouchCalibration getTouchCalibration(int surfaceRotation) {
+            try {
+                return mTouchCalibration[surfaceRotation];
+            } catch (ArrayIndexOutOfBoundsException ex) {
+                Slog.w(InputManagerService.TAG, "Cannot get touch calibration.", ex);
+                return null;
+            }
+        }
+
+        public boolean setTouchCalibration(int surfaceRotation, TouchCalibration calibration) {
+            try {
+                if (!calibration.equals(mTouchCalibration[surfaceRotation])) {
+                    mTouchCalibration[surfaceRotation] = calibration;
+                    return true;
+                }
+                return false;
+            } catch (ArrayIndexOutOfBoundsException ex) {
+                Slog.w(InputManagerService.TAG, "Cannot set touch calibration.", ex);
+                return false;
+            }
+        }
+
         public String getCurrentKeyboardLayout() {
             return mCurrentKeyboardLayout;
         }
@@ -389,6 +441,52 @@
                         }
                         mCurrentKeyboardLayout = descriptor;
                     }
+                } else if (parser.getName().equals("calibration")) {
+                    String format = parser.getAttributeValue(null, "format");
+                    String rotation = parser.getAttributeValue(null, "rotation");
+                    int r = -1;
+
+                    if (format == null) {
+                        throw new XmlPullParserException(
+                                "Missing format attribute on calibration.");
+                    }
+                    if (!format.equals("affine")) {
+                        throw new XmlPullParserException(
+                                "Unsupported format for calibration.");
+                    }
+                    if (rotation != null) {
+                        try {
+                            r = stringToSurfaceRotation(rotation);
+                        } catch (IllegalArgumentException e) {
+                            throw new XmlPullParserException(
+                                    "Unsupported rotation for calibration.");
+                        }
+                    }
+
+                    float[] matrix = TouchCalibration.IDENTITY.getAffineTransform();
+                    int depth = parser.getDepth();
+                    while (XmlUtils.nextElementWithin(parser, depth)) {
+                        String tag = parser.getName().toLowerCase();
+                        String value = parser.nextText();
+
+                        for (int i = 0; i < matrix.length && i < CALIBRATION_NAME.length; i++) {
+                            if (tag.equals(CALIBRATION_NAME[i])) {
+                                matrix[i] = Float.parseFloat(value);
+                                break;
+                            }
+                        }
+                    }
+
+                    if (r == -1) {
+                        // Assume calibration applies to all rotations
+                        for (r = 0; r < mTouchCalibration.length; r++) {
+                            mTouchCalibration[r] = new TouchCalibration(matrix[0],
+                                matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]);
+                        }
+                    } else {
+                        mTouchCalibration[r] = new TouchCalibration(matrix[0],
+                            matrix[1], matrix[2], matrix[3], matrix[4], matrix[5]);
+                    }
                 }
             }
 
@@ -411,6 +509,49 @@
                 }
                 serializer.endTag(null, "keyboard-layout");
             }
+
+            for (int i = 0; i < mTouchCalibration.length; i++) {
+                if (mTouchCalibration[i] != null) {
+                    String rotation = surfaceRotationToString(i);
+                    float[] transform = mTouchCalibration[i].getAffineTransform();
+
+                    serializer.startTag(null, "calibration");
+                    serializer.attribute(null, "format", "affine");
+                    serializer.attribute(null, "rotation", rotation);
+                    for (int j = 0; j < transform.length && j < CALIBRATION_NAME.length; j++) {
+                        serializer.startTag(null, CALIBRATION_NAME[j]);
+                        serializer.text(Float.toString(transform[j]));
+                        serializer.endTag(null, CALIBRATION_NAME[j]);
+                    }
+                    serializer.endTag(null, "calibration");
+                }
+            }
+        }
+
+        private static String surfaceRotationToString(int surfaceRotation) {
+            switch (surfaceRotation) {
+                case Surface.ROTATION_0:   return "0";
+                case Surface.ROTATION_90:  return "90";
+                case Surface.ROTATION_180: return "180";
+                case Surface.ROTATION_270: return "270";
+            }
+            throw new IllegalArgumentException("Unsupported surface rotation value" + surfaceRotation);
+        }
+
+        private static int stringToSurfaceRotation(String s) {
+            if ("0".equals(s)) {
+                return Surface.ROTATION_0;
+            }
+            if ("90".equals(s)) {
+                return Surface.ROTATION_90;
+            }
+            if ("180".equals(s)) {
+                return Surface.ROTATION_180;
+            }
+            if ("270".equals(s)) {
+                return Surface.ROTATION_270;
+            }
+            throw new IllegalArgumentException("Unsupported surface rotation string '" + s + "'");
         }
     }
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index c006613..be94ca7 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -19,7 +19,6 @@
 import static android.view.WindowManager.LayoutParams.*;
 
 import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
-
 import android.app.AppOpsManager;
 import android.util.ArraySet;
 import android.util.TimeUtils;
@@ -302,8 +301,16 @@
         }
     };
 
-    // Current user when multi-user is enabled. Don't show windows of non-current user.
+    /**
+     * Current user when multi-user is enabled. Don't show windows of
+     * non-current user. Also see mRelatedUserIds.
+     */
     int mCurrentUserId;
+    /**
+     * Users related to the current user. These are also allowed to show windows
+     * on the current user.
+     */
+    int[] mRelatedUserIds = new int[0];
 
     final Context mContext;
 
@@ -5284,10 +5291,16 @@
         mPolicy.setTouchExplorationEnabled(enabled);
     }
 
-    public void setCurrentUser(final int newUserId) {
+    public void updateRelatedUserIds(final int[] relatedUserIds) {
         synchronized (mWindowMap) {
-            int oldUserId = mCurrentUserId;
+            mRelatedUserIds = relatedUserIds;
+        }
+    }
+
+    public void setCurrentUser(final int newUserId, final int[] relatedUserIds) {
+        synchronized (mWindowMap) {
             mCurrentUserId = newUserId;
+            mRelatedUserIds = relatedUserIds;
             mAppTransition.setCurrentUser(newUserId);
             mPolicy.setCurrentUserLw(newUserId);
 
@@ -5302,6 +5315,15 @@
         }
     }
 
+    /* Called by WindowState */
+    boolean isRelatedToOrCurrentUserLocked(int userId) {
+        if (userId == mCurrentUserId) return true;
+        for (int i = 0; i < mRelatedUserIds.length; i++) {
+            if (mRelatedUserIds[i] == userId) return true;
+        }
+        return false;
+    }
+
     public void enableScreenAfterBoot() {
         synchronized(mWindowMap) {
             if (DEBUG_BOOT) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index a8e45c4..2c0e99e 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1249,7 +1249,7 @@
         }
 
         return win.mShowToOwnerOnly
-                && UserHandle.getUserId(win.mOwnerUid) != mService.mCurrentUserId;
+                && !mService.isRelatedToOrCurrentUserLocked(UserHandle.getUserId(win.mOwnerUid));
     }
 
     private static void applyInsets(Region outRegion, Rect frame, Rect inset) {
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 0207c55..e1069ae 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -49,6 +49,7 @@
 #include <android/graphics/GraphicsJNI.h>
 
 #include <ScopedLocalRef.h>
+#include <ScopedPrimitiveArray.h>
 #include <ScopedUtfChars.h>
 
 #include "com_android_server_power_PowerManagerService.h"
@@ -86,6 +87,7 @@
     jmethodID getPointerIcon;
     jmethodID getKeyboardLayoutOverlay;
     jmethodID getDeviceAlias;
+    jmethodID getTouchCalibrationForInputDevice;
 } gServiceClassInfo;
 
 static struct {
@@ -105,6 +107,11 @@
     jmethodID constructor;
 } gInputDeviceIdentifierInfo;
 
+static struct {
+    jclass clazz;
+    jmethodID getAffineTransform;
+} gTouchCalibrationClassInfo;
+
 
 
 // --- Global functions ---
@@ -182,6 +189,7 @@
     void setSystemUiVisibility(int32_t visibility);
     void setPointerSpeed(int32_t speed);
     void setShowTouches(bool enabled);
+    void reloadCalibration();
 
     /* --- InputReaderPolicyInterface implementation --- */
 
@@ -190,6 +198,10 @@
     virtual void notifyInputDevicesChanged(const Vector<InputDeviceInfo>& inputDevices);
     virtual sp<KeyCharacterMap> getKeyboardLayoutOverlay(const InputDeviceIdentifier& identifier);
     virtual String8 getDeviceAlias(const InputDeviceIdentifier& identifier);
+    virtual TouchAffineTransformation getTouchAffineTransformation(JNIEnv *env,
+            jfloatArray matrixArr);
+    virtual TouchAffineTransformation getTouchAffineTransformation(
+            const String8& inputDeviceDescriptor, int32_t surfaceRotation);
 
     /* --- InputDispatcherPolicyInterface implementation --- */
 
@@ -741,6 +753,11 @@
             InputReaderConfiguration::CHANGE_SHOW_TOUCHES);
 }
 
+void NativeInputManager::reloadCalibration() {
+    mInputManager->getReader()->requestRefreshConfiguration(
+            InputReaderConfiguration::TOUCH_AFFINE_TRANSFORMATION);
+}
+
 bool NativeInputManager::isScreenOn() {
     return android_server_PowerManagerService_isScreenOn();
 }
@@ -749,6 +766,43 @@
     return android_server_PowerManagerService_isScreenBright();
 }
 
+TouchAffineTransformation NativeInputManager::getTouchAffineTransformation(
+        JNIEnv *env, jfloatArray matrixArr) {
+    ScopedFloatArrayRO matrix(env, matrixArr);
+    assert(matrix.size() == 6);
+
+    TouchAffineTransformation transform;
+    transform.x_scale  = matrix[0];
+    transform.x_ymix   = matrix[1];
+    transform.x_offset = matrix[2];
+    transform.y_xmix   = matrix[3];
+    transform.y_scale  = matrix[4];
+    transform.y_offset = matrix[5];
+
+    return transform;
+}
+
+TouchAffineTransformation NativeInputManager::getTouchAffineTransformation(
+        const String8& inputDeviceDescriptor, int32_t surfaceRotation) {
+    JNIEnv* env = jniEnv();
+
+    ScopedLocalRef<jstring> descriptorObj(env, env->NewStringUTF(inputDeviceDescriptor.string()));
+
+    jobject cal = env->CallObjectMethod(mServiceObj,
+            gServiceClassInfo.getTouchCalibrationForInputDevice, descriptorObj.get(),
+            surfaceRotation);
+
+    jfloatArray matrixArr = jfloatArray(env->CallObjectMethod(cal,
+            gTouchCalibrationClassInfo.getAffineTransform));
+
+    TouchAffineTransformation transform = getTouchAffineTransformation(env, matrixArr);
+
+    env->DeleteLocalRef(matrixArr);
+    env->DeleteLocalRef(cal);
+
+    return transform;
+}
+
 bool NativeInputManager::filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) {
     jobject inputEventObj;
 
@@ -1231,6 +1285,11 @@
     im->setShowTouches(enabled);
 }
 
+static void nativeReloadCalibration(JNIEnv* env, jclass clazz, jlong ptr) {
+    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
+    im->reloadCalibration();
+}
+
 static void nativeVibrate(JNIEnv* env,
         jclass clazz, jlong ptr, jint deviceId, jlongArray patternObj,
         jint repeat, jint token) {
@@ -1336,6 +1395,8 @@
             (void*) nativeSetPointerSpeed },
     { "nativeSetShowTouches", "(JZ)V",
             (void*) nativeSetShowTouches },
+    { "nativeReloadCalibration", "(J)V",
+            (void*) nativeReloadCalibration },
     { "nativeVibrate", "(JI[JII)V",
             (void*) nativeVibrate },
     { "nativeCancelVibrate", "(JII)V",
@@ -1446,6 +1507,10 @@
     GET_METHOD_ID(gServiceClassInfo.getDeviceAlias, clazz,
             "getDeviceAlias", "(Ljava/lang/String;)Ljava/lang/String;");
 
+    GET_METHOD_ID(gServiceClassInfo.getTouchCalibrationForInputDevice, clazz,
+            "getTouchCalibrationForInputDevice",
+            "(Ljava/lang/String;I)Landroid/hardware/input/TouchCalibration;");
+
     // InputDevice
 
     FIND_CLASS(gInputDeviceClassInfo.clazz, "android/view/InputDevice");
@@ -1468,6 +1533,14 @@
     GET_METHOD_ID(gInputDeviceIdentifierInfo.constructor, gInputDeviceIdentifierInfo.clazz,
             "<init>", "(Ljava/lang/String;II)V");
 
+    // TouchCalibration
+
+    FIND_CLASS(gTouchCalibrationClassInfo.clazz, "android/hardware/input/TouchCalibration");
+    gTouchCalibrationClassInfo.clazz = jclass(env->NewGlobalRef(gTouchCalibrationClassInfo.clazz));
+
+    GET_METHOD_ID(gTouchCalibrationClassInfo.getAffineTransform, gTouchCalibrationClassInfo.clazz,
+            "getAffineTransform", "()[F");
+
     return 0;
 }