Merge "MediaCodec: revalidate cached queued input buffers if queue fails" into lmp-dev
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 702ac6b..77981f4 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2723,6 +2723,9 @@
     public static final String
             ACTION_OPEN_DOCUMENT_TREE = "android.intent.action.OPEN_DOCUMENT_TREE";
 
+    /** {@hide} */
+    public static final String ACTION_MASTER_CLEAR = "android.intent.action.MASTER_CLEAR";
+
     // ---------------------------------------------------------------------
     // ---------------------------------------------------------------------
     // Standard intent categories (see addCategory()).
@@ -3425,6 +3428,9 @@
     public static final String EXTRA_TIME_PREF_24_HOUR_FORMAT =
             "android.intent.extra.TIME_PREF_24_HOUR_FORMAT";
 
+    /** {@hide} */
+    public static final String EXTRA_REASON = "android.intent.extra.REASON";
+
     // ---------------------------------------------------------------------
     // ---------------------------------------------------------------------
     // Intent flags (see mFlags variable).
diff --git a/core/java/android/hardware/camera2/legacy/BurstHolder.java b/core/java/android/hardware/camera2/legacy/BurstHolder.java
index c141c51..b9c89f8 100644
--- a/core/java/android/hardware/camera2/legacy/BurstHolder.java
+++ b/core/java/android/hardware/camera2/legacy/BurstHolder.java
@@ -22,6 +22,7 @@
 import android.view.Surface;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 
 /**
@@ -38,14 +39,16 @@
      *
      * @param requestId id of the burst request.
      * @param repeating true if this burst is repeating.
-     * @param requests a {@link java.util.List} of {@link CaptureRequest}s in this burst.
+     * @param requests a {@link List} of {@link CaptureRequest}s in this burst.
+     * @param jpegSurfaceIds a {@link Collection} of IDs for the surfaces that have jpeg outputs.
      */
-    public BurstHolder(int requestId, boolean repeating, List<CaptureRequest> requests) {
-        mRequestBuilders = new ArrayList<RequestHolder.Builder>();
+    public BurstHolder(int requestId, boolean repeating, List<CaptureRequest> requests,
+                       Collection<Long> jpegSurfaceIds) {
+        mRequestBuilders = new ArrayList<>();
         int i = 0;
         for (CaptureRequest r : requests) {
             mRequestBuilders.add(new RequestHolder.Builder(requestId, /*subsequenceId*/i,
-                    /*request*/r, repeating));
+                    /*request*/r, repeating, jpegSurfaceIds));
             ++i;
         }
         mRepeating = repeating;
diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
index a724b41..4587c6f 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
@@ -522,7 +522,7 @@
         return surfaceIds;
     }
 
-    static boolean containsSurfaceId(Surface s, List<Long> ids) {
+    static boolean containsSurfaceId(Surface s, Collection<Long> ids) {
         long id = getSurfaceId(s);
         return ids.contains(id);
     }
diff --git a/core/java/android/hardware/camera2/legacy/RequestHolder.java b/core/java/android/hardware/camera2/legacy/RequestHolder.java
index 69c140b..edd8e4e 100644
--- a/core/java/android/hardware/camera2/legacy/RequestHolder.java
+++ b/core/java/android/hardware/camera2/legacy/RequestHolder.java
@@ -43,71 +43,6 @@
     private volatile boolean mFailed = false;
 
     /**
-     * Returns true if the given surface requires jpeg buffers.
-     *
-     * @param s a {@link android.view.Surface} to check.
-     * @return true if the surface requires a jpeg buffer.
-     */
-    public static boolean jpegType(Surface s)
-            throws LegacyExceptionUtils.BufferQueueAbandonedException {
-        return LegacyCameraDevice.detectSurfaceType(s) ==
-                CameraMetadataNative.NATIVE_JPEG_FORMAT;
-    }
-
-    /**
-     * Returns true if the given surface requires non-jpeg buffer types.
-     *
-     * <p>
-     * "Jpeg buffer" refers to the buffers returned in the jpeg
-     * {@link android.hardware.Camera.PictureCallback}.  Non-jpeg buffers are created using a tee
-     * of the preview stream drawn to the surface
-     * set via {@link android.hardware.Camera#setPreviewDisplay(android.view.SurfaceHolder)} or
-     * equivalent methods.
-     * </p>
-     * @param s a {@link android.view.Surface} to check.
-     * @return true if the surface requires a non-jpeg buffer type.
-     */
-    public static boolean previewType(Surface s)
-            throws LegacyExceptionUtils.BufferQueueAbandonedException {
-        return LegacyCameraDevice.detectSurfaceType(s) !=
-                CameraMetadataNative.NATIVE_JPEG_FORMAT;
-    }
-
-    /**
-     * Returns the number of surfaces targeted by the request that require jpeg buffers.
-     */
-    private static int numJpegTargets(CaptureRequest request) {
-        int count = 0;
-        for (Surface s : request.getTargets()) {
-            try {
-                if (jpegType(s)) {
-                    ++count;
-                }
-            } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
-                Log.w(TAG, "Surface abandoned, skipping...", e);
-            }
-        }
-        return count;
-    }
-
-    /**
-     * Returns the number of surfaces targeted by the request that require non-jpeg buffers.
-     */
-    private static int numPreviewTargets(CaptureRequest request) {
-        int count = 0;
-        for (Surface s : request.getTargets()) {
-            try {
-                if (previewType(s)) {
-                    ++count;
-                }
-            } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
-                Log.w(TAG, "Surface abandoned, skipping...", e);
-            }
-        }
-        return count;
-    }
-
-    /**
      * A builder class for {@link RequestHolder} objects.
      *
      * <p>
@@ -121,6 +56,7 @@
         private final boolean mRepeating;
         private final int mNumJpegTargets;
         private final int mNumPreviewTargets;
+        private final Collection<Long> mJpegSurfaceIds;
 
         /**
          * Construct a new {@link Builder} to generate {@link RequestHolder} objects.
@@ -132,17 +68,81 @@
          * @param repeating {@code true} if the request is repeating.
          */
         public Builder(int requestId, int subsequenceId, CaptureRequest request,
-                       boolean repeating) {
+                       boolean repeating, Collection<Long> jpegSurfaceIds) {
             checkNotNull(request, "request must not be null");
             mRequestId = requestId;
             mSubsequenceId = subsequenceId;
             mRequest = request;
             mRepeating = repeating;
+            mJpegSurfaceIds = jpegSurfaceIds;
             mNumJpegTargets = numJpegTargets(mRequest);
             mNumPreviewTargets = numPreviewTargets(mRequest);
         }
 
         /**
+         * Returns true if the given surface requires jpeg buffers.
+         *
+         * @param s a {@link android.view.Surface} to check.
+         * @return true if the surface requires a jpeg buffer.
+         */
+        private boolean jpegType(Surface s)
+                throws LegacyExceptionUtils.BufferQueueAbandonedException {
+            return LegacyCameraDevice.containsSurfaceId(s, mJpegSurfaceIds);
+        }
+
+        /**
+         * Returns true if the given surface requires non-jpeg buffer types.
+         *
+         * <p>
+         * "Jpeg buffer" refers to the buffers returned in the jpeg
+         * {@link android.hardware.Camera.PictureCallback}.  Non-jpeg buffers are created using a tee
+         * of the preview stream drawn to the surface
+         * set via {@link android.hardware.Camera#setPreviewDisplay(android.view.SurfaceHolder)} or
+         * equivalent methods.
+         * </p>
+         * @param s a {@link android.view.Surface} to check.
+         * @return true if the surface requires a non-jpeg buffer type.
+         */
+        private boolean previewType(Surface s)
+                throws LegacyExceptionUtils.BufferQueueAbandonedException {
+            return !jpegType(s);
+        }
+
+        /**
+         * Returns the number of surfaces targeted by the request that require jpeg buffers.
+         */
+        private int numJpegTargets(CaptureRequest request) {
+            int count = 0;
+            for (Surface s : request.getTargets()) {
+                try {
+                    if (jpegType(s)) {
+                        ++count;
+                    }
+                } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
+                    Log.d(TAG, "Surface abandoned, skipping...", e);
+                }
+            }
+            return count;
+        }
+
+        /**
+         * Returns the number of surfaces targeted by the request that require non-jpeg buffers.
+         */
+        private int numPreviewTargets(CaptureRequest request) {
+            int count = 0;
+            for (Surface s : request.getTargets()) {
+                try {
+                    if (previewType(s)) {
+                        ++count;
+                    }
+                } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
+                    Log.d(TAG, "Surface abandoned, skipping...", e);
+                }
+            }
+            return count;
+        }
+
+        /**
          * Build a new {@link RequestHolder} using with parameters generated from this
          *      {@link Builder}.
          *
diff --git a/core/java/android/hardware/camera2/legacy/RequestQueue.java b/core/java/android/hardware/camera2/legacy/RequestQueue.java
index 7598f99..c995029 100644
--- a/core/java/android/hardware/camera2/legacy/RequestQueue.java
+++ b/core/java/android/hardware/camera2/legacy/RequestQueue.java
@@ -39,8 +39,11 @@
     private long mCurrentFrameNumber = 0;
     private long mCurrentRepeatingFrameNumber = INVALID_FRAME;
     private int mCurrentRequestId = 0;
+    private final List<Long> mJpegSurfaceIds;
 
-    public RequestQueue() {}
+    public RequestQueue(List<Long> jpegSurfaceIds) {
+        mJpegSurfaceIds = jpegSurfaceIds;
+    }
 
     /**
      * Return and remove the next burst on the queue.
@@ -117,7 +120,7 @@
     public synchronized int submit(List<CaptureRequest> requests, boolean repeating,
             /*out*/LongParcelable frameNumber) {
         int requestId = mCurrentRequestId++;
-        BurstHolder burst = new BurstHolder(requestId, repeating, requests);
+        BurstHolder burst = new BurstHolder(requestId, repeating, requests, mJpegSurfaceIds);
         long ret = INVALID_FRAME;
         if (burst.isRepeating()) {
             Log.i(TAG, "Repeating capture request set.");
diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
index 4f8fe0b..a9981d8 100644
--- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
+++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
@@ -91,9 +91,11 @@
     private SurfaceTexture mPreviewTexture;
     private Camera.Parameters mParams;
 
+    private final List<Long> mJpegSurfaceIds = new ArrayList<>();
+
     private Size mIntermediateBufferSize;
 
-    private final RequestQueue mRequestQueue = new RequestQueue();
+    private final RequestQueue mRequestQueue = new RequestQueue(mJpegSurfaceIds);
     private LegacyRequest mLastRequest = null;
     private SurfaceTexture mDummyTexture;
     private Surface mDummySurface;
@@ -102,6 +104,10 @@
     private final FpsCounter mPrevCounter = new FpsCounter("Incoming Preview");
     private final FpsCounter mRequestCounter = new FpsCounter("Incoming Requests");
 
+    // Stuff JPEGs into HAL_PIXEL_FORMAT_RGBA_8888 gralloc buffers to get around SW write
+    // limitations for (b/17379185).
+    private static final boolean USE_BLOB_FORMAT_OVERRIDE = true;
+
     /**
      * Container object for Configure messages.
      */
@@ -197,10 +203,13 @@
             }
             for (Surface s : holder.getHolderTargets()) {
                 try {
-                    if (RequestHolder.jpegType(s)) {
+                    if (LegacyCameraDevice.containsSurfaceId(s, mJpegSurfaceIds)) {
                         Log.i(TAG, "Producing jpeg buffer...");
-                        LegacyCameraDevice.setSurfaceDimens(s, data.length +
-                                LegacyCameraDevice.nativeGetJpegFooterSize(), /*height*/1);
+
+                        int totalSize = data.length + LegacyCameraDevice.nativeGetJpegFooterSize();
+                        totalSize += ((totalSize - 1) & ~0x3) + 4; // align to next octonibble
+
+                        LegacyCameraDevice.setSurfaceDimens(s, totalSize, /*height*/1);
                         LegacyCameraDevice.setNextTimestamp(s, timestamp);
                         LegacyCameraDevice.produceFrame(s, data, data.length, /*height*/1,
                                 CameraMetadataNative.NATIVE_JPEG_FORMAT);
@@ -316,6 +325,7 @@
             mGLThreadManager.ignoreNewFrames();
             mGLThreadManager.waitUntilIdle();
         }
+        resetJpegSurfaceFormats(mCallbackOutputs);
         mPreviewOutputs.clear();
         mCallbackOutputs.clear();
         mPreviewTexture = null;
@@ -329,6 +339,12 @@
                     LegacyCameraDevice.setSurfaceOrientation(s, facing, orientation);
                     switch (format) {
                         case CameraMetadataNative.NATIVE_JPEG_FORMAT:
+                            if (USE_BLOB_FORMAT_OVERRIDE) {
+                                // Override to RGBA_8888 format.
+                                LegacyCameraDevice.setSurfaceFormat(s,
+                                        LegacyMetadataMapper.HAL_PIXEL_FORMAT_RGBA_8888);
+                            }
+                            mJpegSurfaceIds.add(LegacyCameraDevice.getSurfaceId(s));
                             mCallbackOutputs.add(s);
                             break;
                         default:
@@ -426,8 +442,19 @@
         }
 
         mCamera.setParameters(mParams);
-        // TODO: configure the JPEG surface with some arbitrary size
-        // using LegacyCameraDevice.nativeConfigureSurface
+    }
+
+    private void resetJpegSurfaceFormats(Collection<Surface> surfaces) {
+        if (!USE_BLOB_FORMAT_OVERRIDE || surfaces == null) {
+            return;
+        }
+        for(Surface s : surfaces) {
+            try {
+                LegacyCameraDevice.setSurfaceFormat(s, LegacyMetadataMapper.HAL_PIXEL_FORMAT_BLOB);
+            } catch (LegacyExceptionUtils.BufferQueueAbandonedException e) {
+                Log.w(TAG, "Surface abandoned, skipping...", e);
+            }
+        }
     }
 
     /**
@@ -459,9 +486,8 @@
         List<Size> configuredJpegSizes = new ArrayList<Size>();
         for (Surface callbackSurface : callbackOutputs) {
             try {
-                int format = LegacyCameraDevice.detectSurfaceType(callbackSurface);
 
-                if (format != CameraMetadataNative.NATIVE_JPEG_FORMAT) {
+                if (!LegacyCameraDevice.containsSurfaceId(callbackSurface, mJpegSurfaceIds)) {
                     continue; // Ignore non-JPEG callback formats
                 }
 
@@ -821,6 +847,7 @@
                     if (mCamera != null) {
                         mCamera.release();
                     }
+                    resetJpegSurfaceFormats(mCallbackOutputs);
                     break;
                 default:
                     throw new AssertionError("Unhandled message " + msg.what +
diff --git a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
index c018c3e..c0d1d5e 100644
--- a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
+++ b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
@@ -581,6 +581,7 @@
             // If pixel conversions aren't handled by egl, use a pbuffer
             try {
                 if (LegacyCameraDevice.needsConversion(s)) {
+                    // Always override to YV12 output for YUV surface formats.
                     LegacyCameraDevice.setSurfaceFormat(s, ImageFormat.YV12);
                     EGLSurfaceHolder holder = new EGLSurfaceHolder();
                     holder.surface = s;
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 7a46e40..b879c83 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -20,6 +20,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.UserManager;
+import android.text.TextUtils;
 import android.util.Log;
 
 import java.io.ByteArrayInputStream;
@@ -333,9 +334,10 @@
         throws IOException {
         String filename = packageFile.getCanonicalPath();
         Log.w(TAG, "!!! REBOOTING TO INSTALL " + filename + " !!!");
-        String arg = "--update_package=" + filename +
-            "\n--locale=" + Locale.getDefault().toString();
-        bootCommand(context, arg);
+
+        final String filenameArg = "--update_package=" + filename;
+        final String localeArg = "--locale=" + Locale.getDefault().toString();
+        bootCommand(context, filenameArg, localeArg);
     }
 
     /**
@@ -352,7 +354,18 @@
      * @throws SecurityException if the current user is not allowed to wipe data.
      */
     public static void rebootWipeUserData(Context context) throws IOException {
-        rebootWipeUserData(context, false);
+        rebootWipeUserData(context, false, context.getPackageName());
+    }
+
+    /** {@hide} */
+    public static void rebootWipeUserData(Context context, String reason) throws IOException {
+        rebootWipeUserData(context, false, reason);
+    }
+
+    /** {@hide} */
+    public static void rebootWipeUserData(Context context, boolean shutdown)
+            throws IOException {
+        rebootWipeUserData(context, shutdown, context.getPackageName());
     }
 
     /**
@@ -373,8 +386,8 @@
      *
      * @hide
      */
-    public static void rebootWipeUserData(Context context, boolean shutdown)
-        throws IOException {
+    public static void rebootWipeUserData(Context context, boolean shutdown, String reason)
+            throws IOException {
         UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
         if (um.hasUserRestriction(UserManager.DISALLOW_FACTORY_RESET)) {
             throw new SecurityException("Wiping data is not allowed for this user.");
@@ -395,13 +408,18 @@
         // Block until the ordered broadcast has completed.
         condition.block();
 
-        String shutdownArg = "";
+        String shutdownArg = null;
         if (shutdown) {
-            shutdownArg = "--shutdown_after\n";
+            shutdownArg = "--shutdown_after";
         }
 
-        bootCommand(context, shutdownArg + "--wipe_data\n--locale=" +
-                    Locale.getDefault().toString());
+        String reasonArg = null;
+        if (!TextUtils.isEmpty(reason)) {
+            reasonArg = "--reason=" + sanitizeArg(reason);
+        }
+
+        final String localeArg = "--locale=" + Locale.getDefault().toString();
+        bootCommand(context, shutdownArg, "--wipe_data", reasonArg, localeArg);
     }
 
     /**
@@ -409,23 +427,38 @@
      * @throws IOException if something goes wrong.
      */
     public static void rebootWipeCache(Context context) throws IOException {
-        bootCommand(context, "--wipe_cache\n--locale=" + Locale.getDefault().toString());
+        rebootWipeCache(context, context.getPackageName());
+    }
+
+    /** {@hide} */
+    public static void rebootWipeCache(Context context, String reason) throws IOException {
+        String reasonArg = null;
+        if (!TextUtils.isEmpty(reason)) {
+            reasonArg = "--reason=" + sanitizeArg(reason);
+        }
+
+        final String localeArg = "--locale=" + Locale.getDefault().toString();
+        bootCommand(context, "--wipe_cache", reasonArg, localeArg);
     }
 
     /**
      * Reboot into the recovery system with the supplied argument.
-     * @param arg to pass to the recovery utility.
+     * @param args to pass to the recovery utility.
      * @throws IOException if something goes wrong.
      */
-    private static void bootCommand(Context context, String arg) throws IOException {
+    private static void bootCommand(Context context, String... args) throws IOException {
         RECOVERY_DIR.mkdirs();  // In case we need it
         COMMAND_FILE.delete();  // In case it's not writable
         LOG_FILE.delete();
 
         FileWriter command = new FileWriter(COMMAND_FILE);
         try {
-            command.write(arg);
-            command.write("\n");
+            for (String arg : args) {
+                if (!TextUtils.isEmpty(arg)) {
+                    command.write(arg);
+                    command.write("\n");
+                }
+            }
         } finally {
             command.close();
         }
@@ -470,5 +503,15 @@
         return log;
     }
 
+    /**
+     * Internally, recovery treats each line of the command file as a separate
+     * argv, so we only need to protect against newlines and nulls.
+     */
+    private static String sanitizeArg(String arg) {
+        arg = arg.replace('\0', '?');
+        arg = arg.replace('\n', '?');
+        return arg;
+    }
+
     private void RecoverySystem() { }  // Do not instantiate
 }
diff --git a/core/java/com/android/internal/os/storage/ExternalStorageFormatter.java b/core/java/com/android/internal/os/storage/ExternalStorageFormatter.java
index fb7f215..a529923 100644
--- a/core/java/com/android/internal/os/storage/ExternalStorageFormatter.java
+++ b/core/java/com/android/internal/os/storage/ExternalStorageFormatter.java
@@ -50,6 +50,7 @@
 
     private boolean mFactoryReset = false;
     private boolean mAlwaysReset = false;
+    private String mReason = null;
 
     StorageEventListener mStorageListener = new StorageEventListener() {
         @Override
@@ -84,6 +85,7 @@
             mAlwaysReset = true;
         }
 
+        mReason = intent.getStringExtra(Intent.EXTRA_REASON);
         mStorageVolume = intent.getParcelableExtra(StorageVolume.EXTRA_STORAGE_VOLUME);
 
         if (mProgressDialog == null) {
@@ -135,7 +137,10 @@
     void fail(int msg) {
         Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
         if (mAlwaysReset) {
-            sendBroadcast(new Intent("android.intent.action.MASTER_CLEAR"));
+            Intent intent = new Intent(Intent.ACTION_MASTER_CLEAR);
+            intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+            intent.putExtra(Intent.EXTRA_REASON, mReason);
+            sendBroadcast(intent);
         }
         stopSelf();
     }
@@ -179,7 +184,10 @@
                         }
                         if (success) {
                             if (mFactoryReset) {
-                                sendBroadcast(new Intent("android.intent.action.MASTER_CLEAR"));
+                                Intent intent = new Intent(Intent.ACTION_MASTER_CLEAR);
+                                intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+                                intent.putExtra(Intent.EXTRA_REASON, mReason);
+                                sendBroadcast(intent);
                                 // Intent handling is asynchronous -- assume it will happen soon.
                                 stopSelf();
                                 return;
@@ -188,7 +196,10 @@
                         // If we didn't succeed, or aren't doing a full factory
                         // reset, then it is time to remount the storage.
                         if (!success && mAlwaysReset) {
-                            sendBroadcast(new Intent("android.intent.action.MASTER_CLEAR"));
+                            Intent intent = new Intent(Intent.ACTION_MASTER_CLEAR);
+                            intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+                            intent.putExtra(Intent.EXTRA_REASON, mReason);
+                            sendBroadcast(intent);
                         } else {
                             try {
                                 mountService.mountVolume(extStoragePath);
diff --git a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
index 980ead0..ee00161 100644
--- a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
+++ b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
@@ -200,7 +200,7 @@
     switch(pixelFmt) {
         case HAL_PIXEL_FORMAT_YCrCb_420_SP: {
             if (bufSize < width * height * 4) {
-                ALOGE("%s: PixelBuffer size %" PRId32 " to small for given dimensions",
+                ALOGE("%s: PixelBuffer size %" PRId32 " too small for given dimensions",
                         __FUNCTION__, bufSize);
                 return BAD_VALUE;
             }
@@ -222,7 +222,7 @@
         }
         case HAL_PIXEL_FORMAT_YV12: {
             if (bufSize < width * height * 4) {
-                ALOGE("%s: PixelBuffer size %" PRId32 " to small for given dimensions",
+                ALOGE("%s: PixelBuffer size %" PRId32 " too small for given dimensions",
                         __FUNCTION__, bufSize);
                 return BAD_VALUE;
             }
@@ -259,7 +259,7 @@
             // Software writes with YCbCr_420_888 format are unsupported
             // by the gralloc module for now
             if (bufSize < width * height * 4) {
-                ALOGE("%s: PixelBuffer size %" PRId32 " to small for given dimensions",
+                ALOGE("%s: PixelBuffer size %" PRId32 " too small for given dimensions",
                         __FUNCTION__, bufSize);
                 return BAD_VALUE;
             }
@@ -281,6 +281,18 @@
                 return BAD_VALUE;
             }
             int8_t* img = NULL;
+            struct camera3_jpeg_blob footer = {
+                jpeg_blob_id: CAMERA3_JPEG_BLOB_ID,
+                jpeg_size: (uint32_t)width
+            };
+
+            size_t totalSize = static_cast<size_t>(width) + sizeof(footer);
+            size_t padding = ((totalSize - 1) & ~0x3) + 4; // align to next octonibble
+            totalSize += padding;
+            if (anb->width != totalSize) {
+                ALOGE("%s: gralloc buffer wrong size to hold jpeg, failed to produce buffer.");
+                return BAD_VALUE;
+            }
 
             ALOGV("%s: Lock buffer from %p for write", __FUNCTION__, anw.get());
             err = buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
@@ -289,12 +301,9 @@
                         err);
                 return err;
             }
-            struct camera3_jpeg_blob footer = {
-                jpeg_blob_id: CAMERA3_JPEG_BLOB_ID,
-                jpeg_size: (uint32_t)width
-            };
             memcpy(img, pixelBuffer, width);
-            memcpy(img + anb->width - sizeof(footer), &footer, sizeof(footer));
+            memset(img + width, 0, padding);
+            memcpy(img + totalSize - sizeof(footer), &footer, sizeof(footer));
             break;
         }
         default: {
diff --git a/docs/html/app.yaml b/docs/html/app.yaml
index 9ffb775..520ee99 100644
--- a/docs/html/app.yaml
+++ b/docs/html/app.yaml
@@ -1,38 +1,25 @@
 application: androidappdocs-staging
 version: 1
-runtime: python
+runtime: python27
+threadsafe: true
 api_version: 1
+# compatibility: gae-1.9.4+
 
-# This file defines two mutually exclusive 
-# hander blocks:
-# - a handler for use on a local dev_appserver
-#   during development or non-production doc build
-# - a handler for use on a production gae 
-#   instance. This handler requires that the
-#   docs files in the app have been compressed 
-#   with divide_and_compress.py and that main.py
-#   and gae_shell/ are present.
-#
-# Only one of the handler blocks should be
-# uncommented at any given time. By default,
-# the development handler is exposed. 
+# handler for local staging servers
+# WARNING: NOT FOR USE IN PRODUCTION
+
+# Use this handler definition with
+# Google App Engine 1.9.4 or higher.
+# NOT compatible with prior GAE versions
 
 handlers:
 
 # DEVELOPMENT HANDLER
-# (this handler block *must* be commented
-# out before pushing to a production server)
-- url: /
-  static_dir: /
+- url: /(.+)
+  static_files: \1
+  upload: (.+)
 
-# PRODUCTION GAE HANDLER
-#- url: /gae_shell/static
-#  static_dir: gae_shell/static
-#  expiration: 1d
-#
-#- url: /gae_shell/.*
-#  script: /gae_shell/shell.py
-#  login: admin
-#
-#- url: .*
-#  script: main.py
+# Direct default, blank requests
+- url: /
+  static_files: index.html
+  upload: index.html
\ No newline at end of file
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index 49e8b9d..df1b126 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -137,8 +137,7 @@
     private boolean mMutated;
 
     public AnimatedVectorDrawable() {
-        mAnimatedVectorState = new AnimatedVectorDrawableState(
-                new AnimatedVectorDrawableState(null));
+        mAnimatedVectorState = new AnimatedVectorDrawableState(null);
     }
 
     private AnimatedVectorDrawable(AnimatedVectorDrawableState state, Resources res,
@@ -160,10 +159,16 @@
 
     @Override
     public ConstantState getConstantState() {
+        mAnimatedVectorState.mChangingConfigurations = getChangingConfigurations();
         return mAnimatedVectorState;
     }
 
     @Override
+    public int getChangingConfigurations() {
+        return super.getChangingConfigurations() | mAnimatedVectorState.mChangingConfigurations;
+    }
+
+    @Override
     public void draw(Canvas canvas) {
         mAnimatedVectorState.mVectorDrawable.draw(canvas);
         if (isStarted()) {
@@ -347,6 +352,8 @@
                         mTargetNameMap.put(animClone, targetName);
                     }
                 }
+            } else {
+                mVectorDrawable = new VectorDrawable();
             }
         }
 
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index b786f94..b541454 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -642,7 +642,7 @@
         private void createSurfacePlanes() {
             mPlanes = new SurfacePlane[ImageReader.this.mNumPlanes];
             for (int i = 0; i < ImageReader.this.mNumPlanes; i++) {
-                mPlanes[i] = nativeCreatePlane(i);
+                mPlanes[i] = nativeCreatePlane(i, ImageReader.this.mFormat);
             }
         }
         private class SurfacePlane extends android.media.Image.Plane {
@@ -661,7 +661,8 @@
                 if (mBuffer != null) {
                     return mBuffer;
                 } else {
-                    mBuffer = SurfaceImage.this.nativeImageGetBuffer(mIndex);
+                    mBuffer = SurfaceImage.this.nativeImageGetBuffer(mIndex,
+                            ImageReader.this.mFormat);
                     // Set the byteBuffer order according to host endianness (native order),
                     // otherwise, the byteBuffer order defaults to ByteOrder.BIG_ENDIAN.
                     return mBuffer.order(ByteOrder.nativeOrder());
@@ -711,8 +712,8 @@
         private SurfacePlane[] mPlanes;
         private boolean mIsImageValid;
 
-        private synchronized native ByteBuffer nativeImageGetBuffer(int idx);
-        private synchronized native SurfacePlane nativeCreatePlane(int idx);
+        private synchronized native ByteBuffer nativeImageGetBuffer(int idx, int readerFormat);
+        private synchronized native SurfacePlane nativeCreatePlane(int idx, int readerFormat);
     }
 
     private synchronized native void nativeInit(Object weakSelf, int w, int h,
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index fa4439d..a7347745 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -317,8 +317,18 @@
     return size;
 }
 
+static int32_t applyFormatOverrides(int32_t bufferFormat, int32_t readerCtxFormat)
+{
+    // Using HAL_PIXEL_FORMAT_RGBA_8888 gralloc buffers containing JPEGs to get around SW
+    // write limitations for some platforms (b/17379185).
+    if (readerCtxFormat == HAL_PIXEL_FORMAT_BLOB && bufferFormat == HAL_PIXEL_FORMAT_RGBA_8888) {
+        return HAL_PIXEL_FORMAT_BLOB;
+    }
+    return bufferFormat;
+}
+
 static void Image_getLockedBufferInfo(JNIEnv* env, CpuConsumer::LockedBuffer* buffer, int idx,
-                                uint8_t **base, uint32_t *size)
+                                uint8_t **base, uint32_t *size, int32_t readerFormat)
 {
     ALOG_ASSERT(buffer != NULL, "Input buffer is NULL!!!");
     ALOG_ASSERT(base != NULL, "base is NULL!!!");
@@ -334,6 +344,8 @@
 
     dataSize = ySize = cSize = cStride = 0;
     int32_t fmt = buffer->format;
+
+    fmt = applyFormatOverrides(fmt, readerFormat);
     switch (fmt) {
         case HAL_PIXEL_FORMAT_YCbCr_420_888:
             pData =
@@ -458,7 +470,8 @@
     *size = dataSize;
 }
 
-static jint Image_imageGetPixelStride(JNIEnv* env, CpuConsumer::LockedBuffer* buffer, int idx)
+static jint Image_imageGetPixelStride(JNIEnv* env, CpuConsumer::LockedBuffer* buffer, int idx,
+        int32_t readerFormat)
 {
     ALOGV("%s: buffer index: %d", __FUNCTION__, idx);
     ALOG_ASSERT((idx < IMAGE_READER_MAX_NUM_PLANES) && (idx >= 0), "Index is out of range:%d", idx);
@@ -467,6 +480,9 @@
     ALOG_ASSERT(buffer != NULL, "buffer is NULL");
 
     int32_t fmt = buffer->format;
+
+    fmt = applyFormatOverrides(fmt, readerFormat);
+
     switch (fmt) {
         case HAL_PIXEL_FORMAT_YCbCr_420_888:
             pixelStride = (idx == 0) ? 1 : buffer->chromaStep;
@@ -515,7 +531,8 @@
     return pixelStride;
 }
 
-static jint Image_imageGetRowStride(JNIEnv* env, CpuConsumer::LockedBuffer* buffer, int idx)
+static jint Image_imageGetRowStride(JNIEnv* env, CpuConsumer::LockedBuffer* buffer, int idx,
+        int32_t readerFormat)
 {
     ALOGV("%s: buffer index: %d", __FUNCTION__, idx);
     ALOG_ASSERT((idx < IMAGE_READER_MAX_NUM_PLANES) && (idx >= 0));
@@ -525,6 +542,8 @@
 
     int32_t fmt = buffer->format;
 
+    fmt = applyFormatOverrides(fmt, readerFormat);
+
     switch (fmt) {
         case HAL_PIXEL_FORMAT_YCbCr_420_888:
             rowStride = (idx == 0) ? buffer->stride : buffer->chromaStride;
@@ -777,9 +796,10 @@
         outputHeight = buffer->crop.getHeight();
     }
 
+    int imgReaderFmt = ctx->getBufferFormat();
     int imageReaderWidth = ctx->getBufferWidth();
     int imageReaderHeight = ctx->getBufferHeight();
-    if ((buffer->format != HAL_PIXEL_FORMAT_BLOB) &&
+    if ((buffer->format != HAL_PIXEL_FORMAT_BLOB) && (imgReaderFmt != HAL_PIXEL_FORMAT_BLOB) &&
             (imageReaderWidth != outputWidth || imageReaderHeight > outputHeight)) {
         /**
          * For video decoder, the buffer height is actually the vertical stride,
@@ -795,15 +815,19 @@
         return -1;
     }
 
-    int imgReaderFmt = ctx->getBufferFormat();
     int bufFmt = buffer->format;
     if (imgReaderFmt != bufFmt) {
-        // Special casing for when producer switches to a format compatible with flexible YUV
-        // (HAL_PIXEL_FORMAT_YCbCr_420_888).
+
         if (imgReaderFmt == HAL_PIXEL_FORMAT_YCbCr_420_888 && (bufFmt ==
                 HAL_PIXEL_FORMAT_YCrCb_420_SP || bufFmt == HAL_PIXEL_FORMAT_YV12)) {
+            // Special casing for when producer switches to a format compatible with flexible YUV
+            // (HAL_PIXEL_FORMAT_YCbCr_420_888).
             ctx->setBufferFormat(bufFmt);
-            ALOGV("%s: Overriding buffer format YUV_420_888 to %x.", __FUNCTION__, bufFmt);
+            ALOGD("%s: Overriding buffer format YUV_420_888 to %x.", __FUNCTION__, bufFmt);
+        } else if (imgReaderFmt == HAL_PIXEL_FORMAT_BLOB && bufFmt == HAL_PIXEL_FORMAT_RGBA_8888) {
+            // Using HAL_PIXEL_FORMAT_RGBA_8888 gralloc buffers containing JPEGs to get around SW
+            // write limitations for (b/17379185).
+            ALOGD("%s: Receiving JPEG in HAL_PIXEL_FORMAT_RGBA_8888 buffer.", __FUNCTION__);
         } else {
             // Return the buffer to the queue.
             consumer->unlockBuffer(*buffer);
@@ -843,7 +867,7 @@
     return android_view_Surface_createFromIGraphicBufferProducer(env, gbp);
 }
 
-static jobject Image_createSurfacePlane(JNIEnv* env, jobject thiz, int idx)
+static jobject Image_createSurfacePlane(JNIEnv* env, jobject thiz, int idx, int readerFormat)
 {
     int rowStride, pixelStride;
     ALOGV("%s: buffer index: %d", __FUNCTION__, idx);
@@ -854,8 +878,11 @@
     if (buffer == NULL) {
         jniThrowException(env, "java/lang/IllegalStateException", "Image was released");
     }
-    rowStride = Image_imageGetRowStride(env, buffer, idx);
-    pixelStride = Image_imageGetPixelStride(env, buffer, idx);
+
+    readerFormat = Image_getPixelFormat(env, readerFormat);
+
+    rowStride = Image_imageGetRowStride(env, buffer, idx, readerFormat);
+    pixelStride = Image_imageGetPixelStride(env, buffer, idx, readerFormat);
 
     jobject surfPlaneObj = env->NewObject(gSurfacePlaneClassInfo.clazz,
             gSurfacePlaneClassInfo.ctor, thiz, idx, rowStride, pixelStride);
@@ -863,7 +890,7 @@
     return surfPlaneObj;
 }
 
-static jobject Image_getByteBuffer(JNIEnv* env, jobject thiz, int idx)
+static jobject Image_getByteBuffer(JNIEnv* env, jobject thiz, int idx, int readerFormat)
 {
     uint8_t *base = NULL;
     uint32_t size = 0;
@@ -877,8 +904,10 @@
         jniThrowException(env, "java/lang/IllegalStateException", "Image was released");
     }
 
+    readerFormat = Image_getPixelFormat(env, readerFormat);
+
     // Create byteBuffer from native buffer
-    Image_getLockedBufferInfo(env, buffer, idx, &base, &size);
+    Image_getLockedBufferInfo(env, buffer, idx, &base, &size, readerFormat);
 
     if (size > static_cast<uint32_t>(INT32_MAX)) {
         // Byte buffer have 'int capacity', so check the range
@@ -910,8 +939,8 @@
 };
 
 static JNINativeMethod gImageMethods[] = {
-    {"nativeImageGetBuffer",   "(I)Ljava/nio/ByteBuffer;",   (void*)Image_getByteBuffer },
-    {"nativeCreatePlane",      "(I)Landroid/media/ImageReader$SurfaceImage$SurfacePlane;",
+    {"nativeImageGetBuffer",   "(II)Ljava/nio/ByteBuffer;",   (void*)Image_getByteBuffer },
+    {"nativeCreatePlane",      "(II)Landroid/media/ImageReader$SurfaceImage$SurfacePlane;",
                                                              (void*)Image_createSurfacePlane },
 };
 
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 30ccd2c..ba5d11d 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -56,6 +56,7 @@
 import android.os.UserManager;
 import android.provider.MediaStore;
 import android.provider.Settings;
+import android.provider.Settings.Secure;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.LruCache;
@@ -689,19 +690,30 @@
         // Get methods
         if (Settings.CALL_METHOD_GET_SYSTEM.equals(method)) {
             if (LOCAL_LOGV) Slog.v(TAG, "call(system:" + request + ") for " + callingUser);
-            if (isManagedProfile(callingUser) && sSystemCloneToManagedKeys.contains(request)) {
-                callingUser = UserHandle.USER_OWNER;
+            // Check if this request should be (re)directed to the primary user's db
+            if (callingUser == UserHandle.USER_OWNER
+                    || shouldShadowParentProfile(callingUser, sSystemCloneToManagedKeys, request)) {
+                dbHelper = getOrEstablishDatabase(UserHandle.USER_OWNER);
+            } else {
+                dbHelper = getOrEstablishDatabase(callingUser);
             }
-            dbHelper = getOrEstablishDatabase(callingUser);
             cache = sSystemCaches.get(callingUser);
             return lookupValue(dbHelper, TABLE_SYSTEM, cache, request);
         }
         if (Settings.CALL_METHOD_GET_SECURE.equals(method)) {
             if (LOCAL_LOGV) Slog.v(TAG, "call(secure:" + request + ") for " + callingUser);
-            if (isManagedProfile(callingUser) && sSecureCloneToManagedKeys.contains(request)) {
-                callingUser = UserHandle.USER_OWNER;
+            // Check if this is a setting to be copied from the primary user
+            if (shouldShadowParentProfile(callingUser, sSecureCloneToManagedKeys, request)) {
+                // If the request if for location providers and there's a restriction, return none
+                if (Secure.LOCATION_PROVIDERS_ALLOWED.equals(request)
+                        && mUserManager.hasUserRestriction(
+                                UserManager.DISALLOW_SHARE_LOCATION, new UserHandle(callingUser))) {
+                    return sSecureCaches.get(callingUser).putIfAbsent(request, "");
+                }
+                dbHelper = getOrEstablishDatabase(UserHandle.USER_OWNER);
+            } else {
+                dbHelper = getOrEstablishDatabase(callingUser);
             }
-            dbHelper = getOrEstablishDatabase(callingUser);
             cache = sSecureCaches.get(callingUser);
             return lookupValue(dbHelper, TABLE_SECURE, cache, request);
         }
@@ -742,11 +754,10 @@
                         + callingUser);
             }
             // Extra check for USER_OWNER to optimize for the 99%
-            if (callingUser != UserHandle.USER_OWNER && isManagedProfile(callingUser)) {
-                if (sSystemCloneToManagedKeys.contains(request)) {
-                    // Don't write these settings
-                    return null;
-                }
+            if (callingUser != UserHandle.USER_OWNER && shouldShadowParentProfile(callingUser,
+                    sSystemCloneToManagedKeys, request)) {
+                // Don't write these settings, as they are cloned from the parent profile
+                return null;
             }
             insertForUser(Settings.System.CONTENT_URI, values, callingUser);
             // Clone the settings to the managed profiles so that notifications can be sent out
@@ -772,11 +783,10 @@
                         + callingUser);
             }
             // Extra check for USER_OWNER to optimize for the 99%
-            if (callingUser != UserHandle.USER_OWNER && isManagedProfile(callingUser)) {
-                if (sSecureCloneToManagedKeys.contains(request)) {
-                    // Don't write these settings
-                    return null;
-                }
+            if (callingUser != UserHandle.USER_OWNER && shouldShadowParentProfile(callingUser,
+                    sSecureCloneToManagedKeys, request)) {
+                // Don't write these settings, as they are cloned from the parent profile
+                return null;
             }
             insertForUser(Settings.Secure.CONTENT_URI, values, callingUser);
             // Clone the settings to the managed profiles so that notifications can be sent out
@@ -816,6 +826,14 @@
         return null;
     }
 
+    /**
+     * Check if the user is a managed profile and name is one of the settings to be cloned
+     * from the parent profile.
+     */
+    private boolean shouldShadowParentProfile(int userId, HashSet<String> keys, String name) {
+        return isManagedProfile(userId) && keys.contains(name);
+    }
+
     // Looks up value 'key' in 'table' and returns either a single-pair Bundle,
     // possibly with a null value, or null on failure.
     private Bundle lookupValue(DatabaseHelper dbHelper, String table,
diff --git a/services/core/java/com/android/server/MasterClearReceiver.java b/services/core/java/com/android/server/MasterClearReceiver.java
index e88bdf8..f1d5aa3 100644
--- a/services/core/java/com/android/server/MasterClearReceiver.java
+++ b/services/core/java/com/android/server/MasterClearReceiver.java
@@ -38,6 +38,7 @@
         }
 
         final boolean shutdown = intent.getBooleanExtra("shutdown", false);
+        final String reason = intent.getStringExtra(Intent.EXTRA_REASON);
 
         Slog.w(TAG, "!!! FACTORY RESET !!!");
         // The reboot call is blocking, so we need to do it on another thread.
@@ -45,7 +46,7 @@
             @Override
             public void run() {
                 try {
-                    RecoverySystem.rebootWipeUserData(context, shutdown);
+                    RecoverySystem.rebootWipeUserData(context, shutdown, reason);
                     Log.wtf(TAG, "Still running after master clear?!");
                 } catch (IOException e) {
                     Slog.e(TAG, "Can't perform master clear/factory reset", e);
diff --git a/services/core/java/com/android/server/media/MediaSessionStack.java b/services/core/java/com/android/server/media/MediaSessionStack.java
index a9bc818..e464be7 100644
--- a/services/core/java/com/android/server/media/MediaSessionStack.java
+++ b/services/core/java/com/android/server/media/MediaSessionStack.java
@@ -94,8 +94,8 @@
             mSessions.add(0, record);
             clearCache();
             return true;
-        } else if (newState == PlaybackState.STATE_PAUSED) {
-            // Just clear the volume cache in this case
+        } else if (!MediaSession.isActiveState(newState)) {
+            // Just clear the volume cache when a state goes inactive
             mCachedVolumeDefault = null;
         }
         return false;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 59d3dc8..d1aba3c 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -2862,7 +2862,7 @@
         return false;
     }
 
-    void wipeDataLocked(int flags) {
+    void wipeDataLocked(int flags, String reason) {
         // If the SD card is encrypted and non-removable, we have to force a wipe.
         boolean forceExtWipe = !Environment.isExternalStorageRemovable() && isExtStorageEncrypted();
         boolean wipeExtRequested = (flags&DevicePolicyManager.WIPE_EXTERNAL_STORAGE) != 0;
@@ -2871,12 +2871,13 @@
         if ((forceExtWipe || wipeExtRequested) && !Environment.isExternalStorageEmulated()) {
             Intent intent = new Intent(ExternalStorageFormatter.FORMAT_AND_FACTORY_RESET);
             intent.putExtra(ExternalStorageFormatter.EXTRA_ALWAYS_RESET, true);
+            intent.putExtra(Intent.EXTRA_REASON, reason);
             intent.setComponent(ExternalStorageFormatter.COMPONENT_NAME);
             mWakeLock.acquire(10000);
             mContext.startService(intent);
         } else {
             try {
-                RecoverySystem.rebootWipeUserData(mContext);
+                RecoverySystem.rebootWipeUserData(mContext, reason);
             } catch (IOException e) {
                 Slog.w(LOG_TAG, "Failed requesting data wipe", e);
             } catch (SecurityException e) {
@@ -2885,6 +2886,7 @@
         }
     }
 
+    @Override
     public void wipeData(int flags, final int userHandle) {
         if (!mHasFeature) {
             return;
@@ -2896,20 +2898,34 @@
         synchronized (this) {
             // This API can only be called by an active device admin,
             // so try to retrieve it to check that the caller is one.
-            getActiveAdminForCallerLocked(null,
+            final ActiveAdmin admin = getActiveAdminForCallerLocked(null,
                     DeviceAdminInfo.USES_POLICY_WIPE_DATA);
+
+            final String source;
+            if (admin != null && admin.info != null) {
+                final ComponentName cname = admin.info.getComponent();
+                if (cname != null) {
+                    source = cname.flattenToShortString();
+                } else {
+                    source = admin.info.getPackageName();
+                }
+            } else {
+                source = "?";
+            }
+
             long ident = Binder.clearCallingIdentity();
             try {
-                wipeDeviceOrUserLocked(flags, userHandle);
+                wipeDeviceOrUserLocked(flags, userHandle,
+                        "DevicePolicyManager.wipeData() from " + source);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
         }
     }
 
-    private void wipeDeviceOrUserLocked(int flags, final int userHandle) {
+    private void wipeDeviceOrUserLocked(int flags, final int userHandle, String reason) {
         if (userHandle == UserHandle.USER_OWNER) {
-            wipeDataLocked(flags);
+            wipeDataLocked(flags, reason);
         } else {
             mHandler.post(new Runnable() {
                 public void run() {
@@ -3061,7 +3077,7 @@
             }
             if (wipeData) {
                 // Call without holding lock.
-                wipeDeviceOrUserLocked(0, identifier);
+                wipeDeviceOrUserLocked(0, identifier, "reportFailedPasswordAttempt()");
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
diff --git a/tests/VectorDrawableTest/AndroidManifest.xml b/tests/VectorDrawableTest/AndroidManifest.xml
index 4c835ec..7796953 100644
--- a/tests/VectorDrawableTest/AndroidManifest.xml
+++ b/tests/VectorDrawableTest/AndroidManifest.xml
@@ -99,6 +99,15 @@
             </intent-filter>
         </activity>
         <activity
+            android:name="AnimatedVectorDrawableDupPerf"
+            android:label="Animated Vector Performance of clones" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="com.android.test.dynamic.TEST" />
+            </intent-filter>
+        </activity>
+        <activity
             android:name="VectorDrawableStaticPerf"
             android:label="System icons" >
             <intent-filter>
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable24.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable24.xml
index 5c1ccaa..f0b4699 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable24.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable24.xml
@@ -19,8 +19,7 @@
         android:viewportHeight="400"
         android:viewportWidth="400" >
 
-    <group android:name="backgroundGroup"
-        android:alpha = "0.5" >
+    <group android:name="backgroundGroup">
         <path
             android:name="background1"
             android:fillColor="#FF000000"
@@ -33,8 +32,7 @@
     <group
         android:name="translateToCenterGroup"
         android:translateX="50.0"
-        android:translateY="90.0"
-        android:alpha = "0.5" >
+        android:translateY="90.0" >
         <path
             android:name="twoLines"
             android:pathData="@string/twoLinePathData"
@@ -45,8 +43,7 @@
             android:name="rotationGroup"
             android:pivotX="0.0"
             android:pivotY="0.0"
-            android:rotation="-45.0"
-            android:alpha = "0.5" >
+            android:rotation="-45.0">
             <path
                 android:name="twoLines1"
                 android:pathData="@string/twoLinePathData"
@@ -56,8 +53,7 @@
             <group
                 android:name="translateGroup"
                 android:translateX="130.0"
-                android:translateY="160.0"
-                android:alpha = "0.5">
+                android:translateY="160.0">
                 <group android:name="scaleGroup" >
                     <path
                         android:name="twoLines3"
@@ -70,8 +66,7 @@
             <group
                 android:name="translateGroupHalf"
                 android:translateX="65.0"
-                android:translateY="80.0"
-                android:alpha = "0.5">
+                android:translateY="80.0">
                 <group android:name="scaleGroup" >
                     <path
                         android:name="twoLines2"
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable25.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable25.xml
index 069a531..f46d14e 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable25.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable25.xml
@@ -21,12 +21,10 @@
 
     <group
         android:name="FirstLevelGroup"
-        android:alpha="0.9"
         android:translateX="100.0"
         android:translateY="0.0" >
         <group
             android:name="SecondLevelGroup1"
-            android:alpha="0.9"
             android:translateX="-100.0"
             android:translateY="50.0" >
             <path
@@ -35,7 +33,6 @@
 
             <group
                 android:name="ThridLevelGroup1"
-                android:alpha="0.9"
                 android:translateX="-100.0"
                 android:translateY="50.0" >
                 <path
@@ -44,7 +41,6 @@
             </group>
             <group
                 android:name="ThridLevelGroup2"
-                android:alpha="0.8"
                 android:translateX="100.0"
                 android:translateY="50.0" >
                 <path
@@ -54,7 +50,6 @@
         </group>
         <group
             android:name="SecondLevelGroup2"
-            android:alpha="0.8"
             android:translateX="100.0"
             android:translateY="50.0" >
             <path
@@ -63,7 +58,6 @@
 
             <group
                 android:name="ThridLevelGroup3"
-                android:alpha="0.9"
                 android:translateX="-100.0"
                 android:translateY="50.0" >
                 <path
@@ -72,7 +66,6 @@
             </group>
             <group
                 android:name="ThridLevelGroup4"
-                android:alpha="0.8"
                 android:translateX="100.0"
                 android:translateY="50.0" >
                 <path
diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableDupPerf.java b/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableDupPerf.java
new file mode 100644
index 0000000..047e494
--- /dev/null
+++ b/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableDupPerf.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ * or implied. See the License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.android.test.dynamic;
+
+import android.app.Activity;
+import android.content.res.Resources;
+import android.graphics.drawable.AnimatedVectorDrawable;
+import android.graphics.drawable.VectorDrawable;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Xml;
+import android.widget.Button;
+import android.widget.GridLayout;
+import android.widget.ScrollView;
+import android.widget.TextView;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.text.DecimalFormat;
+
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class AnimatedVectorDrawableDupPerf extends Activity {
+
+    private static final String LOGTAG = "AnimatedVectorDrawableDupPerf";
+    protected int[] icon = {
+            R.drawable.animation_vector_linear_progress_bar,
+            R.drawable.animation_vector_linear_progress_bar,
+            R.drawable.animation_vector_linear_progress_bar,
+            R.drawable.animation_vector_linear_progress_bar,
+            R.drawable.animation_vector_linear_progress_bar,
+            R.drawable.animation_vector_linear_progress_bar,
+            R.drawable.animation_vector_linear_progress_bar,
+            R.drawable.animation_vector_linear_progress_bar,
+            R.drawable.animation_vector_linear_progress_bar,
+            R.drawable.animation_vector_linear_progress_bar,
+            R.drawable.animation_vector_linear_progress_bar,
+            R.drawable.animation_vector_linear_progress_bar,
+            R.drawable.animation_vector_linear_progress_bar,
+            R.drawable.animation_vector_linear_progress_bar,
+            R.drawable.animation_vector_linear_progress_bar,
+            R.drawable.animation_vector_linear_progress_bar,
+            R.drawable.animation_vector_linear_progress_bar,
+            R.drawable.animation_vector_linear_progress_bar,
+            R.drawable.animation_vector_linear_progress_bar,
+            R.drawable.animation_vector_linear_progress_bar,
+   };
+
+    /** @hide */
+    public static AnimatedVectorDrawable create(Resources resources, int rid) {
+        try {
+            final XmlPullParser parser = resources.getXml(rid);
+            final AttributeSet attrs = Xml.asAttributeSet(parser);
+            int type;
+            while ((type=parser.next()) != XmlPullParser.START_TAG &&
+                    type != XmlPullParser.END_DOCUMENT) {
+                // Empty loop
+            }
+            if (type != XmlPullParser.START_TAG) {
+                throw new XmlPullParserException("No start tag found");
+            }
+
+            final AnimatedVectorDrawable drawable = new AnimatedVectorDrawable();
+            drawable.inflate(resources, parser, attrs);
+
+            return drawable;
+        } catch (XmlPullParserException e) {
+            Log.e(LOGTAG, "parser error", e);
+        } catch (IOException e) {
+            Log.e(LOGTAG, "parser error", e);
+        }
+        return null;
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        ScrollView scrollView = new ScrollView(this);
+        GridLayout container = new GridLayout(this);
+        scrollView.addView(container);
+        container.setColumnCount(5);
+        Resources res = this.getResources();
+        container.setBackgroundColor(0xFF888888);
+        AnimatedVectorDrawable []d = new AnimatedVectorDrawable[icon.length];
+        long time =  android.os.SystemClock.elapsedRealtimeNanos();
+        for (int i = 0; i < icon.length; i++) {
+             d[i] = create(res,icon[i]);
+        }
+        time =  android.os.SystemClock.elapsedRealtimeNanos()-time;
+        TextView t = new TextView(this);
+        DecimalFormat df = new DecimalFormat("#.##");
+        t.setText("avgL=" + df.format(time / (icon.length * 1000000.)) + " ms");
+        container.addView(t);
+        time =  android.os.SystemClock.elapsedRealtimeNanos();
+        for (int i = 0; i < icon.length; i++) {
+            Button button = new Button(this);
+            button.setWidth(200);
+            button.setBackgroundResource(icon[i]);
+            container.addView(button);
+        }
+        setContentView(scrollView);
+        time =  android.os.SystemClock.elapsedRealtimeNanos()-time;
+        t = new TextView(this);
+        t.setText("avgS=" + df.format(time / (icon.length * 1000000.)) + " ms");
+        container.addView(t);
+    }
+
+}