Merge "make sure screencap's data stream matches what adb expects" into jb-mr2-dev
diff --git a/api/current.txt b/api/current.txt
index 73eed6e..12dc6b4 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2445,6 +2445,7 @@
     method public static android.animation.ObjectAnimator ofObject(java.lang.Object, java.lang.String, android.animation.TypeEvaluator, java.lang.Object...);
     method public static android.animation.ObjectAnimator ofObject(T, android.util.Property<T, V>, android.animation.TypeEvaluator<V>, V...);
     method public static android.animation.ObjectAnimator ofPropertyValuesHolder(java.lang.Object, android.animation.PropertyValuesHolder...);
+    method public void setAutoCancel(boolean);
     method public void setProperty(android.util.Property);
     method public void setPropertyName(java.lang.String);
   }
diff --git a/core/java/android/animation/ObjectAnimator.java b/core/java/android/animation/ObjectAnimator.java
index 0372cb0..173ee73 100644
--- a/core/java/android/animation/ObjectAnimator.java
+++ b/core/java/android/animation/ObjectAnimator.java
@@ -19,7 +19,6 @@
 import android.util.Log;
 import android.util.Property;
 
-import java.lang.reflect.Method;
 import java.util.ArrayList;
 
 /**
@@ -49,6 +48,8 @@
 
     private Property mProperty;
 
+    private boolean mAutoCancel = false;
+
     /**
      * Sets the name of the property that will be animated. This name is used to derive
      * a setter function that will be called to set animated values.
@@ -346,17 +347,83 @@
             // No values yet - this animator is being constructed piecemeal. Init the values with
             // whatever the current propertyName is
             if (mProperty != null) {
-                setValues(PropertyValuesHolder.ofObject(mProperty, (TypeEvaluator)null, values));
+                setValues(PropertyValuesHolder.ofObject(mProperty, (TypeEvaluator) null, values));
             } else {
-                setValues(PropertyValuesHolder.ofObject(mPropertyName, (TypeEvaluator)null, values));
+                setValues(PropertyValuesHolder.ofObject(mPropertyName,
+                        (TypeEvaluator) null, values));
             }
         } else {
             super.setObjectValues(values);
         }
     }
 
+    /**
+     * autoCancel controls whether an ObjectAnimator will be canceled automatically
+     * when any other ObjectAnimator with the same target and properties is started.
+     * Setting this flag may make it easier to run different animators on the same target
+     * object without having to keep track of whether there are conflicting animators that
+     * need to be manually canceled. Canceling animators must have the same exact set of
+     * target properties, in the same order.
+     *
+     * @param cancel Whether future ObjectAnimators with the same target and properties
+     * as this ObjectAnimator will cause this ObjectAnimator to be canceled.
+     */
+    public void setAutoCancel(boolean cancel) {
+        mAutoCancel = cancel;
+    }
+
+    private boolean hasSameTargetAndProperties(Animator anim) {
+        if (anim instanceof ObjectAnimator) {
+            PropertyValuesHolder[] theirValues = ((ObjectAnimator) anim).getValues();
+            if (((ObjectAnimator) anim).getTarget() == mTarget &&
+                    mValues.length == theirValues.length) {
+                for (int i = 0; i < mValues.length; ++i) {
+                    PropertyValuesHolder pvhMine = mValues[i];
+                    PropertyValuesHolder pvhTheirs = theirValues[i];
+                    if (pvhMine.getPropertyName() == null ||
+                            !pvhMine.getPropertyName().equals(pvhTheirs.getPropertyName())) {
+                        return false;
+                    }
+                }
+                return true;
+            }
+        }
+        return false;
+    }
+
     @Override
     public void start() {
+        // See if any of the current active/pending animators need to be canceled
+        AnimationHandler handler = sAnimationHandler.get();
+        if (handler != null) {
+            int numAnims = handler.mAnimations.size();
+            for (int i = numAnims - 1; i >= 0; i--) {
+                if (handler.mAnimations.get(i) instanceof ObjectAnimator) {
+                    ObjectAnimator anim = (ObjectAnimator) handler.mAnimations.get(i);
+                    if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) {
+                        anim.cancel();
+                    }
+                }
+            }
+            numAnims = handler.mPendingAnimations.size();
+            for (int i = numAnims - 1; i >= 0; i--) {
+                if (handler.mPendingAnimations.get(i) instanceof ObjectAnimator) {
+                    ObjectAnimator anim = (ObjectAnimator) handler.mPendingAnimations.get(i);
+                    if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) {
+                        anim.cancel();
+                    }
+                }
+            }
+            numAnims = handler.mDelayedAnims.size();
+            for (int i = numAnims - 1; i >= 0; i--) {
+                if (handler.mDelayedAnims.get(i) instanceof ObjectAnimator) {
+                    ObjectAnimator anim = (ObjectAnimator) handler.mDelayedAnims.get(i);
+                    if (anim.mAutoCancel && hasSameTargetAndProperties(anim)) {
+                        anim.cancel();
+                    }
+                }
+            }
+        }
         if (DBG) {
             Log.d("ObjectAnimator", "Anim target, duration: " + mTarget + ", " + getDuration());
             for (int i = 0; i < mValues.length; ++i) {
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 4a58072..ea605b9 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -83,7 +83,10 @@
 
     // The static sAnimationHandler processes the internal timing loop on which all animations
     // are based
-    private static ThreadLocal<AnimationHandler> sAnimationHandler =
+    /**
+     * @hide
+     */
+    protected static ThreadLocal<AnimationHandler> sAnimationHandler =
             new ThreadLocal<AnimationHandler>();
 
     // The time interpolator to be used if none is set on the animation
@@ -531,22 +534,27 @@
      * animations possible.
      *
      * The handler uses the Choreographer for executing periodic callbacks.
+     *
+     * @hide
      */
-    private static class AnimationHandler implements Runnable {
+    protected static class AnimationHandler implements Runnable {
         // The per-thread list of all active animations
-        private final ArrayList<ValueAnimator> mAnimations = new ArrayList<ValueAnimator>();
+        /** @hide */
+        protected final ArrayList<ValueAnimator> mAnimations = new ArrayList<ValueAnimator>();
 
         // Used in doAnimationFrame() to avoid concurrent modifications of mAnimations
         private final ArrayList<ValueAnimator> mTmpAnimations = new ArrayList<ValueAnimator>();
 
         // The per-thread set of animations to be started on the next animation frame
-        private final ArrayList<ValueAnimator> mPendingAnimations = new ArrayList<ValueAnimator>();
+        /** @hide */
+        protected final ArrayList<ValueAnimator> mPendingAnimations = new ArrayList<ValueAnimator>();
 
         /**
          * Internal per-thread collections used to avoid set collisions as animations start and end
          * while being processed.
+         * @hide
          */
-        private final ArrayList<ValueAnimator> mDelayedAnims = new ArrayList<ValueAnimator>();
+        protected final ArrayList<ValueAnimator> mDelayedAnims = new ArrayList<ValueAnimator>();
         private final ArrayList<ValueAnimator> mEndingAnims = new ArrayList<ValueAnimator>();
         private final ArrayList<ValueAnimator> mReadyAnims = new ArrayList<ValueAnimator>();
 
diff --git a/core/java/android/security/IKeystoreService.java b/core/java/android/security/IKeystoreService.java
index 651693a..2ae3c64 100644
--- a/core/java/android/security/IKeystoreService.java
+++ b/core/java/android/security/IKeystoreService.java
@@ -148,6 +148,10 @@
                     for (int i = 0; i < size; i++) {
                         _result[i] = _reply.readString();
                     }
+                    int _ret = _reply.readInt();
+                    if (_ret != 1) {
+                        return null;
+                    }
                 } finally {
                     _reply.recycle();
                     _data.recycle();
@@ -401,6 +405,25 @@
                 }
                 return _result;
             }
+
+            @Override
+            public int migrate(String name, int targetUid) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                int _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(name);
+                    _data.writeInt(targetUid);
+                    mRemote.transact(Stub.TRANSACTION_migrate, _data, _reply, 0);
+                    _reply.readException();
+                    _result = _reply.readInt();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
         }
 
         private static final String DESCRIPTOR = "android.security.keystore";
@@ -425,6 +448,7 @@
         static final int TRANSACTION_grant = IBinder.FIRST_CALL_TRANSACTION + 17;
         static final int TRANSACTION_ungrant = IBinder.FIRST_CALL_TRANSACTION + 18;
         static final int TRANSACTION_getmtime = IBinder.FIRST_CALL_TRANSACTION + 19;
+        static final int TRANSACTION_migrate = IBinder.FIRST_CALL_TRANSACTION + 20;
 
         /**
          * Cast an IBinder object into an IKeystoreService interface, generating
@@ -509,4 +533,6 @@
     public int ungrant(String name, int granteeUid) throws RemoteException;
 
     public long getmtime(String name) throws RemoteException;
+
+    public int migrate(String name, int targetUid) throws RemoteException;
 }
diff --git a/core/java/android/view/Overlay.java b/core/java/android/view/Overlay.java
index f15d4d2..210bc31 100644
--- a/core/java/android/view/Overlay.java
+++ b/core/java/android/view/Overlay.java
@@ -20,11 +20,7 @@
 /**
  * An overlay is an extra layer that sits on top of a View (the "host view") which is drawn after
  * all other content in that view (including children, if the view is a ViewGroup). Interaction
- * with the overlay layer is done in terms of adding/removing views and drawables. Invalidation and
- * redrawing of the overlay layer (and its host view) is handled differently for views versus
- * drawables in the overlay. Views invalidate themselves as usual, causing appropriate redrawing
- * to occur automatically. Drawables, on the other hand, do not manage invalidation, so changes to
- * drawable objects should be accompanied by appropriate calls to invalidate() on the host view.
+ * with the overlay layer is done in terms of adding/removing views and drawables.
  *
  * @see android.view.View#getOverlay()
  */
@@ -33,9 +29,7 @@
     /**
      * Adds a Drawable to the overlay. The bounds of the drawable should be relative to
      * the host view. Any drawable added to the overlay should be removed when it is no longer
-     * needed or no longer visible. There is no automatic invalidation of the host view; changes to
-     * the drawable should be accompanied by appropriate invalidation calls to the host view
-     * to cause the proper area of the view, and the overlay, to be redrawn.
+     * needed or no longer visible.
      *
      * @param drawable The Drawable to be added to the overlay. This drawable will be
      * drawn when the view redraws its overlay.
diff --git a/core/java/android/view/ViewOverlay.java b/core/java/android/view/ViewOverlay.java
index 8c2ab9d..939377d 100644
--- a/core/java/android/view/ViewOverlay.java
+++ b/core/java/android/view/ViewOverlay.java
@@ -26,8 +26,8 @@
  * ViewOverlay is a container that View uses to host all objects (views and drawables) that
  * are added to its "overlay", gotten through {@link View#getOverlay()}. Views and drawables are
  * added to the overlay via the add/remove methods in this class. These views and drawables are
- * then drawn whenever the view itself is drawn, after which it will draw its overlay (if it
- * exists).
+ * drawn whenever the view itself is drawn; first the view draws its own content (and children,
+ * if it is a ViewGroup), then it draws its overlay (if it has one).
  *
  * Besides managing and drawing the list of drawables, this class serves two purposes:
  * (1) it noops layout calls because children are absolutely positioned and
@@ -65,6 +65,7 @@
             // Make each drawable unique in the overlay; can't add it more than once
             mDrawables.add(drawable);
             invalidate(drawable.getBounds());
+            drawable.setCallback(this);
         }
     }
 
@@ -73,10 +74,16 @@
         if (mDrawables != null) {
             mDrawables.remove(drawable);
             invalidate(drawable.getBounds());
+            drawable.setCallback(null);
         }
     }
 
     @Override
+    public void invalidateDrawable(Drawable drawable) {
+        invalidate(drawable.getBounds());
+    }
+
+    @Override
     public void add(View child) {
         super.addView(child);
     }
diff --git a/core/jni/android_os_SELinux.cpp b/core/jni/android_os_SELinux.cpp
index b12fdfc..0a97f39 100644
--- a/core/jni/android_os_SELinux.cpp
+++ b/core/jni/android_os_SELinux.cpp
@@ -23,428 +23,407 @@
 #include "selinux/selinux.h"
 #include "selinux/android.h"
 #include <errno.h>
+#include <ScopedLocalRef.h>
+#include <ScopedUtfChars.h>
+#include <UniquePtr.h>
 
 namespace android {
 
-  static jboolean isSELinuxDisabled = true;
+struct SecurityContext_Delete {
+    void operator()(security_context_t p) const {
+        freecon(p);
+    }
+};
+typedef UniquePtr<char[], SecurityContext_Delete> Unique_SecurityContext;
 
-  static void throw_NullPointerException(JNIEnv *env, const char* msg) {
-    jclass clazz;
-    clazz = env->FindClass("java/lang/NullPointerException");
-    env->ThrowNew(clazz, msg);
-  }
+static jboolean isSELinuxDisabled = true;
 
-  /*
-   * Function: isSELinuxEnabled
-   * Purpose:  checks whether SELinux is enabled/disbaled
-   * Parameters: none
-   * Return value : true (enabled) or false (disabled)
-   * Exceptions: none
-   */
-  static jboolean isSELinuxEnabled(JNIEnv *env, jobject classz) {
-
+/*
+ * Function: isSELinuxEnabled
+ * Purpose:  checks whether SELinux is enabled/disbaled
+ * Parameters: none
+ * Return value : true (enabled) or false (disabled)
+ * Exceptions: none
+ */
+static jboolean isSELinuxEnabled(JNIEnv *env, jobject) {
     return !isSELinuxDisabled;
-  }
+}
 
-  /*
-   * Function: isSELinuxEnforced
-   * Purpose: return the current SELinux enforce mode
-   * Parameters: none
-   * Return value: true (enforcing) or false (permissive)
-   * Exceptions: none
-   */
-  static jboolean isSELinuxEnforced(JNIEnv *env, jobject clazz) {
+/*
+ * Function: isSELinuxEnforced
+ * Purpose: return the current SELinux enforce mode
+ * Parameters: none
+ * Return value: true (enforcing) or false (permissive)
+ * Exceptions: none
+ */
+static jboolean isSELinuxEnforced(JNIEnv *env, jobject) {
     return (security_getenforce() == 1) ? true : false;
-  }
+}
 
-  /*
-   * Function: setSELinuxEnforce
-   * Purpose: set the SE Linux enforcing mode
-   * Parameters: true (enforcing) or false (permissive)
-   * Return value: true (success) or false (fail)
-   * Exceptions: none
-   */
-  static jboolean setSELinuxEnforce(JNIEnv *env, jobject clazz, jboolean value) {
-    if (isSELinuxDisabled)
-      return false;
+/*
+ * Function: setSELinuxEnforce
+ * Purpose: set the SE Linux enforcing mode
+ * Parameters: true (enforcing) or false (permissive)
+ * Return value: true (success) or false (fail)
+ * Exceptions: none
+ */
+static jboolean setSELinuxEnforce(JNIEnv *env, jobject, jboolean value) {
+    if (isSELinuxDisabled) {
+        return false;
+    }
 
-    int enforce = (value) ? 1 : 0;
+    int enforce = value ? 1 : 0;
 
     return (security_setenforce(enforce) != -1) ? true : false;
-  }
+}
 
-  /*
-   * Function: getPeerCon
-   * Purpose: retrieves security context of peer socket
-   * Parameters:
-   *        fileDescriptor: peer socket file as a FileDescriptor object
-   * Returns: jstring representing the security_context of socket or NULL if error
-   * Exceptions: NullPointerException if fileDescriptor object is NULL
-   */
-  static jstring getPeerCon(JNIEnv *env, jobject clazz, jobject fileDescriptor) {
-    if (isSELinuxDisabled)
-      return NULL;
+/*
+ * Function: getPeerCon
+ * Purpose: retrieves security context of peer socket
+ * Parameters:
+ *        fileDescriptor: peer socket file as a FileDescriptor object
+ * Returns: jstring representing the security_context of socket or NULL if error
+ * Exceptions: NullPointerException if fileDescriptor object is NULL
+ */
+static jstring getPeerCon(JNIEnv *env, jobject, jobject fileDescriptor) {
+    if (isSELinuxDisabled) {
+        return NULL;
+    }
 
     if (fileDescriptor == NULL) {
-      throw_NullPointerException(env, "Trying to check security context of a null peer socket.");
-      return NULL;
+        jniThrowNullPointerException(env,
+                "Trying to check security context of a null peer socket.");
+        return NULL;
     }
 
-    security_context_t context = NULL;
-    jstring securityString = NULL;
-
     int fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
-
     if (env->ExceptionOccurred() != NULL) {
-      ALOGE("There was an issue with retrieving the file descriptor");
-      goto bail;
+        ALOGE("getPeerCon => getFD for %p failed", fileDescriptor);
+        return NULL;
     }
 
-    if (getpeercon(fd, &context) == -1)
-      goto bail;
+    security_context_t tmp;
+    int ret = getpeercon(fd, &tmp);
+    Unique_SecurityContext context(tmp);
 
-    ALOGV("getPeerCon: Successfully retrived context of peer socket '%s'", context);
-
-    securityString = env->NewStringUTF(context);
-
-  bail:
-    if (context != NULL)
-      freecon(context);
-
-    return securityString;
-  }
-
-  /*
-   * Function: setFSCreateCon
-   * Purpose: set security context used for creating a new file system object
-   * Parameters:
-   *       context: security_context_t representing the new context of a file system object,
-   *                set to NULL to return to the default policy behavior
-   * Returns: true on success, false on error
-   * Exception: none
-   */
-  static jboolean setFSCreateCon(JNIEnv *env, jobject clazz, jstring context) {
-    if (isSELinuxDisabled)
-      return false;
-
-    char * securityContext = NULL;
-    const char *constant_securityContext = NULL;
-
-    if (context != NULL) {
-      constant_securityContext = env->GetStringUTFChars(context, NULL);
-
-      // GetStringUTFChars returns const char * yet setfscreatecon needs char *
-      securityContext = const_cast<char *>(constant_securityContext);
+    ScopedLocalRef<jstring> contextStr(env, NULL);
+    if (ret != -1) {
+        contextStr.reset(env->NewStringUTF(context.get()));
     }
 
-    int ret;
-    if ((ret = setfscreatecon(securityContext)) == -1)
-      goto bail;
+    ALOGV("getPeerCon(%d) => %s", fd, contextStr.get());
+    return contextStr.release();
+}
 
-    ALOGV("setFSCreateCon: set new security context to '%s' ", context == NULL ? "default", context);
+/*
+ * Function: setFSCreateCon
+ * Purpose: set security context used for creating a new file system object
+ * Parameters:
+ *       context: security_context_t representing the new context of a file system object,
+ *                set to NULL to return to the default policy behavior
+ * Returns: true on success, false on error
+ * Exception: none
+ */
+static jboolean setFSCreateCon(JNIEnv *env, jobject, jstring contextStr) {
+    if (isSELinuxDisabled) {
+        return false;
+    }
 
-  bail:
-    if (constant_securityContext != NULL)
-      env->ReleaseStringUTFChars(context, constant_securityContext);
+    UniquePtr<ScopedUtfChars> context;
+    const char* context_c_str = NULL;
+    if (contextStr != NULL) {
+        context.reset(new ScopedUtfChars(env, contextStr));
+        context_c_str = context->c_str();
+        if (context_c_str == NULL) {
+            return false;
+        }
+    }
+
+    int ret = setfscreatecon(const_cast<char *>(context_c_str));
+
+    ALOGV("setFSCreateCon(%s) => %d", context_c_str, ret);
 
     return (ret == 0) ? true : false;
-  }
+}
 
-  /*
-   * Function: setFileCon
-   * Purpose:  set the security context of a file object
-   * Parameters:
-   *       path: the location of the file system object
-   *       con: the new security context of the file system object
-   * Returns: true on success, false on error
-   * Exception: NullPointerException is thrown if either path or context strign are NULL
-   */
-  static jboolean setFileCon(JNIEnv *env, jobject clazz, jstring path, jstring con) {
-    if (isSELinuxDisabled)
-      return false;
-
-    if (path == NULL) {
-      throw_NullPointerException(env, "Trying to change the security context of a NULL file object.");
-      return false;
+/*
+ * Function: setFileCon
+ * Purpose:  set the security context of a file object
+ * Parameters:
+ *       path: the location of the file system object
+ *       context: the new security context of the file system object
+ * Returns: true on success, false on error
+ * Exception: NullPointerException is thrown if either path or context strign are NULL
+ */
+static jboolean setFileCon(JNIEnv *env, jobject, jstring pathStr, jstring contextStr) {
+    if (isSELinuxDisabled) {
+        return false;
     }
 
-    if (con == NULL) {
-      throw_NullPointerException(env, "Trying to set the security context of a file object with NULL.");
-      return false;
+    ScopedUtfChars path(env, pathStr);
+    if (path.c_str() == NULL) {
+        return false;
     }
 
-    const char *objectPath = env->GetStringUTFChars(path, NULL);
-    const char *constant_con = env->GetStringUTFChars(con, NULL);
+    ScopedUtfChars context(env, contextStr);
+    if (context.c_str() == NULL) {
+        return false;
+    }
 
     // GetStringUTFChars returns const char * yet setfilecon needs char *
-    char *newCon = const_cast<char *>(constant_con);
+    char *tmp = const_cast<char *>(context.c_str());
+    int ret = setfilecon(path.c_str(), tmp);
 
-    int ret;
-    if ((ret = setfilecon(objectPath, newCon)) == -1)
-      goto bail;
-
-    ALOGV("setFileCon: Succesfully set security context '%s' for '%s'", newCon, objectPath);
-
-  bail:
-    env->ReleaseStringUTFChars(path, objectPath);
-    env->ReleaseStringUTFChars(con, constant_con);
+    ALOGV("setFileCon(%s, %s) => %d", path.c_str(), context.c_str(), ret);
     return (ret == 0) ? true : false;
-  }
+}
 
-  /*
-   * Function: getFileCon
-   * Purpose: retrieves the context associated with the given path in the file system
-   * Parameters:
-   *        path: given path in the file system
-   * Returns:
-   *        string representing the security context string of the file object
-   *        the string may be NULL if an error occured
-   * Exceptions: NullPointerException if the path object is null
-   */
-  static jstring getFileCon(JNIEnv *env, jobject clazz, jstring path) {
-    if (isSELinuxDisabled)
-      return NULL;
-
-    if (path == NULL) {
-      throw_NullPointerException(env, "Trying to check security context of a null path.");
-      return NULL;
+/*
+ * Function: getFileCon
+ * Purpose: retrieves the context associated with the given path in the file system
+ * Parameters:
+ *        path: given path in the file system
+ * Returns:
+ *        string representing the security context string of the file object
+ *        the string may be NULL if an error occured
+ * Exceptions: NullPointerException if the path object is null
+ */
+static jstring getFileCon(JNIEnv *env, jobject, jstring pathStr) {
+    if (isSELinuxDisabled) {
+        return NULL;
     }
 
-    const char *objectPath = env->GetStringUTFChars(path, NULL);
+    ScopedUtfChars path(env, pathStr);
+    if (path.c_str() == NULL) {
+        return NULL;
+    }
 
-    security_context_t context = NULL;
-    jstring securityString = NULL;
+    security_context_t tmp;
+    int ret = getfilecon(path.c_str(), &tmp);
+    Unique_SecurityContext context(tmp);
 
-    if (getfilecon(objectPath, &context) == -1)
-      goto bail;
+    ScopedLocalRef<jstring> securityString(env, NULL);
+    if (ret != -1) {
+        securityString.reset(env->NewStringUTF(context.get()));
+    }
 
-    ALOGV("getFileCon: Successfully retrived context '%s' for file '%s'", context, objectPath);
+    ALOGV("getFileCon(%s) => %s", path.c_str(), context.get());
+    return securityString.release();
+}
 
-    securityString = env->NewStringUTF(context);
+/*
+ * Function: getCon
+ * Purpose: Get the context of the current process.
+ * Parameters: none
+ * Returns: a jstring representing the security context of the process,
+ *          the jstring may be NULL if there was an error
+ * Exceptions: none
+ */
+static jstring getCon(JNIEnv *env, jobject) {
+    if (isSELinuxDisabled) {
+        return NULL;
+    }
 
-  bail:
-    if (context != NULL)
-      freecon(context);
+    security_context_t tmp;
+    int ret = getcon(&tmp);
+    Unique_SecurityContext context(tmp);
 
-    env->ReleaseStringUTFChars(path, objectPath);
+    ScopedLocalRef<jstring> securityString(env, NULL);
+    if (ret != -1) {
+        securityString.reset(env->NewStringUTF(context.get()));
+    }
 
-    return securityString;
-  }
+    ALOGV("getCon() => %s", context.get());
+    return securityString.release();
+}
 
-  /*
-   * Function: getCon
-   * Purpose: Get the context of the current process.
-   * Parameters: none
-   * Returns: a jstring representing the security context of the process,
-   *          the jstring may be NULL if there was an error
-   * Exceptions: none
-   */
-  static jstring getCon(JNIEnv *env, jobject clazz) {
-    if (isSELinuxDisabled)
-      return NULL;
+/*
+ * Function: getPidCon
+ * Purpose: Get the context of a process identified by its pid
+ * Parameters:
+ *            pid: a jint representing the process
+ * Returns: a jstring representing the security context of the pid,
+ *          the jstring may be NULL if there was an error
+ * Exceptions: none
+ */
+static jstring getPidCon(JNIEnv *env, jobject, jint pid) {
+    if (isSELinuxDisabled) {
+        return NULL;
+    }
 
-    security_context_t context = NULL;
-    jstring securityString = NULL;
+    security_context_t tmp;
+    int ret = getpidcon(static_cast<pid_t>(pid), &tmp);
+    Unique_SecurityContext context(tmp);
 
-    if (getcon(&context) == -1)
-      goto bail;
+    ScopedLocalRef<jstring> securityString(env, NULL);
+    if (ret != -1) {
+        securityString.reset(env->NewStringUTF(context.get()));
+    }
 
-    ALOGV("getCon: Successfully retrieved context '%s'", context);
+    ALOGV("getPidCon(%d) => %s", pid, context.get());
+    return securityString.release();
+}
 
-    securityString = env->NewStringUTF(context);
-
-  bail:
-    if (context != NULL)
-      freecon(context);
-
-    return securityString;
-  }
-
-  /*
-   * Function: getPidCon
-   * Purpose: Get the context of a process identified by its pid
-   * Parameters:
-   *            pid: a jint representing the process
-   * Returns: a jstring representing the security context of the pid,
-   *          the jstring may be NULL if there was an error
-   * Exceptions: none
-   */
-  static jstring getPidCon(JNIEnv *env, jobject clazz, jint pid) {
-    if (isSELinuxDisabled)
-      return NULL;
-
-    security_context_t context = NULL;
-    jstring securityString = NULL;
-
-    pid_t checkPid = (pid_t)pid;
-
-    if (getpidcon(checkPid, &context) == -1)
-      goto bail;
-
-    ALOGV("getPidCon: Successfully retrived context '%s' for pid '%d'", context, checkPid);
-
-    securityString = env->NewStringUTF(context);
-
-  bail:
-    if (context != NULL)
-      freecon(context);
-
-    return securityString;
-  }
-
-  /*
-   * Function: getBooleanNames
-   * Purpose: Gets a list of the SELinux boolean names.
-   * Parameters: None
-   * Returns: an array of strings  containing the SELinux boolean names.
-   *          returns NULL string on error
-   * Exceptions: None
-   */
-  static jobjectArray getBooleanNames(JNIEnv *env, JNIEnv clazz) {
-    if (isSELinuxDisabled)
-      return NULL;
+/*
+ * Function: getBooleanNames
+ * Purpose: Gets a list of the SELinux boolean names.
+ * Parameters: None
+ * Returns: an array of strings  containing the SELinux boolean names.
+ *          returns NULL string on error
+ * Exceptions: None
+ */
+static jobjectArray getBooleanNames(JNIEnv *env, JNIEnv) {
+    if (isSELinuxDisabled) {
+        return NULL;
+    }
 
     char **list;
-    int i, len, ret;
-    jclass stringClass;
-    jobjectArray stringArray = NULL;
+    int len;
+    if (security_get_boolean_names(&list, &len) == -1) {
+        return NULL;
+    }
 
-    if (security_get_boolean_names(&list, &len) == -1)
-      return NULL;
-
-    stringClass = env->FindClass("java/lang/String");
-    stringArray = env->NewObjectArray(len, stringClass, env->NewStringUTF(""));
-    for (i = 0; i < len; i++) {
-      jstring obj;
-      obj = env->NewStringUTF(list[i]);
-      env->SetObjectArrayElement(stringArray, i, obj);
-      env->DeleteLocalRef(obj);
-      free(list[i]);
+    jclass stringClass = env->FindClass("java/lang/String");
+    jobjectArray stringArray = env->NewObjectArray(len, stringClass, NULL);
+    for (int i = 0; i < len; i++) {
+        ScopedLocalRef<jstring> obj(env, env->NewStringUTF(list[i]));
+        env->SetObjectArrayElement(stringArray, i, obj.get());
+        free(list[i]);
     }
     free(list);
 
     return stringArray;
-  }
+}
 
-  /*
-   * Function: getBooleanValue
-   * Purpose: Gets the value for the given SELinux boolean name.
-   * Parameters:
-   *            String: The name of the SELinux boolean.
-   * Returns: a boolean: (true) boolean is set or (false) it is not.
-   * Exceptions: None
-   */
-  static jboolean getBooleanValue(JNIEnv *env, jobject clazz, jstring name) {
-    if (isSELinuxDisabled)
-      return false;
+/*
+ * Function: getBooleanValue
+ * Purpose: Gets the value for the given SELinux boolean name.
+ * Parameters:
+ *            String: The name of the SELinux boolean.
+ * Returns: a boolean: (true) boolean is set or (false) it is not.
+ * Exceptions: None
+ */
+static jboolean getBooleanValue(JNIEnv *env, jobject, jstring nameStr) {
+    if (isSELinuxDisabled) {
+        return false;
+    }
 
-    const char *boolean_name;
-    int ret;
+    if (nameStr == NULL) {
+        return false;
+    }
 
-    if (name == NULL)
-      return false;
-    boolean_name = env->GetStringUTFChars(name, NULL);
-    ret = security_get_boolean_active(boolean_name);
-    env->ReleaseStringUTFChars(name, boolean_name);
+    ScopedUtfChars name(env, nameStr);
+    int ret = security_get_boolean_active(name.c_str());
+
+    ALOGV("getBooleanValue(%s) => %d", name.c_str(), ret);
     return (ret == 1) ? true : false;
-  }
+}
 
-  /*
-   * Function: setBooleanNames
-   * Purpose: Sets the value for the given SELinux boolean name.
-   * Parameters:
-   *            String: The name of the SELinux boolean.
-   *            Boolean: The new value of the SELinux boolean.
-   * Returns: a boolean indicating whether or not the operation succeeded.
-   * Exceptions: None
-   */
-  static jboolean setBooleanValue(JNIEnv *env, jobject clazz, jstring name, jboolean value) {
-    if (isSELinuxDisabled)
-      return false;
+/*
+ * Function: setBooleanNames
+ * Purpose: Sets the value for the given SELinux boolean name.
+ * Parameters:
+ *            String: The name of the SELinux boolean.
+ *            Boolean: The new value of the SELinux boolean.
+ * Returns: a boolean indicating whether or not the operation succeeded.
+ * Exceptions: None
+ */
+static jboolean setBooleanValue(JNIEnv *env, jobject, jstring nameStr, jboolean value) {
+    if (isSELinuxDisabled) {
+        return false;
+    }
 
-    const char *boolean_name = NULL;
-    int ret;
+    if (nameStr == NULL) {
+        return false;
+    }
 
-    if (name == NULL)
-      return false;
-    boolean_name = env->GetStringUTFChars(name, NULL);
-    ret = security_set_boolean(boolean_name, (value) ? 1 : 0);
-    env->ReleaseStringUTFChars(name, boolean_name);
-    if (ret)
-      return false;
+    ScopedUtfChars name(env, nameStr);
+    int ret = security_set_boolean(name.c_str(), value ? 1 : 0);
+    if (ret) {
+        return false;
+    }
 
-    if (security_commit_booleans() == -1)
-      return false;
+    if (security_commit_booleans() == -1) {
+        return false;
+    }
 
     return true;
-  }
+}
 
-  /*
-   * Function: checkSELinuxAccess
-   * Purpose: Check permissions between two security contexts.
-   * Parameters: scon: subject security context as a string
-   *             tcon: object security context as a string
-   *             tclass: object's security class name as a string
-   *             perm: permission name as a string
-   * Returns: boolean: (true) if permission was granted, (false) otherwise
-   * Exceptions: None
-   */
-  static jboolean checkSELinuxAccess(JNIEnv *env, jobject clazz, jstring scon, jstring tcon, jstring tclass, jstring perm) {
-    if (isSELinuxDisabled)
-      return true;
+/*
+ * Function: checkSELinuxAccess
+ * Purpose: Check permissions between two security contexts.
+ * Parameters: subjectContextStr: subject security context as a string
+ *             objectContextStr: object security context as a string
+ *             objectClassStr: object's security class name as a string
+ *             permissionStr: permission name as a string
+ * Returns: boolean: (true) if permission was granted, (false) otherwise
+ * Exceptions: None
+ */
+static jboolean checkSELinuxAccess(JNIEnv *env, jobject, jstring subjectContextStr,
+        jstring objectContextStr, jstring objectClassStr, jstring permissionStr) {
+    if (isSELinuxDisabled) {
+        return true;
+    }
 
-    int accessGranted = -1;
+    ScopedUtfChars subjectContext(env, subjectContextStr);
+    if (subjectContext.c_str() == NULL) {
+        return false;
+    }
 
-    const char *const_scon, *const_tcon, *mytclass, *myperm;
-    char *myscon, *mytcon;
+    ScopedUtfChars objectContext(env, objectContextStr);
+    if (objectContext.c_str() == NULL) {
+        return false;
+    }
 
-    if (scon == NULL || tcon == NULL || tclass == NULL || perm == NULL)
-      goto bail;
+    ScopedUtfChars objectClass(env, objectClassStr);
+    if (objectClass.c_str() == NULL) {
+        return false;
+    }
 
-    const_scon = env->GetStringUTFChars(scon, NULL);
-    const_tcon = env->GetStringUTFChars(tcon, NULL);
-    mytclass   = env->GetStringUTFChars(tclass, NULL);
-    myperm     = env->GetStringUTFChars(perm, NULL);
+    ScopedUtfChars permission(env, permissionStr);
+    if (permission.c_str() == NULL) {
+        return false;
+    }
 
-    // selinux_check_access needs char* for some
-    myscon = const_cast<char *>(const_scon);
-    mytcon = const_cast<char *>(const_tcon);
+    char *tmp1 = const_cast<char *>(subjectContext.c_str());
+    char *tmp2 = const_cast<char *>(objectContext.c_str());
+    int accessGranted = selinux_check_access(tmp1, tmp2, objectClass.c_str(), permission.c_str(),
+            NULL);
 
-    accessGranted = selinux_check_access(myscon, mytcon, mytclass, myperm, NULL);
+    ALOGV("checkSELinuxAccess(%s, %s, %s, %s) => %d", subjectContext.c_str(), objectContext.c_str(),
+            objectClass.c_str(), permission.c_str(), accessGranted);
 
-    ALOGV("selinux_check_access returned %d", accessGranted);
-
-    env->ReleaseStringUTFChars(scon, const_scon);
-    env->ReleaseStringUTFChars(tcon, const_tcon);
-    env->ReleaseStringUTFChars(tclass, mytclass);
-    env->ReleaseStringUTFChars(perm, myperm);
-
-  bail:
     return (accessGranted == 0) ? true : false;
-  }
+}
 
-  /*
-   * Function: native_restorecon
-   * Purpose: restore default SELinux security context
-   * Parameters: pathname: the pathname for the file to be relabeled
-   * Returns: boolean: (true) file label successfully restored, (false) otherwise
-   * Exceptions: none
-   */
-  static jboolean native_restorecon(JNIEnv *env, jobject clazz, jstring pathname) {
-    if (isSELinuxDisabled)
-      return true;
+/*
+ * Function: native_restorecon
+ * Purpose: restore default SELinux security context
+ * Parameters: pathname: the pathname for the file to be relabeled
+ * Returns: boolean: (true) file label successfully restored, (false) otherwise
+ * Exceptions: none
+ */
+static jboolean native_restorecon(JNIEnv *env, jobject, jstring pathnameStr) {
+    if (isSELinuxDisabled) {
+        return true;
+    }
 
-    const char *file = const_cast<char *>(env->GetStringUTFChars(pathname, NULL));
-    int ret = selinux_android_restorecon(file);
-    env->ReleaseStringUTFChars(pathname, file);
+    ScopedUtfChars pathname(env, pathnameStr);
+    if (pathname.c_str() == NULL) {
+        ALOGV("restorecon(%p) => threw exception", pathname);
+        return false;
+    }
+
+    int ret = selinux_android_restorecon(pathname.c_str());
+    ALOGV("restorecon(%s) => %d", pathname.c_str(), ret);
     return (ret == 0);
-  }
+}
 
-  /*
-   * JNI registration.
-   */
-  static JNINativeMethod method_table[] = {
-
+/*
+ * JNI registration.
+ */
+static JNINativeMethod method_table[] = {
     /* name,                     signature,                    funcPtr */
     { "checkSELinuxAccess"       , "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z" , (void*)checkSELinuxAccess },
     { "getBooleanNames"          , "()[Ljava/lang/String;"                        , (void*)getBooleanNames  },
@@ -460,25 +439,25 @@
     { "setFileContext"           , "(Ljava/lang/String;Ljava/lang/String;)Z"      , (void*)setFileCon       },
     { "setFSCreateContext"       , "(Ljava/lang/String;)Z"                        , (void*)setFSCreateCon   },
     { "setSELinuxEnforce"        , "(Z)Z"                                         , (void*)setSELinuxEnforce},
-  };
+};
 
-  static int log_callback(int type, const char *fmt, ...) {
+static int log_callback(int type, const char *fmt, ...) {
     va_list ap;
     va_start(ap, fmt);
     LOG_PRI_VA(ANDROID_LOG_ERROR, "SELinux", fmt, ap);
     va_end(ap);
     return 0;
-  }
+}
 
-  int register_android_os_SELinux(JNIEnv *env) {
+int register_android_os_SELinux(JNIEnv *env) {
     union selinux_callback cb;
     cb.func_log = log_callback;
     selinux_set_callback(SELINUX_CB_LOG, cb);
 
     isSELinuxDisabled = (is_selinux_enabled() != 1) ? true : false;
 
-    return AndroidRuntime::registerNativeMethods(
-         env, "android/os/SELinux",
-         method_table, NELEM(method_table));
-  }
+    return AndroidRuntime::registerNativeMethods(env, "android/os/SELinux", method_table,
+            NELEM(method_table));
+}
+
 }
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d57d56a..614ac07 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1136,6 +1136,7 @@
   <java-symbol type="xml" name="power_profile" />
   <java-symbol type="xml" name="time_zones_by_country" />
   <java-symbol type="xml" name="sms_short_codes" />
+  <java-symbol type="xml" name="audio_assets" />
 
   <java-symbol type="raw" name="accessibility_gestures" />
   <java-symbol type="raw" name="incognito_mode_start_page" />
diff --git a/core/res/res/xml/audio_assets.xml b/core/res/res/xml/audio_assets.xml
new file mode 100644
index 0000000..746dbab
--- /dev/null
+++ b/core/res/res/xml/audio_assets.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2012, 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.
+*/
+-->
+
+<!-- Mapping of UI sound effects to audio assets under /system/media/audio/ui.
+     Modify this file to override default sound assets.
+     Currently only touch sounds can be overridden. Other groups can be added
+     in the future for other UI sounds like camera, lock, dock...
+-->
+
+<audio_assets version="1.0">
+    <group name="touch_sounds">
+        <asset id="FX_KEY_CLICK" file="Effect_Tick.ogg"/>
+        <asset id="FX_FOCUS_NAVIGATION_UP" file="Effect_Tick.ogg"/>
+        <asset id="FX_FOCUS_NAVIGATION_DOWN" file="Effect_Tick.ogg"/>
+        <asset id="FX_FOCUS_NAVIGATION_LEFT" file="Effect_Tick.ogg"/>
+        <asset id="FX_FOCUS_NAVIGATION_RIGHT" file="Effect_Tick.ogg"/>
+        <asset id="FX_KEYPRESS_STANDARD" file="KeypressStandard.ogg"/>
+        <asset id="FX_KEYPRESS_SPACEBAR" file="KeypressSpacebar.ogg"/>
+        <asset id="FX_KEYPRESS_DELETE" file="KeypressDelete.ogg"/>
+        <asset id="FX_KEYPRESS_RETURN" file="KeypressReturn.ogg"/>
+    </group>
+</audio_assets>
diff --git a/core/tests/coretests/src/android/animation/AutoCancelTest.java b/core/tests/coretests/src/android/animation/AutoCancelTest.java
new file mode 100644
index 0000000..b1f88db
--- /dev/null
+++ b/core/tests/coretests/src/android/animation/AutoCancelTest.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2013 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.animation;
+
+import android.os.Handler;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.util.HashMap;
+import java.util.concurrent.TimeUnit;
+
+public class AutoCancelTest extends ActivityInstrumentationTestCase2<BasicAnimatorActivity> {
+
+    boolean mAnimX1Canceled = false;
+    boolean mAnimXY1Canceled = false;
+    boolean mAnimX2Canceled = false;
+    boolean mAnimXY2Canceled = false;
+
+    private static final long START_DELAY = 100;
+    private static final long DELAYED_START_DURATION = 200;
+    private static final long FUTURE_TIMEOUT = 1000;
+
+    HashMap<Animator, Boolean> mCanceledMap = new HashMap<Animator, Boolean>();
+
+    public AutoCancelTest() {
+        super(BasicAnimatorActivity.class);
+    }
+
+    ObjectAnimator setupAnimator(long startDelay, String... properties) {
+        ObjectAnimator returnVal;
+        if (properties.length == 1) {
+            returnVal = ObjectAnimator.ofFloat(this, properties[0], 0, 1);
+        } else {
+            PropertyValuesHolder[] pvhArray = new PropertyValuesHolder[properties.length];
+            for (int i = 0; i < properties.length; i++) {
+                pvhArray[i] = PropertyValuesHolder.ofFloat(properties[i], 0, 1);
+            }
+            returnVal = ObjectAnimator.ofPropertyValuesHolder(this, pvhArray);
+        }
+        returnVal.setAutoCancel(true);
+        returnVal.setStartDelay(startDelay);
+        returnVal.addListener(mCanceledListener);
+        return returnVal;
+    }
+
+    private void setupAnimators(long startDelay, boolean startLater, final FutureWaiter future)
+    throws Exception {
+        // Animators to be auto-canceled
+        final ObjectAnimator animX1 = setupAnimator(startDelay, "x");
+        final ObjectAnimator animY1 = setupAnimator(startDelay, "y");
+        final ObjectAnimator animXY1 = setupAnimator(startDelay, "x", "y");
+        final ObjectAnimator animXZ1 = setupAnimator(startDelay, "x", "z");
+
+        animX1.start();
+        animY1.start();
+        animXY1.start();
+        animXZ1.start();
+
+        final ObjectAnimator animX2 = setupAnimator(0, "x");
+        animX2.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                // We expect only animX1 to be canceled at this point
+                if (mCanceledMap.get(animX1) == null ||
+                        mCanceledMap.get(animX1) != true ||
+                        mCanceledMap.get(animY1) != null ||
+                        mCanceledMap.get(animXY1) != null ||
+                        mCanceledMap.get(animXZ1) != null) {
+                    future.set(false);
+                }
+            }
+        });
+
+        final ObjectAnimator animXY2 = setupAnimator(0, "x", "y");
+        animXY2.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationStart(Animator animation) {
+                // We expect only animXY1 to be canceled at this point
+                if (mCanceledMap.get(animXY1) == null ||
+                        mCanceledMap.get(animXY1) != true ||
+                        mCanceledMap.get(animY1) != null ||
+                        mCanceledMap.get(animXZ1) != null) {
+                    future.set(false);
+                }
+
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                // Release future if not done already via failures during start
+                future.release();
+            }
+        });
+
+        if (startLater) {
+            Handler handler = new Handler();
+            handler.postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    animX2.start();
+                    animXY2.start();
+                }
+            }, DELAYED_START_DURATION);
+        } else {
+            animX2.start();
+            animXY2.start();
+        }
+    }
+
+    @SmallTest
+    public void testAutoCancel() throws Exception {
+        final FutureWaiter future = new FutureWaiter();
+        getActivity().runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    setupAnimators(0, false, future);
+                } catch (Exception e) {
+                    future.setException(e);
+                }
+            }
+        });
+        assertTrue(future.get(FUTURE_TIMEOUT, TimeUnit.MILLISECONDS));
+    }
+
+    @SmallTest
+    public void testAutoCancelDelayed() throws Exception {
+        final FutureWaiter future = new FutureWaiter();
+        getActivity().runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    setupAnimators(START_DELAY, false, future);
+                } catch (Exception e) {
+                    future.setException(e);
+                }
+            }
+        });
+        assertTrue(future.get(FUTURE_TIMEOUT, TimeUnit.MILLISECONDS));
+    }
+
+    @SmallTest
+    public void testAutoCancelTestLater() throws Exception {
+        final FutureWaiter future = new FutureWaiter();
+        getActivity().runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    setupAnimators(0, true, future);
+                } catch (Exception e) {
+                    future.setException(e);
+                }
+            }
+        });
+        assertTrue(future.get(FUTURE_TIMEOUT, TimeUnit.MILLISECONDS));
+    }
+
+    @SmallTest
+    public void testAutoCancelDelayedTestLater() throws Exception {
+        final FutureWaiter future = new FutureWaiter();
+        getActivity().runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    setupAnimators(START_DELAY, true, future);
+                } catch (Exception e) {
+                    future.setException(e);
+                }
+            }
+        });
+        assertTrue(future.get(FUTURE_TIMEOUT, TimeUnit.MILLISECONDS));
+    }
+
+    private AnimatorListenerAdapter mCanceledListener = new AnimatorListenerAdapter() {
+        @Override
+        public void onAnimationCancel(Animator animation) {
+            mCanceledMap.put(animation, true);
+        }
+    };
+
+    public void setX(float x) {}
+
+    public void setY(float y) {}
+
+    public void setZ(float z) {}
+}
+
+
diff --git a/core/tests/coretests/src/android/animation/FutureWaiter.java b/core/tests/coretests/src/android/animation/FutureWaiter.java
index 320a1c2..0c65e20 100644
--- a/core/tests/coretests/src/android/animation/FutureWaiter.java
+++ b/core/tests/coretests/src/android/animation/FutureWaiter.java
@@ -23,14 +23,21 @@
  * {@link com.google.common.util.concurrent.AbstractFuture#set(Object)} method internally. It
  * also exposes the protected {@link AbstractFuture#setException(Throwable)} method.
  */
-public class FutureWaiter extends AbstractFuture<Void> {
+public class FutureWaiter extends AbstractFuture<Boolean> {
 
     /**
      * Release the Future currently waiting on
      * {@link com.google.common.util.concurrent.AbstractFuture#get()}.
      */
     public void release() {
-        super.set(null);
+        super.set(true);
+    }
+
+    /**
+     * Used to indicate failure (when the result value is false).
+     */
+    public void set(boolean result) {
+        super.set(result);
     }
 
     @Override
diff --git a/docs/html/training/notify-user/build-notification.jd b/docs/html/training/notify-user/build-notification.jd
index ba66028..80f2cd5 100644
--- a/docs/html/training/notify-user/build-notification.jd
+++ b/docs/html/training/notify-user/build-notification.jd
@@ -149,12 +149,14 @@
 <p>For example:</p>
 
 <pre>
+NotificationCompat.Builder mBuilder;
+...
 // Sets an ID for the notification
 int mNotificationId = 001;
 // Gets an instance of the NotificationManager service
 NotificationManager mNotifyMgr = 
         (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
 // Builds the notification and issues it.
-mNotifyMgr.notify(mNotificationId, builder.build());
+mNotifyMgr.notify(mNotificationId, mBuilder.build());
 </pre>
 
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 37f2250..c90f400 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -146,6 +146,10 @@
 
         if (oldBounds.left != left || oldBounds.top != top ||
                 oldBounds.right != right || oldBounds.bottom != bottom) {
+            if (!oldBounds.isEmpty()) {
+                // first invalidate the previous bounds
+                invalidateSelf();
+            }
             mBounds.set(left, top, right, bottom);
             onBoundsChange(mBounds);
         }
diff --git a/keystore/java/android/security/AndroidKeyStore.java b/keystore/java/android/security/AndroidKeyStore.java
index 65d7b8f..8a9826b 100644
--- a/keystore/java/android/security/AndroidKeyStore.java
+++ b/keystore/java/android/security/AndroidKeyStore.java
@@ -453,17 +453,19 @@
          * convention.
          */
         final String[] certAliases = mKeyStore.saw(Credentials.USER_CERTIFICATE);
-        for (String alias : certAliases) {
-            final byte[] certBytes = mKeyStore.get(Credentials.USER_CERTIFICATE + alias);
-            if (certBytes == null) {
-                continue;
-            }
+        if (certAliases != null) {
+            for (String alias : certAliases) {
+                final byte[] certBytes = mKeyStore.get(Credentials.USER_CERTIFICATE + alias);
+                if (certBytes == null) {
+                    continue;
+                }
 
-            final Certificate c = toCertificate(certBytes);
-            nonCaEntries.add(alias);
+                final Certificate c = toCertificate(certBytes);
+                nonCaEntries.add(alias);
 
-            if (cert.equals(c)) {
-                return alias;
+                if (cert.equals(c)) {
+                    return alias;
+                }
             }
         }
 
@@ -472,19 +474,22 @@
          * PrivateKeyEntry we looked at above.
          */
         final String[] caAliases = mKeyStore.saw(Credentials.CA_CERTIFICATE);
-        for (String alias : caAliases) {
-            if (nonCaEntries.contains(alias)) {
-                continue;
-            }
+        if (certAliases != null) {
+            for (String alias : caAliases) {
+                if (nonCaEntries.contains(alias)) {
+                    continue;
+                }
 
-            final byte[] certBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias);
-            if (certBytes == null) {
-                continue;
-            }
+                final byte[] certBytes = mKeyStore.get(Credentials.CA_CERTIFICATE + alias);
+                if (certBytes == null) {
+                    continue;
+                }
 
-            final Certificate c = toCertificate(mKeyStore.get(Credentials.CA_CERTIFICATE + alias));
-            if (cert.equals(c)) {
-                return alias;
+                final Certificate c =
+                        toCertificate(mKeyStore.get(Credentials.CA_CERTIFICATE + alias));
+                if (cert.equals(c)) {
+                    return alias;
+                }
             }
         }
 
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 4b69317..4dc0beb 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -87,9 +87,22 @@
         }
     }
 
-    public boolean put(String key, byte[] value) {
+    public boolean put(String key, byte[] value, int uid) {
         try {
-            return mBinder.insert(key, value, -1) == NO_ERROR;
+            return mBinder.insert(key, value, uid) == NO_ERROR;
+        } catch (RemoteException e) {
+            Log.w(TAG, "Cannot connect to keystore", e);
+            return false;
+        }
+    }
+
+    public boolean put(String key, byte[] value) {
+        return put(key, value, -1);
+    }
+
+    public boolean delete(String key, int uid) {
+        try {
+            return mBinder.del(key, uid) == NO_ERROR;
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return false;
@@ -97,8 +110,12 @@
     }
 
     public boolean delete(String key) {
+        return delete(key, -1);
+    }
+
+    public boolean contains(String key, int uid) {
         try {
-            return mBinder.del(key, -1) == NO_ERROR;
+            return mBinder.exist(key, uid) == NO_ERROR;
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return false;
@@ -106,23 +123,22 @@
     }
 
     public boolean contains(String key) {
-        try {
-            return mBinder.exist(key, -1) == NO_ERROR;
-        } catch (RemoteException e) {
-            Log.w(TAG, "Cannot connect to keystore", e);
-            return false;
-        }
+        return contains(key, -1);
     }
 
-    public String[] saw(String prefix) {
+    public String[] saw(String prefix, int uid) {
         try {
-            return mBinder.saw(prefix, -1);
+            return mBinder.saw(prefix, uid);
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return null;
         }
     }
 
+    public String[] saw(String prefix) {
+        return saw(prefix, -1);
+    }
+
     public boolean reset() {
         try {
             return mBinder.reset() == NO_ERROR;
@@ -169,9 +185,22 @@
         }
     }
 
-    public boolean generate(String key) {
+    public boolean generate(String key, int uid) {
         try {
-            return mBinder.generate(key, -1) == NO_ERROR;
+            return mBinder.generate(key, uid) == NO_ERROR;
+        } catch (RemoteException e) {
+            Log.w(TAG, "Cannot connect to keystore", e);
+            return false;
+        }
+    }
+
+    public boolean generate(String key) {
+        return generate(key, -1);
+    }
+
+    public boolean importKey(String keyName, byte[] key, int uid) {
+        try {
+            return mBinder.import_key(keyName, key, uid) == NO_ERROR;
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return false;
@@ -179,12 +208,7 @@
     }
 
     public boolean importKey(String keyName, byte[] key) {
-        try {
-            return mBinder.import_key(keyName, key, -1) == NO_ERROR;
-        } catch (RemoteException e) {
-            Log.w(TAG, "Cannot connect to keystore", e);
-            return false;
-        }
+        return importKey(keyName, key, -1);
     }
 
     public byte[] getPubkey(String key) {
@@ -196,15 +220,19 @@
         }
     }
 
-    public boolean delKey(String key) {
+    public boolean delKey(String key, int uid) {
         try {
-            return mBinder.del_key(key, -1) == NO_ERROR;
+            return mBinder.del_key(key, uid) == NO_ERROR;
         } catch (RemoteException e) {
             Log.w(TAG, "Cannot connect to keystore", e);
             return false;
         }
     }
 
+    public boolean delKey(String key) {
+        return delKey(key, -1);
+    }
+
     public byte[] sign(String key, byte[] data) {
         try {
             return mBinder.sign(key, data);
@@ -259,6 +287,15 @@
         }
     }
 
+    public boolean migrate(String key, int uid) {
+        try {
+            return mBinder.migrate(key, uid) == NO_ERROR;
+        } catch (RemoteException e) {
+            Log.w(TAG, "Cannot connect to keystore", e);
+            return false;
+        }
+    }
+
     public int getLastError() {
         return mError;
     }
diff --git a/keystore/tests/src/android/security/AndroidKeyPairGeneratorTest.java b/keystore/tests/src/android/security/AndroidKeyPairGeneratorTest.java
index cd031b4..69007c4 100644
--- a/keystore/tests/src/android/security/AndroidKeyPairGeneratorTest.java
+++ b/keystore/tests/src/android/security/AndroidKeyPairGeneratorTest.java
@@ -67,7 +67,9 @@
         assertTrue(mAndroidKeyStore.password("1111"));
         assertTrue(mAndroidKeyStore.isUnlocked());
 
-        assertEquals(0, mAndroidKeyStore.saw("").length);
+        String[] aliases = mAndroidKeyStore.saw("");
+        assertNotNull(aliases);
+        assertEquals(0, aliases.length);
 
         mGenerator = java.security.KeyPairGenerator.getInstance(AndroidKeyPairGenerator.NAME);
     }
diff --git a/keystore/tests/src/android/security/KeyStoreTest.java b/keystore/tests/src/android/security/KeyStoreTest.java
index 07a2d7b..8f8ee92 100644
--- a/keystore/tests/src/android/security/KeyStoreTest.java
+++ b/keystore/tests/src/android/security/KeyStoreTest.java
@@ -17,6 +17,7 @@
 package android.security;
 
 import android.app.Activity;
+import android.os.Process;
 import android.security.KeyStore;
 import android.test.ActivityUnitTestCase;
 import android.test.AssertionFailedError;
@@ -128,7 +129,7 @@
         super.tearDown();
     }
 
-    public void teststate() throws Exception {
+    public void testState() throws Exception {
         assertEquals(KeyStore.State.UNINITIALIZED, mKeyStore.state());
     }
 
@@ -154,6 +155,24 @@
         assertTrue(Arrays.equals(TEST_KEYVALUE, mKeyStore.get(TEST_KEYNAME)));
     }
 
+    public void testPut_grantedUid_Wifi() throws Exception {
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+        assertFalse(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, Process.WIFI_UID));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+        mKeyStore.password(TEST_PASSWD);
+        assertTrue(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, Process.WIFI_UID));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+    }
+
+    public void testPut_ungrantedUid_Bluetooth() throws Exception {
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
+        assertFalse(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, Process.BLUETOOTH_UID));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
+        mKeyStore.password(TEST_PASSWD);
+        assertFalse(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, Process.BLUETOOTH_UID));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
+    }
+
     public void testI18n() throws Exception {
         assertFalse(mKeyStore.put(TEST_I18N_KEY, TEST_I18N_VALUE));
         assertFalse(mKeyStore.contains(TEST_I18N_KEY));
@@ -167,22 +186,64 @@
         mKeyStore.password(TEST_PASSWD);
         assertFalse(mKeyStore.delete(TEST_KEYNAME));
 
-        mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE);
+        assertTrue(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE));
         assertTrue(Arrays.equals(TEST_KEYVALUE, mKeyStore.get(TEST_KEYNAME)));
         assertTrue(mKeyStore.delete(TEST_KEYNAME));
         assertNull(mKeyStore.get(TEST_KEYNAME));
     }
 
+    public void testDelete_grantedUid_Wifi() throws Exception {
+        assertFalse(mKeyStore.delete(TEST_KEYNAME, Process.WIFI_UID));
+        mKeyStore.password(TEST_PASSWD);
+        assertFalse(mKeyStore.delete(TEST_KEYNAME, Process.WIFI_UID));
+
+        assertTrue(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, Process.WIFI_UID));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+        assertTrue(mKeyStore.delete(TEST_KEYNAME, Process.WIFI_UID));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+    }
+
+    public void testDelete_ungrantedUid_Bluetooth() throws Exception {
+        assertFalse(mKeyStore.delete(TEST_KEYNAME, Process.BLUETOOTH_UID));
+        mKeyStore.password(TEST_PASSWD);
+        assertFalse(mKeyStore.delete(TEST_KEYNAME, Process.BLUETOOTH_UID));
+
+        assertFalse(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, Process.BLUETOOTH_UID));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
+        assertFalse(mKeyStore.delete(TEST_KEYNAME, Process.BLUETOOTH_UID));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
+    }
+
     public void testContains() throws Exception {
         assertFalse(mKeyStore.contains(TEST_KEYNAME));
 
-        mKeyStore.password(TEST_PASSWD);
+        assertTrue(mKeyStore.password(TEST_PASSWD));
         assertFalse(mKeyStore.contains(TEST_KEYNAME));
 
-        mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE);
+        assertTrue(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE));
         assertTrue(mKeyStore.contains(TEST_KEYNAME));
     }
 
+    public void testContains_grantedUid_Wifi() throws Exception {
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+
+        assertTrue(mKeyStore.password(TEST_PASSWD));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+
+        assertTrue(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, Process.WIFI_UID));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+    }
+
+    public void testContains_grantedUid_Bluetooth() throws Exception {
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
+
+        assertTrue(mKeyStore.password(TEST_PASSWD));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
+
+        assertFalse(mKeyStore.put(TEST_KEYNAME, TEST_KEYVALUE, Process.BLUETOOTH_UID));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
+    }
+
     public void testSaw() throws Exception {
         String[] emptyResult = mKeyStore.saw(TEST_KEYNAME);
         assertNotNull(emptyResult);
@@ -198,6 +259,48 @@
                      new HashSet(Arrays.asList(results)));
     }
 
+    public void testSaw_ungrantedUid_Bluetooth() throws Exception {
+        String[] results1 = mKeyStore.saw(TEST_KEYNAME, Process.BLUETOOTH_UID);
+        assertNull(results1);
+
+        mKeyStore.password(TEST_PASSWD);
+        mKeyStore.put(TEST_KEYNAME1, TEST_KEYVALUE);
+        mKeyStore.put(TEST_KEYNAME2, TEST_KEYVALUE);
+
+        String[] results2 = mKeyStore.saw(TEST_KEYNAME, Process.BLUETOOTH_UID);
+        assertNull(results2);
+    }
+
+    public void testSaw_grantedUid_Wifi() throws Exception {
+        String[] results1 = mKeyStore.saw(TEST_KEYNAME, Process.WIFI_UID);
+        assertNotNull(results1);
+        assertEquals(0, results1.length);
+
+        mKeyStore.password(TEST_PASSWD);
+        mKeyStore.put(TEST_KEYNAME1, TEST_KEYVALUE, Process.WIFI_UID);
+        mKeyStore.put(TEST_KEYNAME2, TEST_KEYVALUE, Process.WIFI_UID);
+
+        String[] results2 = mKeyStore.saw(TEST_KEYNAME, Process.WIFI_UID);
+        assertEquals(new HashSet(Arrays.asList(TEST_KEYNAME1.substring(TEST_KEYNAME.length()),
+                                               TEST_KEYNAME2.substring(TEST_KEYNAME.length()))),
+                     new HashSet(Arrays.asList(results2)));
+    }
+
+    public void testSaw_grantedUid_Vpn() throws Exception {
+        String[] results1 = mKeyStore.saw(TEST_KEYNAME, Process.VPN_UID);
+        assertNotNull(results1);
+        assertEquals(0, results1.length);
+
+        mKeyStore.password(TEST_PASSWD);
+        mKeyStore.put(TEST_KEYNAME1, TEST_KEYVALUE, Process.VPN_UID);
+        mKeyStore.put(TEST_KEYNAME2, TEST_KEYVALUE, Process.VPN_UID);
+
+        String[] results2 = mKeyStore.saw(TEST_KEYNAME, Process.VPN_UID);
+        assertEquals(new HashSet(Arrays.asList(TEST_KEYNAME1.substring(TEST_KEYNAME.length()),
+                                               TEST_KEYNAME2.substring(TEST_KEYNAME.length()))),
+                     new HashSet(Arrays.asList(results2)));
+    }
+
     public void testLock() throws Exception {
         assertFalse(mKeyStore.lock());
 
@@ -239,17 +342,57 @@
     }
 
     public void testGenerate_Success() throws Exception {
-        mKeyStore.password(TEST_PASSWD);
+        assertTrue(mKeyStore.password(TEST_PASSWD));
 
         assertTrue("Should be able to generate key when unlocked",
                 mKeyStore.generate(TEST_KEYNAME));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+    }
+
+    public void testGenerate_grantedUid_Wifi_Success() throws Exception {
+        assertTrue(mKeyStore.password(TEST_PASSWD));
+
+        assertTrue("Should be able to generate key when unlocked",
+                mKeyStore.generate(TEST_KEYNAME, Process.WIFI_UID));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME));
+    }
+
+    public void testGenerate_ungrantedUid_Bluetooth_Failure() throws Exception {
+        assertTrue(mKeyStore.password(TEST_PASSWD));
+
+        assertFalse(mKeyStore.generate(TEST_KEYNAME, Process.BLUETOOTH_UID));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME));
     }
 
     public void testImport_Success() throws Exception {
-        mKeyStore.password(TEST_PASSWD);
+        assertTrue(mKeyStore.password(TEST_PASSWD));
 
         assertTrue("Should be able to import key when unlocked",
                 mKeyStore.importKey(TEST_KEYNAME, PRIVKEY_BYTES));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+    }
+
+    public void testImport_grantedUid_Wifi_Success() throws Exception {
+        assertTrue(mKeyStore.password(TEST_PASSWD));
+
+        assertTrue("Should be able to import key when unlocked",
+                mKeyStore.importKey(TEST_KEYNAME, PRIVKEY_BYTES, Process.WIFI_UID));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME));
+    }
+
+    public void testImport_ungrantedUid_Bluetooth_Failure() throws Exception {
+        assertTrue(mKeyStore.password(TEST_PASSWD));
+
+        assertFalse(mKeyStore.importKey(TEST_KEYNAME, PRIVKEY_BYTES, Process.BLUETOOTH_UID));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME));
     }
 
     public void testImport_Failure_BadEncoding() throws Exception {
@@ -257,12 +400,15 @@
 
         assertFalse("Invalid DER-encoded key should not be imported",
                 mKeyStore.importKey(TEST_KEYNAME, TEST_DATA));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
     }
 
     public void testSign_Success() throws Exception {
         mKeyStore.password(TEST_PASSWD);
 
         assertTrue(mKeyStore.generate(TEST_KEYNAME));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME));
         final byte[] signature = mKeyStore.sign(TEST_KEYNAME, TEST_DATA);
 
         assertNotNull("Signature should not be null", signature);
@@ -272,6 +418,7 @@
         mKeyStore.password(TEST_PASSWD);
 
         assertTrue(mKeyStore.generate(TEST_KEYNAME));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME));
         final byte[] signature = mKeyStore.sign(TEST_KEYNAME, TEST_DATA);
 
         assertNotNull("Signature should not be null", signature);
@@ -406,6 +553,38 @@
                 mKeyStore.ungrant(TEST_KEYNAME, 0));
     }
 
+    public void testMigrate_grantedUid_Wifi_Success() throws Exception {
+        assertTrue(mKeyStore.password(TEST_PASSWD));
+
+        assertFalse(mKeyStore.contains(TEST_KEYNAME));
+
+        assertTrue(mKeyStore.generate(TEST_KEYNAME));
+
+        assertTrue(mKeyStore.contains(TEST_KEYNAME));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+
+        assertTrue(mKeyStore.migrate(TEST_KEYNAME, Process.WIFI_UID));
+
+        assertFalse(mKeyStore.contains(TEST_KEYNAME));
+        assertTrue(mKeyStore.contains(TEST_KEYNAME, Process.WIFI_UID));
+    }
+
+    public void testMigrate_ungrantedUid_Bluetooth_Failure() throws Exception {
+        assertTrue(mKeyStore.password(TEST_PASSWD));
+
+        assertFalse(mKeyStore.contains(TEST_KEYNAME));
+
+        assertTrue(mKeyStore.generate(TEST_KEYNAME));
+
+        assertTrue(mKeyStore.contains(TEST_KEYNAME));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
+
+        assertFalse(mKeyStore.migrate(TEST_KEYNAME, Process.BLUETOOTH_UID));
+
+        assertTrue(mKeyStore.contains(TEST_KEYNAME));
+        assertFalse(mKeyStore.contains(TEST_KEYNAME, Process.BLUETOOTH_UID));
+    }
+
     /**
      * The amount of time to allow before and after expected time for variance
      * in timing tests.
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index f0a5c28..3e4d6f3 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -42,6 +42,8 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
 import android.database.ContentObserver;
 import android.media.MediaPlayer.OnCompletionListener;
 import android.media.MediaPlayer.OnErrorListener;
@@ -70,10 +72,14 @@
 import android.view.VolumePanel;
 
 import com.android.internal.telephony.ITelephony;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.lang.reflect.Field;
 import java.util.ArrayList;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.HashMap;
@@ -197,28 +203,12 @@
 
     /* Sound effect file names  */
     private static final String SOUND_EFFECTS_PATH = "/media/audio/ui/";
-    private static final String[] SOUND_EFFECT_FILES = new String[] {
-        "Effect_Tick.ogg",
-        "KeypressStandard.ogg",
-        "KeypressSpacebar.ogg",
-        "KeypressDelete.ogg",
-        "KeypressReturn.ogg"
-    };
+    private static final List<String> SOUND_EFFECT_FILES = new ArrayList<String>();
 
     /* Sound effect file name mapping sound effect id (AudioManager.FX_xxx) to
      * file index in SOUND_EFFECT_FILES[] (first column) and indicating if effect
      * uses soundpool (second column) */
-    private final int[][] SOUND_EFFECT_FILES_MAP = new int[][] {
-        {0, -1},  // FX_KEY_CLICK
-        {0, -1},  // FX_FOCUS_NAVIGATION_UP
-        {0, -1},  // FX_FOCUS_NAVIGATION_DOWN
-        {0, -1},  // FX_FOCUS_NAVIGATION_LEFT
-        {0, -1},  // FX_FOCUS_NAVIGATION_RIGHT
-        {1, -1},  // FX_KEYPRESS_STANDARD
-        {2, -1},  // FX_KEYPRESS_SPACEBAR
-        {3, -1},  // FX_FOCUS_DELETE
-        {4, -1}   // FX_FOCUS_RETURN
-    };
+    private final int[][] SOUND_EFFECT_FILES_MAP = new int[AudioManager.NUM_SOUND_EFFECTS][2];
 
    /** @hide Maximum volume index values for audio streams */
     private final int[] MAX_STREAM_VOLUME = new int[] {
@@ -1634,6 +1624,99 @@
         return mMode;
     }
 
+    //==========================================================================================
+    // Sound Effects
+    //==========================================================================================
+
+    private static final String TAG_AUDIO_ASSETS = "audio_assets";
+    private static final String ATTR_VERSION = "version";
+    private static final String TAG_GROUP = "group";
+    private static final String ATTR_GROUP_NAME = "name";
+    private static final String TAG_ASSET = "asset";
+    private static final String ATTR_ASSET_ID = "id";
+    private static final String ATTR_ASSET_FILE = "file";
+
+    private static final String ASSET_FILE_VERSION = "1.0";
+    private static final String GROUP_TOUCH_SOUNDS = "touch_sounds";
+
+    private void loadTouchSoundAssetDefaults() {
+        SOUND_EFFECT_FILES.add("Effect_Tick.ogg");
+        for (int i = 0; i < AudioManager.NUM_SOUND_EFFECTS; i++) {
+            SOUND_EFFECT_FILES_MAP[i][0] = 0;
+            SOUND_EFFECT_FILES_MAP[i][1] = -1;
+        }
+    }
+
+    private void loadTouchSoundAssets() {
+        XmlResourceParser parser = null;
+
+        loadTouchSoundAssetDefaults();
+
+        try {
+            parser = mContext.getResources().getXml(com.android.internal.R.xml.audio_assets);
+
+            XmlUtils.beginDocument(parser, TAG_AUDIO_ASSETS);
+            String version = parser.getAttributeValue(null, ATTR_VERSION);
+            boolean inTouchSoundsGroup = false;
+
+            if (ASSET_FILE_VERSION.equals(version)) {
+                while (true) {
+                    XmlUtils.nextElement(parser);
+                    String element = parser.getName();
+                    if (element == null) {
+                        break;
+                    }
+                    if (element.equals(TAG_GROUP)) {
+                        String name = parser.getAttributeValue(null, ATTR_GROUP_NAME);
+                        if (GROUP_TOUCH_SOUNDS.equals(name)) {
+                            inTouchSoundsGroup = true;
+                            break;
+                        }
+                    }
+                }
+                while (inTouchSoundsGroup) {
+                    XmlUtils.nextElement(parser);
+                    String element = parser.getName();
+                    if (element == null) {
+                        break;
+                    }
+                    if (element.equals(TAG_ASSET)) {
+                        String id = parser.getAttributeValue(null, ATTR_ASSET_ID);
+                        String file = parser.getAttributeValue(null, ATTR_ASSET_FILE);
+                        int fx;
+
+                        try {
+                            Field field = AudioManager.class.getField(id);
+                            fx = field.getInt(null);
+                        } catch (Exception e) {
+                            Log.w(TAG, "Invalid touch sound ID: "+id);
+                            continue;
+                        }
+
+                        int i = SOUND_EFFECT_FILES.indexOf(file);
+                        if (i == -1) {
+                            i = SOUND_EFFECT_FILES.size();
+                            SOUND_EFFECT_FILES.add(file);
+                        }
+                        SOUND_EFFECT_FILES_MAP[fx][0] = i;
+                    } else {
+                        break;
+                    }
+                }
+            }
+        } catch (Resources.NotFoundException e) {
+            Log.w(TAG, "audio assets file not found", e);
+        } catch (XmlPullParserException e) {
+            Log.w(TAG, "XML parser exception reading touch sound assets", e);
+        } catch (IOException e) {
+            Log.w(TAG, "I/O exception reading touch sound assets", e);
+        } finally {
+            if (parser != null) {
+                parser.close();
+            }
+        }
+    }
+
     /** @see AudioManager#playSoundEffect(int) */
     public void playSoundEffect(int effectType) {
         sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SENDMSG_NOOP,
@@ -1654,6 +1737,8 @@
     public boolean loadSoundEffects() {
         int status;
 
+        loadTouchSoundAssets();
+
         synchronized (mSoundEffectsLock) {
             if (!mBootCompleted) {
                 Log.w(TAG, "loadSoundEffects() called before boot complete");
@@ -1692,8 +1777,8 @@
              * Once loaded, the value in poolId is the sample ID and the same
              * sample can be reused for another effect using the same file.
              */
-            int[] poolId = new int[SOUND_EFFECT_FILES.length];
-            for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) {
+            int[] poolId = new int[SOUND_EFFECT_FILES.size()];
+            for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.size(); fileIdx++) {
                 poolId[fileIdx] = -1;
             }
             /*
@@ -1711,7 +1796,7 @@
                 if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == -1) {
                     String filePath = Environment.getRootDirectory()
                             + SOUND_EFFECTS_PATH
-                            + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effect][0]];
+                            + SOUND_EFFECT_FILES.get(SOUND_EFFECT_FILES_MAP[effect][0]);
                     int sampleId = mSoundPool.load(filePath, 0);
                     if (sampleId <= 0) {
                         Log.w(TAG, "Soundpool could not load file: "+filePath);
@@ -1776,8 +1861,8 @@
             mAudioHandler.removeMessages(MSG_LOAD_SOUND_EFFECTS);
             mAudioHandler.removeMessages(MSG_PLAY_SOUND_EFFECT);
 
-            int[] poolId = new int[SOUND_EFFECT_FILES.length];
-            for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) {
+            int[] poolId = new int[SOUND_EFFECT_FILES.size()];
+            for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.size(); fileIdx++) {
                 poolId[fileIdx] = 0;
             }
 
@@ -3279,7 +3364,8 @@
                 } else {
                     MediaPlayer mediaPlayer = new MediaPlayer();
                     try {
-                        String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effectType][0]];
+                        String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH +
+                                    SOUND_EFFECT_FILES.get(SOUND_EFFECT_FILES_MAP[effectType][0]);
                         mediaPlayer.setDataSource(filePath);
                         mediaPlayer.setAudioStreamType(AudioSystem.STREAM_SYSTEM);
                         mediaPlayer.prepare();