Merge "Change the initial disabled state of disabled IMEs" into jb-mr2-dev
diff --git a/api/current.txt b/api/current.txt
index 3418761..1166546 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -11497,6 +11497,10 @@
     field public static final int QUALITY_MEDIUM = 1; // 0x1
   }
 
+  public final class DeniedByServerException extends android.media.MediaDrmException {
+    ctor public DeniedByServerException(java.lang.String);
+  }
+
   public class ExifInterface {
     ctor public ExifInterface(java.lang.String) throws java.io.IOException;
     method public double getAltitude(double);
@@ -11818,18 +11822,18 @@
   }
 
   public final class MediaDrm {
-    ctor public MediaDrm(java.util.UUID) throws android.media.MediaDrmException;
+    ctor public MediaDrm(java.util.UUID) throws android.media.UnsupportedSchemeException;
     method public void closeSession(byte[]);
     method public android.media.MediaDrm.CryptoSession getCryptoSession(byte[], java.lang.String, java.lang.String);
-    method public android.media.MediaDrm.KeyRequest getKeyRequest(byte[], byte[], java.lang.String, int, java.util.HashMap<java.lang.String, java.lang.String>);
+    method public android.media.MediaDrm.KeyRequest getKeyRequest(byte[], byte[], java.lang.String, int, java.util.HashMap<java.lang.String, java.lang.String>) throws android.media.NotProvisionedException;
     method public byte[] getPropertyByteArray(java.lang.String);
     method public java.lang.String getPropertyString(java.lang.String);
     method public android.media.MediaDrm.ProvisionRequest getProvisionRequest();
     method public java.util.List<byte[]> getSecureStops();
     method public static final boolean isCryptoSchemeSupported(java.util.UUID);
-    method public byte[] openSession();
-    method public byte[] provideKeyResponse(byte[], byte[]);
-    method public void provideProvisionResponse(byte[]);
+    method public byte[] openSession() throws android.media.NotProvisionedException;
+    method public byte[] provideKeyResponse(byte[], byte[]) throws android.media.DeniedByServerException, android.media.NotProvisionedException;
+    method public void provideProvisionResponse(byte[]) throws android.media.DeniedByServerException;
     method public java.util.HashMap<java.lang.String, java.lang.String> queryKeyStatus(byte[]);
     method public final void release();
     method public void releaseSecureStops(byte[]);
@@ -11873,7 +11877,7 @@
     method public java.lang.String getDefaultUrl();
   }
 
-  public final class MediaDrmException extends java.lang.Exception {
+  public class MediaDrmException extends java.lang.Exception {
     ctor public MediaDrmException(java.lang.String);
   }
 
@@ -12339,6 +12343,10 @@
     field public static final int SYNC_EVENT_PRESENTATION_COMPLETE = 1; // 0x1
   }
 
+  public final class NotProvisionedException extends android.media.MediaDrmException {
+    ctor public NotProvisionedException(java.lang.String);
+  }
+
   public class RemoteControlClient {
     ctor public RemoteControlClient(android.app.PendingIntent);
     ctor public RemoteControlClient(android.app.PendingIntent, android.os.Looper);
@@ -12581,6 +12589,10 @@
     field public static final int TONE_SUP_RINGTONE = 23; // 0x17
   }
 
+  public final class UnsupportedSchemeException extends android.media.MediaDrmException {
+    ctor public UnsupportedSchemeException(java.lang.String);
+  }
+
 }
 
 package android.media.audiofx {
diff --git a/core/java/android/content/RestrictionEntry.java b/core/java/android/content/RestrictionEntry.java
index 217cf76..283a097 100644
--- a/core/java/android/content/RestrictionEntry.java
+++ b/core/java/android/content/RestrictionEntry.java
@@ -109,6 +109,7 @@
      */
     public RestrictionEntry(String key, String selectedString) {
         this.key = key;
+        this.type = TYPE_CHOICE;
         this.currentValue = selectedString;
     }
 
@@ -119,6 +120,7 @@
      */
     public RestrictionEntry(String key, boolean selectedState) {
         this.key = key;
+        this.type = TYPE_BOOLEAN;
         setSelectedState(selectedState);
     }
 
@@ -129,6 +131,7 @@
      */
     public RestrictionEntry(String key, String[] selectedStrings) {
         this.key = key;
+        this.type = TYPE_MULTI_SELECT;
         this.currentValues = selectedStrings;
     }
 
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index acb3725..34e0c12 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -287,7 +287,10 @@
         pi.sharedUserLabel = p.mSharedUserLabel;
         pi.applicationInfo = generateApplicationInfo(p, flags, state, userId);
         pi.installLocation = p.installLocation;
-        pi.requiredForAllUsers = p.mRequiredForAllUsers;
+        if ((pi.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) != 0
+                || (pi.applicationInfo.flags&ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
+            pi.requiredForAllUsers = p.mRequiredForAllUsers;
+        }
         pi.restrictedAccountType = p.mRestrictedAccountType;
         pi.requiredAccountType = p.mRequiredAccountType;
         pi.firstInstallTime = firstInstallTime;
@@ -1812,11 +1815,12 @@
                     false)) {
                 ai.flags |= ApplicationInfo.FLAG_PERSISTENT;
             }
-            if (sa.getBoolean(
-                    com.android.internal.R.styleable.AndroidManifestApplication_requiredForAllUsers,
-                    false)) {
-                owner.mRequiredForAllUsers = true;
-            }
+        }
+
+        if (sa.getBoolean(
+                com.android.internal.R.styleable.AndroidManifestApplication_requiredForAllUsers,
+                false)) {
+            owner.mRequiredForAllUsers = true;
         }
 
         String restrictedAccountType = sa.getString(com.android.internal.R.styleable
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index 0017c46a..4f59e8e 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -1077,7 +1077,7 @@
         printer.println("  isPrimaryConnection: " + mIsPrimaryConnection);
         printer.println("  onlyAllowReadOnlyOperations: " + mOnlyAllowReadOnlyOperations);
 
-        mRecentOperations.dump(printer);
+        mRecentOperations.dump(printer, verbose);
 
         if (verbose) {
             mPreparedStatementCache.dump(printer);
@@ -1376,7 +1376,7 @@
         private void logOperationLocked(int cookie, String detail) {
             final Operation operation = getOperationLocked(cookie);
             StringBuilder msg = new StringBuilder();
-            operation.describe(msg);
+            operation.describe(msg, false);
             if (detail != null) {
                 msg.append(", ").append(detail);
             }
@@ -1399,14 +1399,14 @@
                 final Operation operation = mOperations[mIndex];
                 if (operation != null && !operation.mFinished) {
                     StringBuilder msg = new StringBuilder();
-                    operation.describe(msg);
+                    operation.describe(msg, false);
                     return msg.toString();
                 }
                 return null;
             }
         }
 
-        public void dump(Printer printer) {
+        public void dump(Printer printer, boolean verbose) {
             synchronized (mOperations) {
                 printer.println("  Most recently executed operations:");
                 int index = mIndex;
@@ -1418,7 +1418,7 @@
                         msg.append("    ").append(n).append(": [");
                         msg.append(operation.getFormattedStartTime());
                         msg.append("] ");
-                        operation.describe(msg);
+                        operation.describe(msg, verbose);
                         printer.println(msg.toString());
 
                         if (index > 0) {
@@ -1449,7 +1449,7 @@
         public Exception mException;
         public int mCookie;
 
-        public void describe(StringBuilder msg) {
+        public void describe(StringBuilder msg, boolean verbose) {
             msg.append(mKind);
             if (mFinished) {
                 msg.append(" took ").append(mEndTime - mStartTime).append("ms");
@@ -1461,7 +1461,7 @@
             if (mSql != null) {
                 msg.append(", sql=\"").append(trimSqlForDisplay(mSql)).append("\"");
             }
-            if (mBindArgs != null && mBindArgs.size() != 0) {
+            if (verbose && mBindArgs != null && mBindArgs.size() != 0) {
                 msg.append(", bindArgs=[");
                 final int count = mBindArgs.size();
                 for (int i = 0; i < count; i++) {
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index 4989c3a..e0786f7 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -34,19 +34,21 @@
 
     private static native int nativeCreateFromSurfaceTexture(SurfaceTexture surfaceTexture)
             throws OutOfResourcesException;
+    private static native int nativeCreateFromSurfaceControl(int surfaceControlNativeObject);
 
-    private native Canvas nativeLockCanvas(int nativeObject, Rect dirty);
-    private native void nativeUnlockCanvasAndPost(int nativeObject, Canvas canvas);
+    private static native void nativeLockCanvas(int nativeObject, Canvas canvas, Rect dirty)
+            throws OutOfResourcesException;
+    private static native void nativeUnlockCanvasAndPost(int nativeObject, Canvas canvas);
 
     private static native void nativeRelease(int nativeObject);
     private static native boolean nativeIsValid(int nativeObject);
     private static native boolean nativeIsConsumerRunningBehind(int nativeObject);
-    private static native int nativeCopyFrom(int nativeObject, int surfaceControlNativeObject);
     private static native int nativeReadFromParcel(int nativeObject, Parcel source);
     private static native void nativeWriteToParcel(int nativeObject, Parcel dest);
 
     public static final Parcelable.Creator<Surface> CREATOR =
             new Parcelable.Creator<Surface>() {
+        @Override
         public Surface createFromParcel(Parcel source) {
             try {
                 Surface s = new Surface();
@@ -57,26 +59,20 @@
                 return null;
             }
         }
+
+        @Override
         public Surface[] newArray(int size) {
             return new Surface[size];
         }
     };
 
     private final CloseGuard mCloseGuard = CloseGuard.get();
+
+    // Guarded state.
+    final Object mLock = new Object(); // protects the native state
     private String mName;
-
-    // Note: These fields are accessed by native code.
-    // The mSurfaceControl will only be present for Surfaces used by the window
-    // server or system processes. When this class is parceled we defer to the
-    // mSurfaceControl to do the parceling. Otherwise we parcel the
-    // mNativeSurface.
     int mNativeObject; // package scope only for SurfaceControl access
-
-    // protects the native state
-    private final Object mNativeObjectLock = new Object();
-
-    private int mGenerationId; // incremented each time mNativeSurface changes
-    @SuppressWarnings("UnusedDeclaration")
+    private int mGenerationId; // incremented each time mNativeObject changes
     private final Canvas mCanvas = new CompatibleCanvas();
 
     // A matrix to scale the matrix set by application. This is set to null for
@@ -125,21 +121,22 @@
             throw new IllegalArgumentException("surfaceTexture must not be null");
         }
 
-        mName = surfaceTexture.toString();
-        try {
-            mNativeObject = nativeCreateFromSurfaceTexture(surfaceTexture);
-        } catch (OutOfResourcesException ex) {
-            // We can't throw OutOfResourcesException because it would be an API change.
-            throw new RuntimeException(ex);
+        synchronized (mLock) {
+            mName = surfaceTexture.toString();
+            try {
+                setNativeObjectLocked(nativeCreateFromSurfaceTexture(surfaceTexture));
+            } catch (OutOfResourcesException ex) {
+                // We can't throw OutOfResourcesException because it would be an API change.
+                throw new RuntimeException(ex);
+            }
         }
-
-        mCloseGuard.open("release");
     }
 
     /* called from android_view_Surface_createFromIGraphicBufferProducer() */
     private Surface(int nativeObject) {
-        mNativeObject = nativeObject;
-        mCloseGuard.open("release");
+        synchronized (mLock) {
+            setNativeObjectLocked(nativeObject);
+        }
     }
 
     @Override
@@ -160,13 +157,11 @@
      * This will make the surface invalid.
      */
     public void release() {
-        synchronized (mNativeObjectLock) {
+        synchronized (mLock) {
             if (mNativeObject != 0) {
                 nativeRelease(mNativeObject);
-                mNativeObject = 0;
-                mGenerationId++;
+                setNativeObjectLocked(0);
             }
-            mCloseGuard.close();
         }
     }
 
@@ -187,7 +182,7 @@
      * Otherwise returns false.
      */
     public boolean isValid() {
-        synchronized (mNativeObjectLock) {
+        synchronized (mLock) {
             if (mNativeObject == 0) return false;
             return nativeIsValid(mNativeObject);
         }
@@ -201,7 +196,9 @@
      * @hide
      */
     public int getGenerationId() {
-        return mGenerationId;
+        synchronized (mLock) {
+            return mGenerationId;
+        }
     }
 
     /**
@@ -211,7 +208,7 @@
      * @hide
      */
     public boolean isConsumerRunningBehind() {
-        synchronized (mNativeObjectLock) {
+        synchronized (mLock) {
             checkNotReleasedLocked();
             return nativeIsConsumerRunningBehind(mNativeObject);
         }
@@ -234,9 +231,10 @@
      */
     public Canvas lockCanvas(Rect inOutDirty)
             throws OutOfResourcesException, IllegalArgumentException {
-        synchronized (mNativeObjectLock) {
+        synchronized (mLock) {
             checkNotReleasedLocked();
-            return nativeLockCanvas(mNativeObject, inOutDirty);
+            nativeLockCanvas(mNativeObject, mCanvas, inOutDirty);
+            return mCanvas;
         }
     }
 
@@ -247,7 +245,12 @@
      * @param canvas The canvas previously obtained from {@link #lockCanvas}.
      */
     public void unlockCanvasAndPost(Canvas canvas) {
-        synchronized (mNativeObjectLock) {
+        if (canvas != mCanvas) {
+            throw new IllegalArgumentException("canvas object must be the same instance that "
+                    + "was previously returned by lockCanvas");
+        }
+
+        synchronized (mLock) {
             checkNotReleasedLocked();
             nativeUnlockCanvasAndPost(mNativeObject, canvas);
         }
@@ -273,7 +276,6 @@
         }
     }
 
-
     /**
      * Copy another surface to this one.  This surface now holds a reference
      * to the same data as the original surface, and is -not- the owner.
@@ -287,22 +289,24 @@
         if (other == null) {
             throw new IllegalArgumentException("other must not be null");
         }
-        if (other.mNativeObject == 0) {
+
+        int surfaceControlPtr = other.mNativeObject;
+        if (surfaceControlPtr == 0) {
             throw new NullPointerException(
                     "SurfaceControl native object is null. Are you using a released SurfaceControl?");
         }
-        synchronized (mNativeObjectLock) {
-            mNativeObject = nativeCopyFrom(mNativeObject, other.mNativeObject);
-            if (mNativeObject == 0) {
-                // nativeCopyFrom released our reference
-                mCloseGuard.close();
+        int newNativeObject = nativeCreateFromSurfaceControl(surfaceControlPtr);
+
+        synchronized (mLock) {
+            if (mNativeObject != 0) {
+                nativeRelease(mNativeObject);
             }
-            mGenerationId++;
+            setNativeObjectLocked(newNativeObject);
         }
     }
 
     /**
-     * This is intended to be used by {@link SurfaceView.updateWindow} only.
+     * This is intended to be used by {@link SurfaceView#updateWindow} only.
      * @param other access is not thread safe
      * @hide
      * @deprecated
@@ -313,21 +317,18 @@
             throw new IllegalArgumentException("other must not be null");
         }
         if (other != this) {
-            synchronized (mNativeObjectLock) {
+            final int newPtr;
+            synchronized (other.mLock) {
+                newPtr = other.mNativeObject;
+                other.setNativeObjectLocked(0);
+            }
+
+            synchronized (mLock) {
                 if (mNativeObject != 0) {
-                    // release our reference to our native object
                     nativeRelease(mNativeObject);
                 }
-                // transfer the reference from other to us
-                if (other.mNativeObject != 0 && mNativeObject == 0) {
-                    mCloseGuard.open("release");
-                }
-                mNativeObject = other.mNativeObject;
-                mGenerationId++;
+                setNativeObjectLocked(newPtr);
             }
-            other.mNativeObject = 0;
-            other.mGenerationId++;
-            other.mCloseGuard.close();
         }
     }
 
@@ -340,14 +341,10 @@
         if (source == null) {
             throw new IllegalArgumentException("source must not be null");
         }
-        synchronized (mNativeObjectLock) {
+
+        synchronized (mLock) {
             mName = source.readString();
-            int nativeObject = nativeReadFromParcel(mNativeObject, source);
-            if (nativeObject !=0 && mNativeObject == 0) {
-                mCloseGuard.open("release");
-            }
-            mNativeObject = nativeObject;
-            mGenerationId++;
+            setNativeObjectLocked(nativeReadFromParcel(mNativeObject, source));
         }
     }
 
@@ -356,7 +353,7 @@
         if (dest == null) {
             throw new IllegalArgumentException("dest must not be null");
         }
-        synchronized (mNativeObjectLock) {
+        synchronized (mLock) {
             dest.writeString(mName);
             nativeWriteToParcel(mNativeObject, dest);
         }
@@ -367,7 +364,27 @@
 
     @Override
     public String toString() {
-        return "Surface(name=" + mName + ")";
+        synchronized (mLock) {
+            return "Surface(name=" + mName + ")";
+        }
+    }
+
+    private void setNativeObjectLocked(int ptr) {
+        if (mNativeObject != ptr) {
+            if (mNativeObject == 0 && ptr != 0) {
+                mCloseGuard.open("release");
+            } else if (mNativeObject != 0 && ptr == 0) {
+                mCloseGuard.close();
+            }
+            mNativeObject = ptr;
+            mGenerationId += 1;
+        }
+    }
+
+    private void checkNotReleasedLocked() {
+        if (mNativeObject == 0) {
+            throw new IllegalStateException("Surface has already been released.");
+        }
     }
 
     /**
@@ -451,9 +468,4 @@
             mOrigMatrix.set(m);
         }
     }
-
-    private void checkNotReleasedLocked() {
-        if (mNativeObject == 0) throw new NullPointerException(
-                "mNativeObject is null. Have you called release() already?");
-    }
 }
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index e869d09..6b530ef 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -496,8 +496,14 @@
         if (displayToken == null) {
             throw new IllegalArgumentException("displayToken must not be null");
         }
-        int nativeSurface = surface != null ? surface.mNativeObject : 0;
-        nativeSetDisplaySurface(displayToken, nativeSurface);
+
+        if (surface != null) {
+            synchronized (surface.mLock) {
+                nativeSetDisplaySurface(displayToken, surface.mNativeObject);
+            }
+        } else {
+            nativeSetDisplaySurface(displayToken, 0);
+        }
     }
 
     public static IBinder createDisplay(String name, boolean secure) {
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index a41a389..9a19ce5 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -55,8 +55,7 @@
 static struct {
     jclass clazz;
     jfieldID mNativeObject;
-    jfieldID mNativeObjectLock;
-    jfieldID mCanvas;
+    jfieldID mLock;
     jmethodID ctor;
 } gSurfaceClassInfo;
 
@@ -93,7 +92,7 @@
 sp<Surface> android_view_Surface_getSurface(JNIEnv* env, jobject surfaceObj) {
     sp<Surface> sur;
     jobject lock = env->GetObjectField(surfaceObj,
-            gSurfaceClassInfo.mNativeObjectLock);
+            gSurfaceClassInfo.mLock);
     if (env->MonitorEnter(lock) == JNI_OK) {
         sur = reinterpret_cast<Surface *>(
                 env->GetIntField(surfaceObj, gSurfaceClassInfo.mNativeObject));
@@ -200,12 +199,13 @@
   SkSafeUnref(previousCanvas);
 }
 
-static jobject nativeLockCanvas(JNIEnv* env, jobject surfaceObj, jint nativeObject, jobject dirtyRectObj) {
+static void nativeLockCanvas(JNIEnv* env, jclass clazz,
+        jint nativeObject, jobject canvasObj, jobject dirtyRectObj) {
     sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));
 
     if (!isSurfaceValid(surface)) {
         doThrowIAE(env);
-        return NULL;
+        return;
     }
 
     // get dirty region
@@ -232,11 +232,10 @@
                 OutOfResourcesException :
                 "java/lang/IllegalArgumentException";
         jniThrowException(env, exception, NULL);
-        return NULL;
+        return;
     }
 
     // Associate a SkCanvas object to this surface
-    jobject canvasObj = env->GetObjectField(surfaceObj, gSurfaceClassInfo.mCanvas);
     env->SetIntField(canvasObj, gCanvasClassInfo.mSurfaceFormat, outBuffer.format);
 
     SkBitmap bitmap;
@@ -277,17 +276,10 @@
         env->SetIntField(dirtyRectObj, gRectClassInfo.right, bounds.right);
         env->SetIntField(dirtyRectObj, gRectClassInfo.bottom, bounds.bottom);
     }
-
-    return canvasObj;
 }
 
-static void nativeUnlockCanvasAndPost(JNIEnv* env, jobject surfaceObj, jint nativeObject, jobject canvasObj) {
-    jobject ownCanvasObj = env->GetObjectField(surfaceObj, gSurfaceClassInfo.mCanvas);
-    if (!env->IsSameObject(ownCanvasObj, canvasObj)) {
-        doThrowIAE(env);
-        return;
-    }
-
+static void nativeUnlockCanvasAndPost(JNIEnv* env, jclass clazz,
+        jint nativeObject, jobject canvasObj) {
     sp<Surface> surface(reinterpret_cast<Surface *>(nativeObject));
     if (!isSurfaceValid(surface)) {
         return;
@@ -306,8 +298,8 @@
 
 // ----------------------------------------------------------------------------
 
-static jint nativeCopyFrom(JNIEnv* env, jclass clazz,
-        jint nativeObject, jint surfaceControlNativeObj) {
+static jint nativeCreateFromSurfaceControl(JNIEnv* env, jclass clazz,
+        jint surfaceControlNativeObj) {
     /*
      * This is used by the WindowManagerService just after constructing
      * a Surface and is necessary for returning the Surface reference to
@@ -315,17 +307,11 @@
      */
 
     sp<SurfaceControl> ctrl(reinterpret_cast<SurfaceControl *>(surfaceControlNativeObj));
-    sp<Surface> other(ctrl->getSurface());
-    if (other != NULL) {
-        other->incStrong(&sRefBaseOwner);
+    sp<Surface> surface(ctrl->getSurface());
+    if (surface != NULL) {
+        surface->incStrong(&sRefBaseOwner);
     }
-
-    sp<Surface> sur(reinterpret_cast<Surface *>(nativeObject));
-    if (sur != NULL) {
-        sur->decStrong(&sRefBaseOwner);
-    }
-
-    return int(other.get());
+    return reinterpret_cast<jint>(surface.get());
 }
 
 static jint nativeReadFromParcel(JNIEnv* env, jclass clazz,
@@ -386,12 +372,12 @@
             (void*)nativeIsValid },
     {"nativeIsConsumerRunningBehind", "(I)Z",
             (void*)nativeIsConsumerRunningBehind },
-    {"nativeLockCanvas", "(ILandroid/graphics/Rect;)Landroid/graphics/Canvas;",
+    {"nativeLockCanvas", "(ILandroid/graphics/Canvas;Landroid/graphics/Rect;)V",
             (void*)nativeLockCanvas },
     {"nativeUnlockCanvasAndPost", "(ILandroid/graphics/Canvas;)V",
             (void*)nativeUnlockCanvasAndPost },
-    {"nativeCopyFrom", "(II)I",
-            (void*)nativeCopyFrom },
+    {"nativeCreateFromSurfaceControl", "(I)I",
+            (void*)nativeCreateFromSurfaceControl },
     {"nativeReadFromParcel", "(ILandroid/os/Parcel;)I",
             (void*)nativeReadFromParcel },
     {"nativeWriteToParcel", "(ILandroid/os/Parcel;)V",
@@ -407,10 +393,8 @@
     gSurfaceClassInfo.clazz = jclass(env->NewGlobalRef(clazz));
     gSurfaceClassInfo.mNativeObject =
             env->GetFieldID(gSurfaceClassInfo.clazz, "mNativeObject", "I");
-    gSurfaceClassInfo.mNativeObjectLock =
-            env->GetFieldID(gSurfaceClassInfo.clazz, "mNativeObjectLock", "Ljava/lang/Object;");
-    gSurfaceClassInfo.mCanvas =
-            env->GetFieldID(gSurfaceClassInfo.clazz, "mCanvas", "Landroid/graphics/Canvas;");
+    gSurfaceClassInfo.mLock =
+            env->GetFieldID(gSurfaceClassInfo.clazz, "mLock", "Ljava/lang/Object;");
     gSurfaceClassInfo.ctor = env->GetMethodID(gSurfaceClassInfo.clazz, "<init>", "(I)V");
 
     clazz = env->FindClass("android/graphics/Canvas");
diff --git a/docs/html/about/dashboards/index.jd b/docs/html/about/dashboards/index.jd
index e17a0fd..6956634 100644
--- a/docs/html/about/dashboards/index.jd
+++ b/docs/html/about/dashboards/index.jd
@@ -57,7 +57,7 @@
 </div>
 
 
-<p style="clear:both"><em>Data collected during a 14-day period ending on April 2, 2013.
+<p style="clear:both"><em>Data collected during a 14-day period ending on May 1, 2013.
 <br/>Any versions with less than 0.1% distribution are not shown.</em>
 </p>
 
@@ -83,7 +83,7 @@
 </div>
 
 
-<p style="clear:both"><em>Data collected during a 14-day period ending on April 2, 2013
+<p style="clear:both"><em>Data collected during a 14-day period ending on May 1, 2013
 <br/>Any screen configurations with less than 0.1% distribution are not shown.</em></p>
 
 
@@ -102,7 +102,7 @@
 
 
 <img alt="" style="float:right"
-src="//chart.googleapis.com/chart?cht=p&chs=400x250&chco=c4df9b,6fad0c&chl=GL%201.1%20only|GL%202.0%20%26%201.1&chd=t%3A0.3,99.7&chf=bg,s,00000000" />
+src="//chart.googleapis.com/chart?cht=p&chs=400x250&chco=c4df9b,6fad0c&chl=GL%201.1%20only|GL%202.0%20%26%201.1&chd=t%3A0.2,99.8&chf=bg,s,00000000" />
 
 <p>To declare which version of OpenGL ES your application requires, you should use the {@code
 android:glEsVersion} attribute of the <a
@@ -120,17 +120,17 @@
 </tr>
 <tr>
 <td>1.1 only</th>
-<td>0.3%</td>
+<td>0.2%</td>
 </tr>
 <tr>
 <td>2.0 &amp; 1.1</th>
-<td>99.7%</td>
+<td>99.8%</td>
 </tr>
 </table>
 
 
 
-<p style="clear:both"><em>Data collected during a 14-day period ending on April 2, 2013</em></p>
+<p style="clear:both"><em>Data collected during a 14-day period ending on May 1, 2013</em></p>
 
 
 
@@ -148,7 +148,7 @@
 var VERSION_DATA =
 [
   {
-    "chart": "//chart.googleapis.com/chart?chf=bg%2Cs%2C00000000&chd=t%3A1.8%2C4.0%2C39.8%2C0.2%2C29.3%2C25.0&chl=Eclair%7CFroyo%7CGingerbread%7CHoneycomb%7CIce%20Cream%20Sandwich%7CJelly%20Bean&chs=500x250&cht=p&chco=c4df9b%2C6fad0c",
+    "chart": "//chart.googleapis.com/chart?chs=500x250&cht=p&chf=bg%2Cs%2C00000000&chd=t%3A1.8%2C3.7%2C38.5%2C0.1%2C27.5%2C28.4&chco=c4df9b%2C6fad0c&chl=Eclair%7CFroyo%7CGingerbread%7CHoneycomb%7CIce%20Cream%20Sandwich%7CJelly%20Bean",
     "data": [
       {
         "api": 4,
@@ -163,7 +163,7 @@
       {
         "api": 8,
         "name": "Froyo",
-        "perc": "4.0"
+        "perc": "3.7"
       },
       {
         "api": 9,
@@ -173,27 +173,27 @@
       {
         "api": 10,
         "name": "Gingerbread",
-        "perc": "39.7"
+        "perc": "38.4"
       },
       {
         "api": 13,
         "name": "Honeycomb",
-        "perc": "0.2"
+        "perc": "0.1"
       },
       {
         "api": 15,
         "name": "Ice Cream Sandwich",
-        "perc": "29.3"
+        "perc": "27.5"
       },
       {
         "api": 16,
         "name": "Jelly Bean",
-        "perc": "23.0"
+        "perc": "26.1"
       },
       {
         "api": 17,
         "name": "Jelly Bean",
-        "perc": "2.0"
+        "perc": "2.3"
       }
     ]
   }
@@ -205,34 +205,35 @@
 
 var SCREEN_DATA =
 [
+
   {
     "data": {
       "Large": {
-        "hdpi": "0.5",
-        "ldpi": "0.7",
-        "mdpi": "2.7",
+        "hdpi": "0.4",
+        "ldpi": "0.6",
+        "mdpi": "2.9",
         "tvdpi": "1.0",
-        "xhdpi": "0.8"
+        "xhdpi": "0.7"
       },
       "Normal": {
-        "hdpi": "37.9",
+        "hdpi": "37.3",
         "ldpi": "0.1",
         "mdpi": "16.1",
-        "xhdpi": "25.0",
-        "xxhdpi": "0.8"
+        "xhdpi": "24.9",
+        "xxhdpi": "1.3"
       },
       "Small": {
-        "ldpi": "9.5"
+        "ldpi": "9.8"
       },
       "Xlarge": {
         "hdpi": "0.1",
-        "ldpi": "0.1",
-        "mdpi": "4.6",
+        "ldpi": "0.2",
+        "mdpi": "4.5",
         "xhdpi": "0.1"
       }
     },
-    "densitychart": "//chart.googleapis.com/chart?chf=bg%2Cs%2C00000000&chd=t%3A10.4%2C23.4%2C1.0%2C38.5%2C25.9%2C0.8&chl=ldpi%7Cmdpi%7Ctvdpi%7Chdpi%7Cxhdpi%7Cxxhdpi&chs=400x250&cht=p&chco=c4df9b%2C6fad0c",
-    "layoutchart": "//chart.googleapis.com/chart?chf=bg%2Cs%2C00000000&chd=t%3A4.9%2C5.7%2C79.9%2C9.5&chl=Xlarge%7CLarge%7CNormal%7CSmall&chs=400x250&cht=p&chco=c4df9b%2C6fad0c"
+    "densitychart": "//chart.googleapis.com/chart?chs=400x250&cht=p&chf=bg%2Cs%2C00000000&chd=t%3A10.7%2C23.5%2C1.0%2C37.8%2C25.7%2C1.3&chco=c4df9b%2C6fad0c&chl=ldpi%7Cmdpi%7Ctvdpi%7Chdpi%7Cxhdpi%7Cxxhdpi",
+    "layoutchart": "//chart.googleapis.com/chart?chs=400x250&cht=p&chf=bg%2Cs%2C00000000&chd=t%3A4.9%2C5.6%2C79.7%2C9.8&chco=c4df9b%2C6fad0c&chl=Xlarge%7CLarge%7CNormal%7CSmall"
   }
 ];
 
diff --git a/docs/html/training/contacts-provider/display-contact-badge.jd b/docs/html/training/contacts-provider/display-contact-badge.jd
index f08935d..041eb58 100644
--- a/docs/html/training/contacts-provider/display-contact-badge.jd
+++ b/docs/html/training/contacts-provider/display-contact-badge.jd
@@ -169,8 +169,8 @@
     // Gets a content URI for the contact
     mContactUri =
             Contacts.getLookupUri(
-                Cursor.getLong(mIdColumn),
-                Cursor.getString(mLookupKeyColumn)
+                mCursor.getLong(mIdColumn),
+                mCursor.getString(mLookupKeyColumn)
             );
     mBadge.assignContactUri(mContactUri);
 </pre>
@@ -221,7 +221,7 @@
      * Assuming the current Cursor position is the contact you want,
      * gets the thumbnail ID
      */
-    mThumbnailUri = Cursor.getString(mThumbnailColumn);
+    mThumbnailUri = mCursor.getString(mThumbnailColumn);
     ...
 </pre>
 <p>
diff --git a/media/java/android/media/DeniedByServerException.java b/media/java/android/media/DeniedByServerException.java
new file mode 100644
index 0000000..9c1633a
--- /dev/null
+++ b/media/java/android/media/DeniedByServerException.java
@@ -0,0 +1,27 @@
+/*
+ * 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.media;
+
+/**
+ * Exception thrown when the provisioning server or key server denies a
+ * certficate or license for a device.
+ */
+public final class DeniedByServerException extends MediaDrmException {
+    public DeniedByServerException(String detailMessage) {
+        super(detailMessage);
+    }
+}
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index a58fa51..be2d9bc 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -127,11 +127,14 @@
     private static final native boolean isCryptoSchemeSupportedNative(byte[] uuid);
 
     /**
-     * Instantiate a MediaDrm object using opaque, crypto scheme specific
-     * data.
+     * Instantiate a MediaDrm object
+     *
      * @param uuid The UUID of the crypto scheme.
+     *
+     * @throws UnsupportedSchemeException if the device does not support the
+     * specified scheme UUID
      */
-    public MediaDrm(UUID uuid) throws MediaDrmException {
+    public MediaDrm(UUID uuid) throws UnsupportedSchemeException {
         Looper looper;
         if ((looper = Looper.myLooper()) != null) {
             mEventHandler = new EventHandler(this, looper);
@@ -268,8 +271,10 @@
 
     /**
      * Open a new session with the MediaDrm object.  A session ID is returned.
+     *
+     * @throws NotProvisionedException if provisioning is needed
      */
-    public native byte[] openSession();
+    public native byte[] openSession() throws NotProvisionedException;
 
     /**
      * Close a session on the MediaDrm object that was previously opened
@@ -346,10 +351,14 @@
      * keys, which are identified by a keySetId.
      * @param optionalParameters are included in the key request message to
      * allow a client application to provide additional message parameters to the server.
+     *
+     * @throws NotProvisionedException if reprovisioning is needed, due to a
+     * problem with the certifcate
      */
     public native KeyRequest getKeyRequest(byte[] scope, byte[] init,
                                            String mimeType, int keyType,
-                                           HashMap<String, String> optionalParameters);
+                                           HashMap<String, String> optionalParameters)
+        throws NotProvisionedException;
 
 
     /**
@@ -360,8 +369,15 @@
      *
      * @param sessionId the session ID for the DRM session
      * @param response the byte array response from the server
+     *
+     * @throws NotProvisionedException if the response indicates that
+     * reprovisioning is required
+     * @throws DeniedByServerException if the response indicates that the
+     * server rejected the request
      */
-    public native byte[] provideKeyResponse(byte[] sessionId, byte[] response);
+    public native byte[] provideKeyResponse(byte[] sessionId, byte[] response)
+        throws NotProvisionedException, DeniedByServerException;
+
 
     /**
      * Restore persisted offline keys into a new session.  keySetId identifies the
@@ -430,8 +446,12 @@
      *
      * @param response the opaque provisioning response byte array to provide to the
      * DRM engine plugin.
+     *
+     * @throws DeniedByServerException if the response indicates that the
+     * server rejected the request
      */
-    public native void provideProvisionResponse(byte[] response);
+    public native void provideProvisionResponse(byte[] response)
+        throws DeniedByServerException;
 
     /**
      * A means of enforcing limits on the number of concurrent streams per subscriber
diff --git a/media/java/android/media/MediaDrmException.java b/media/java/android/media/MediaDrmException.java
index d6f5ff4..d547574 100644
--- a/media/java/android/media/MediaDrmException.java
+++ b/media/java/android/media/MediaDrmException.java
@@ -17,10 +17,9 @@
 package android.media;
 
 /**
- * Exception thrown if MediaDrm object could not be instantiated for
- * whatever reason.
+ * Base class for MediaDrm exceptions
  */
-public final class MediaDrmException extends Exception {
+public class MediaDrmException extends Exception {
     public MediaDrmException(String detailMessage) {
         super(detailMessage);
     }
diff --git a/media/java/android/media/NotProvisionedException.java b/media/java/android/media/NotProvisionedException.java
new file mode 100644
index 0000000..32b8151
--- /dev/null
+++ b/media/java/android/media/NotProvisionedException.java
@@ -0,0 +1,29 @@
+/*
+ * 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.media;
+
+/**
+ * Exception thrown when an operation on a MediaDrm object is attempted
+ * and the device does not have a certificate.  The app should obtain and
+ * install a certificate using the MediaDrm provisioning methods then retry
+ * the operation.
+ */
+public final class NotProvisionedException extends MediaDrmException {
+    public NotProvisionedException(String detailMessage) {
+        super(detailMessage);
+    }
+}
diff --git a/media/java/android/media/UnsupportedSchemeException.java b/media/java/android/media/UnsupportedSchemeException.java
new file mode 100644
index 0000000..d7b5d47
--- /dev/null
+++ b/media/java/android/media/UnsupportedSchemeException.java
@@ -0,0 +1,27 @@
+/*
+ * 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.media;
+
+/**
+ * Exception thrown when an attempt is made to construct a MediaDrm object
+ * using a crypto scheme UUID that is not supported by the device
+ */
+public final class UnsupportedSchemeException extends MediaDrmException {
+    public UnsupportedSchemeException(String detailMessage) {
+        super(detailMessage);
+    }
+}
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index d1b499e..ec88949 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -219,12 +219,6 @@
     case ERROR_DRM_TAMPER_DETECTED:
         drmMessage = "Invalid state";
         break;
-    case ERROR_DRM_NOT_PROVISIONED:
-        drmMessage = "Not provisioned";
-        break;
-    case ERROR_DRM_DEVICE_REVOKED:
-        drmMessage = "Device revoked";
-        break;
     default:
         break;
     }
@@ -238,6 +232,12 @@
     if (err == BAD_VALUE) {
         jniThrowException(env, "java/lang/IllegalArgumentException", msg);
         return true;
+    } else if (err == ERROR_DRM_NOT_PROVISIONED) {
+        jniThrowException(env, "android/media/NotProvisionedException", msg);
+        return true;
+    } else if (err == ERROR_DRM_DEVICE_REVOKED) {
+        jniThrowException(env, "android/media/DeniedByServerException", msg);
+        return true;
     } else if (err != OK) {
         String8 errbuf;
         if (drmMessage != NULL) {
@@ -248,6 +248,7 @@
                 msg = errbuf.string();
             }
         }
+        ALOGE("Illegal state exception: %s", msg);
         jniThrowException(env, "java/lang/IllegalStateException", msg);
         return true;
     }
@@ -574,7 +575,7 @@
     if (err != OK) {
         jniThrowException(
                 env,
-                "android/media/MediaDrmException",
+                "android/media/UnsupportedSchemeException",
                 "Failed to instantiate drm object.");
         return;
     }
diff --git a/media/jni/audioeffect/android_media_Visualizer.cpp b/media/jni/audioeffect/android_media_Visualizer.cpp
index c2655c7..4d77cfd 100644
--- a/media/jni/audioeffect/android_media_Visualizer.cpp
+++ b/media/jni/audioeffect/android_media_Visualizer.cpp
@@ -164,17 +164,18 @@
 
     visualizer_callback_cookie *callbackInfo = (visualizer_callback_cookie *)user;
     JNIEnv *env = AndroidRuntime::getJNIEnv();
-    AutoMutex lock(&callbackInfo->callback_data_lock);
+
+    if (!user || !env) {
+        ALOGW("captureCallback error user %p, env %p", user, env);
+        return;
+    }
 
     ALOGV("captureCallback: callbackInfo %p, visualizer_ref %p visualizer_class %p",
             callbackInfo,
             callbackInfo->visualizer_ref,
             callbackInfo->visualizer_class);
 
-    if (!user || !env) {
-        ALOGW("captureCallback error user %p, env %p", user, env);
-        return;
-    }
+    AutoMutex lock(&callbackInfo->callback_data_lock);
 
     if (waveformSize != 0 && waveform != NULL) {
         jbyteArray jArray;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index ff36485..f33dc20 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -25,6 +25,7 @@
 import android.util.Slog;
 import android.view.MotionEvent;
 import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
 
 import com.android.systemui.EventLogTags;
 import com.android.systemui.R;
@@ -56,8 +57,6 @@
         mHandleBar = resources.getDrawable(R.drawable.status_bar_close);
         mHandleBarHeight = resources.getDimensionPixelSize(R.dimen.close_handle_height);
         mHandleView = findViewById(R.id.handle);
-
-        setContentDescription(resources.getString(R.string.accessibility_desc_notification_shade));
     }
 
     @Override
@@ -71,6 +70,17 @@
         super.fling(vel, always);
     }
 
+    @Override
+    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+        if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
+            event.getText()
+                    .add(getContext().getString(R.string.accessibility_desc_notification_shade));
+            return true;
+        }
+
+        return super.dispatchPopulateAccessibilityEvent(event);
+    }
+
     // We draw the handle ourselves so that it's always glued to the bottom of the window.
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsPanelView.java
index 2314d93..33335631 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SettingsPanelView.java
@@ -28,6 +28,7 @@
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityEvent;
 
 import com.android.systemui.EventLogTags;
 import com.android.systemui.R;
@@ -62,10 +63,8 @@
         mHandleBar = resources.getDrawable(R.drawable.status_bar_close);
         mHandleBarHeight = resources.getDimensionPixelSize(R.dimen.close_handle_height);
         mHandleView = findViewById(R.id.handle);
-
-        setContentDescription(resources.getString(R.string.accessibility_desc_quick_settings));
     }
-    
+
     public void setQuickSettings(QuickSettings qs) {
         mQS = qs;
     }
@@ -120,6 +119,17 @@
         }
     }
 
+    @Override
+    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+        if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
+            event.getText()
+                    .add(getContext().getString(R.string.accessibility_desc_quick_settings));
+            return true;
+        }
+
+        return super.dispatchPopulateAccessibilityEvent(event);
+    }
+
     // We draw the handle ourselves so that it's always glued to the bottom of the window.
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
diff --git a/services/java/com/android/server/connectivity/Vpn.java b/services/java/com/android/server/connectivity/Vpn.java
index 10c7e27..e7d1fa4 100644
--- a/services/java/com/android/server/connectivity/Vpn.java
+++ b/services/java/com/android/server/connectivity/Vpn.java
@@ -95,7 +95,8 @@
     private Connection mConnection;
     private LegacyVpnRunner mLegacyVpnRunner;
     private PendingIntent mStatusIntent;
-    private boolean mEnableNotif = true;
+    private volatile boolean mEnableNotif = true;
+    private volatile boolean mEnableTeardown = true;
     private final IConnectivityManager mConnService;
 
     public Vpn(Context context, VpnCallback callback, INetworkManagementService netService,
@@ -113,10 +114,23 @@
         }
     }
 
+    /**
+     * Set if this object is responsible for showing its own notifications. When
+     * {@code false}, notifications are handled externally by someone else.
+     */
     public void setEnableNotifications(boolean enableNotif) {
         mEnableNotif = enableNotif;
     }
 
+    /**
+     * Set if this object is responsible for watching for {@link NetworkInfo}
+     * teardown. When {@code false}, teardown is handled externally by someone
+     * else.
+     */
+    public void setEnableTeardown(boolean enableTeardown) {
+        mEnableTeardown = enableTeardown;
+    }
+
     @Override
     protected void startMonitoringInternal() {
         // Ignored; events are sent through callbacks for now
@@ -647,6 +661,8 @@
         private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
+                if (!mEnableTeardown) return;
+
                 if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
                     if (intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE,
                             ConnectivityManager.TYPE_NONE) == mOuterConnection.get()) {
@@ -688,7 +704,6 @@
             IntentFilter filter = new IntentFilter();
             filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
             mContext.registerReceiver(mBroadcastReceiver, filter);
-
         }
 
         public void check(String interfaze) {
diff --git a/services/java/com/android/server/net/LockdownVpnTracker.java b/services/java/com/android/server/net/LockdownVpnTracker.java
index 5b6e485..e251925 100644
--- a/services/java/com/android/server/net/LockdownVpnTracker.java
+++ b/services/java/com/android/server/net/LockdownVpnTracker.java
@@ -128,7 +128,10 @@
             mAcceptedEgressIface = null;
             mVpn.stopLegacyVpn();
         }
-        if (egressDisconnected) return;
+        if (egressDisconnected) {
+            hideNotification();
+            return;
+        }
 
         final int egressType = egressInfo.getType();
         if (vpnInfo.getDetailedState() == DetailedState.FAILED) {
@@ -192,6 +195,7 @@
         Slog.d(TAG, "initLocked()");
 
         mVpn.setEnableNotifications(false);
+        mVpn.setEnableTeardown(false);
 
         final IntentFilter resetFilter = new IntentFilter(ACTION_LOCKDOWN_RESET);
         mContext.registerReceiver(mResetReceiver, resetFilter, CONNECTIVITY_INTERNAL, null);
@@ -235,6 +239,7 @@
 
         mContext.unregisterReceiver(mResetReceiver);
         mVpn.setEnableNotifications(true);
+        mVpn.setEnableTeardown(true);
     }
 
     public void reset() {
diff --git a/services/java/com/android/server/pm/Installer.java b/services/java/com/android/server/pm/Installer.java
index d9c85bf..734d071 100644
--- a/services/java/com/android/server/pm/Installer.java
+++ b/services/java/com/android/server/pm/Installer.java
@@ -307,8 +307,8 @@
         return execute(builder.toString());
     }
 
-    public int getSizeInfo(String pkgName, int persona, String apkPath, String fwdLockApkPath,
-            String asecPath, PackageStats pStats) {
+    public int getSizeInfo(String pkgName, int persona, String apkPath, String libDirPath,
+            String fwdLockApkPath, String asecPath, PackageStats pStats) {
         StringBuilder builder = new StringBuilder("getsize");
         builder.append(' ');
         builder.append(pkgName);
@@ -317,6 +317,8 @@
         builder.append(' ');
         builder.append(apkPath);
         builder.append(' ');
+        builder.append(libDirPath != null ? libDirPath : "!");
+        builder.append(' ');
         builder.append(fwdLockApkPath != null ? fwdLockApkPath : "!");
         builder.append(' ');
         builder.append(asecPath != null ? asecPath : "!");
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index fcea9e7..e804dfa 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -422,8 +422,71 @@
     PackageParser.Package mPlatformPackage;
 
     // Set of pending broadcasts for aggregating enable/disable of components.
-    final HashMap<String, ArrayList<String>> mPendingBroadcasts
-            = new HashMap<String, ArrayList<String>>();
+    static class PendingPackageBroadcasts {
+        // for each user id, a map of <package name -> components within that package>
+        final SparseArray<HashMap<String, ArrayList<String>>> mUidMap;
+
+        public PendingPackageBroadcasts() {
+            mUidMap = new SparseArray<HashMap<String, ArrayList<String>>>();
+        }
+
+        public ArrayList<String> get(int userId, String packageName) {
+            HashMap<String, ArrayList<String>> packages = getOrAllocate(userId);
+            return packages.get(packageName);
+        }
+
+        public void put(int userId, String packageName, ArrayList<String> components) {
+            HashMap<String, ArrayList<String>> packages = getOrAllocate(userId);
+            packages.put(packageName, components);
+        }
+
+        public void remove(int userId, String packageName) {
+            HashMap<String, ArrayList<String>> packages = mUidMap.get(userId);
+            if (packages != null) {
+                packages.remove(packageName);
+            }
+        }
+
+        public void remove(int userId) {
+            mUidMap.remove(userId);
+        }
+
+        public int userIdCount() {
+            return mUidMap.size();
+        }
+
+        public int userIdAt(int n) {
+            return mUidMap.keyAt(n);
+        }
+
+        public HashMap<String, ArrayList<String>> packagesForUserId(int userId) {
+            return mUidMap.get(userId);
+        }
+
+        public int size() {
+            // total number of pending broadcast entries across all userIds
+            int num = 0;
+            for (int i = 0; i< mUidMap.size(); i++) {
+                num += mUidMap.valueAt(i).size();
+            }
+            return num;
+        }
+
+        public void clear() {
+            mUidMap.clear();
+        }
+
+        private HashMap<String, ArrayList<String>> getOrAllocate(int userId) {
+            HashMap<String, ArrayList<String>> map = mUidMap.get(userId);
+            if (map == null) {
+                map = new HashMap<String, ArrayList<String>>();
+                mUidMap.put(userId, map);
+            }
+            return map;
+        }
+    }
+    final PendingPackageBroadcasts mPendingBroadcasts = new PendingPackageBroadcasts();
+
     // Service Connection to remote media container service to copy
     // package uri's from external media onto secure containers
     // or internal storage.
@@ -667,16 +730,23 @@
                         packages = new String[size];
                         components = new ArrayList[size];
                         uids = new int[size];
-                        Iterator<Map.Entry<String, ArrayList<String>>>
-                                it = mPendingBroadcasts.entrySet().iterator();
-                        int i = 0;
-                        while (it.hasNext() && i < size) {
-                            Map.Entry<String, ArrayList<String>> ent = it.next();
-                            packages[i] = ent.getKey();
-                            components[i] = ent.getValue();
-                            PackageSetting ps = mSettings.mPackages.get(ent.getKey());
-                            uids[i] = (ps != null) ? ps.appId : -1;
-                            i++;
+                        int i = 0;  // filling out the above arrays
+
+                        for (int n = 0; n < mPendingBroadcasts.userIdCount(); n++) {
+                            int packageUserId = mPendingBroadcasts.userIdAt(n);
+                            Iterator<Map.Entry<String, ArrayList<String>>> it
+                                    = mPendingBroadcasts.packagesForUserId(packageUserId)
+                                            .entrySet().iterator();
+                            while (it.hasNext() && i < size) {
+                                Map.Entry<String, ArrayList<String>> ent = it.next();
+                                packages[i] = ent.getKey();
+                                components[i] = ent.getValue();
+                                PackageSetting ps = mSettings.mPackages.get(ent.getKey());
+                                uids[i] = (ps != null)
+                                        ? UserHandle.getUid(packageUserId, ps.appId)
+                                        : -1;
+                                i++;
+                            }
                         }
                         size = i;
                         mPendingBroadcasts.clear();
@@ -6055,7 +6125,6 @@
         long callingId = Binder.clearCallingIdentity();
         try {
             boolean sendAdded = false;
-            boolean isSystem = false;
             Bundle extras = new Bundle(1);
 
             // writer
@@ -6069,28 +6138,29 @@
                     mSettings.writePackageRestrictionsLPr(userId);
                     extras.putInt(Intent.EXTRA_UID, UserHandle.getUid(userId, pkgSetting.appId));
                     sendAdded = true;
-                    isSystem = (pkgSetting.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0;
                 }
             }
 
             if (sendAdded) {
                 sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
                         packageName, extras, null, null, new int[] {userId});
-                if (isSystem) {
-                    // The just-installed/enabled app is bundled on the system, so presumed
-                    // to be able to run automatically without needing an explicit launch.
-                    // Send it a BOOT_COMPLETED if it would ordinarily have gotten one.
-                    Intent bcIntent = new Intent(Intent.ACTION_BOOT_COMPLETED)
-                            .addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
-                            .setPackage(packageName);
-                    try {
-                        IActivityManager am = ActivityManagerNative.getDefault();
+                try {
+                    IActivityManager am = ActivityManagerNative.getDefault();
+                    final boolean isSystem =
+                            isSystemApp(pkgSetting) || isUpdatedSystemApp(pkgSetting);
+                    if (isSystem && am.isUserRunning(userId, false)) {
+                        // The just-installed/enabled app is bundled on the system, so presumed
+                        // to be able to run automatically without needing an explicit launch.
+                        // Send it a BOOT_COMPLETED if it would ordinarily have gotten one.
+                        Intent bcIntent = new Intent(Intent.ACTION_BOOT_COMPLETED)
+                                .addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)
+                                .setPackage(packageName);
                         am.broadcastIntent(null, bcIntent, null, null, 0, null, null, null,
                                 android.app.AppOpsManager.OP_NONE, false, false, userId);
-                    } catch (RemoteException e) {
-                        // shouldn't happen
-                        Slog.w(TAG, "Unable to bootstrap installed package", e);
                     }
+                } catch (RemoteException e) {
+                    // shouldn't happen
+                    Slog.w(TAG, "Unable to bootstrap installed package", e);
                 }
             }
         } finally {
@@ -9164,18 +9234,22 @@
         }
         PackageParser.Package p;
         boolean dataOnly = false;
+        String libDirPath = null;
         String asecPath = null;
         synchronized (mPackages) {
             p = mPackages.get(packageName);
+            PackageSetting ps = mSettings.mPackages.get(packageName);
             if(p == null) {
                 dataOnly = true;
-                PackageSetting ps = mSettings.mPackages.get(packageName);
                 if((ps == null) || (ps.pkg == null)) {
                     Slog.w(TAG, "Package named '" + packageName +"' doesn't exist.");
                     return false;
                 }
                 p = ps.pkg;
             }
+            if (ps != null) {
+                libDirPath = ps.nativeLibraryPathString;
+            }
             if (p != null && (isExternal(p) || isForwardLocked(p))) {
                 String secureContainerId = cidFromCodePath(p.applicationInfo.sourceDir);
                 if (secureContainerId != null) {
@@ -9194,8 +9268,8 @@
                 publicSrcDir = applicationInfo.publicSourceDir;
             }
         }
-        int res = mInstaller.getSizeInfo(packageName, userHandle, p.mPath, publicSrcDir,
-                asecPath, pStats);
+        int res = mInstaller.getSizeInfo(packageName, userHandle, p.mPath, libDirPath,
+                publicSrcDir, asecPath, pStats);
         if (res < 0) {
             return false;
         }
@@ -9541,8 +9615,7 @@
                 }
             }
             mSettings.writePackageRestrictionsLPr(userId);
-            packageUid = UserHandle.getUid(userId, pkgSetting.appId);
-            components = mPendingBroadcasts.get(packageName);
+            components = mPendingBroadcasts.get(userId, packageName);
             final boolean newPackage = components == null;
             if (newPackage) {
                 components = new ArrayList<String>();
@@ -9554,10 +9627,10 @@
                 sendNow = true;
                 // Purge entry from pending broadcast list if another one exists already
                 // since we are sending one right away.
-                mPendingBroadcasts.remove(packageName);
+                mPendingBroadcasts.remove(userId, packageName);
             } else {
                 if (newPackage) {
-                    mPendingBroadcasts.put(packageName, components);
+                    mPendingBroadcasts.put(userId, packageName, components);
                 }
                 if (!mHandler.hasMessages(SEND_PENDING_BROADCAST)) {
                     // Schedule a message
@@ -9569,6 +9642,7 @@
         long callingId = Binder.clearCallingIdentity();
         try {
             if (sendNow) {
+                packageUid = UserHandle.getUid(userId, pkgSetting.appId);
                 sendPackageChangedBroadcast(packageName,
                         (flags&PackageManager.DONT_KILL_APP) != 0, components, packageUid);
             }
@@ -10704,8 +10778,9 @@
 
     /** Called by UserManagerService */
     void cleanUpUserLILPw(int userHandle) {
-        if (mDirtyUsers.remove(userHandle));
+        mDirtyUsers.remove(userHandle);
         mSettings.removeUserLPr(userHandle);
+        mPendingBroadcasts.remove(userHandle);
         if (mInstaller != null) {
             // Technically, we shouldn't be doing this with the package lock
             // held.  However, this is very rare, and there is already so much
diff --git a/services/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java b/services/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java
index 0ab86e4..5dd30f1 100644
--- a/services/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java
+++ b/services/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java
@@ -94,11 +94,15 @@
 
     private void unpackBundle() throws IOException {
         BufferedInputStream stream = new BufferedInputStream(new FileInputStream(updateContent));
-        int[] chunkLengths = readChunkLengths(stream);
-        installFile(new File(updateDir, seappContextsPath), stream, chunkLengths[0]);
-        installFile(new File(updateDir, propertyContextsPath), stream, chunkLengths[1]);
-        installFile(new File(updateDir, fileContextsPath), stream, chunkLengths[2]);
-        installFile(new File(updateDir, sepolicyPath), stream, chunkLengths[3]);
+        try {
+            int[] chunkLengths = readChunkLengths(stream);
+            installFile(new File(updateDir, seappContextsPath), stream, chunkLengths[0]);
+            installFile(new File(updateDir, propertyContextsPath), stream, chunkLengths[1]);
+            installFile(new File(updateDir, fileContextsPath), stream, chunkLengths[2]);
+            installFile(new File(updateDir, sepolicyPath), stream, chunkLengths[3]);
+        } finally {
+            IoUtils.closeQuietly(stream);
+        }
     }
 
     private void applyUpdate() throws IOException, ErrnoException {
@@ -124,10 +128,10 @@
     private void setEnforcingMode(Context context) {
         String mode = Settings.Global.getString(context.getContentResolver(),
             Settings.Global.SELINUX_STATUS);
-        if (mode.equals("1")) {
+        if ("1".equals(mode)) {
             Slog.i(TAG, "Setting enforcing mode");
             SystemProperties.set("persist.selinux.enforcing", mode);
-        } else if (mode.equals("0")) {
+        } else if ("0".equals(mode)) {
             Slog.i(TAG, "Tried to set permissive mode, ignoring");
         } else {
             Slog.e(TAG, "Got invalid enforcing mode: " + mode);
diff --git a/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibility.java b/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibility.java
index 7ae0fb8..f5c4c34 100644
--- a/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibility.java
+++ b/tests/Compatibility/src/com/android/compatibilitytest/AppCompatibility.java
@@ -18,9 +18,12 @@
 
 import android.app.ActivityManager;
 import android.app.ActivityManager.ProcessErrorStateInfo;
+import android.app.ActivityManager.RunningAppProcessInfo;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.os.Bundle;
 import android.test.InstrumentationTestCase;
 import android.util.Log;
@@ -28,9 +31,11 @@
 import junit.framework.Assert;
 
 import java.util.Collection;
+import java.util.List;
 
 /**
- * Application Compatibility Test that launches an application and detects crashes.
+ * Application Compatibility Test that launches an application and detects
+ * crashes.
  */
 public class AppCompatibility extends InstrumentationTestCase {
 
@@ -49,10 +54,10 @@
     private Bundle mArgs;
 
     @Override
-    public void setUp() throws Exception{
+    public void setUp() throws Exception {
         super.setUp();
         mRunner = (AppCompatibilityRunner) getInstrumentation();
-        assertNotNull("Could not fetch InstrumentationTestRunner.",mRunner);
+        assertNotNull("Could not fetch InstrumentationTestRunner.", mRunner);
 
         mContext = mRunner.getTargetContext();
         Assert.assertNotNull("Could not get the Context", mContext);
@@ -83,7 +88,9 @@
     }
 
     /**
-     * Actual test case that launches the package and throws an exception on the first error.
+     * Actual test case that launches the package and throws an exception on the
+     * first error.
+     *
      * @throws Exception
      */
     public void testAppStability() throws Exception {
@@ -91,9 +98,11 @@
         if (packageName != null) {
             Log.d(TAG, "Launching app " + packageName);
             Collection<ProcessErrorStateInfo> err = launchActivity(packageName);
-            // Make sure there are no errors when launching the application, otherwise raise an
+            // Make sure there are no errors when launching the application,
+            // otherwise raise an
             // exception with the first error encountered.
             assertNull(getFirstError(err), err);
+            assertTrue("App crashed after launch.", processStillUp(packageName));
         } else {
             Log.d(TAG, "Missing argument, use " + PACKAGE_TO_LAUNCH +
                     " to specify the package to launch");
@@ -102,6 +111,7 @@
 
     /**
      * Gets the first error in collection and return the long message for it.
+     *
      * @param in {@link Collection} of {@link ProcessErrorStateInfo} to parse.
      * @return {@link String} the long message of the error.
      */
@@ -118,8 +128,11 @@
 
     /**
      * Launches and activity and queries for errors.
-     * @param packageName {@link String} the package name of the application to launch.
-     * @return  {@link Collection} of {@link ProcessErrorStateInfo} detected during the app launch.
+     *
+     * @param packageName {@link String} the package name of the application to
+     *            launch.
+     * @return {@link Collection} of {@link ProcessErrorStateInfo} detected
+     *         during the app launch.
      */
     private Collection<ProcessErrorStateInfo> launchActivity(String packageName) {
         Intent homeIntent = new Intent(Intent.ACTION_MAIN);
@@ -129,14 +142,20 @@
         Intent intent = mPackageManager.getLaunchIntentForPackage(packageName);
         // Skip if the apk does not have a launch intent.
         if (intent == null) {
+            Log.d(TAG, "Skipping " + packageName + "; missing launch intent");
             return null;
         }
 
-        // We check for any Crash or ANR dialogs that are already up, and we ignore them.  This is
-        // so that we don't report crashes that were caused by prior apps (which those particular
-        // tests should have caught and reported already).  Otherwise, test failures would cascade
-        // from the initial broken app to many/all of the tests following that app's launch.
-        final Collection<ProcessErrorStateInfo> preErr = mActivityManager.getProcessesInErrorState();
+        // We check for any Crash or ANR dialogs that are already up, and we
+        // ignore them. This is
+        // so that we don't report crashes that were caused by prior apps (which
+        // those particular
+        // tests should have caught and reported already). Otherwise, test
+        // failures would cascade
+        // from the initial broken app to many/all of the tests following that
+        // app's launch.
+        final Collection<ProcessErrorStateInfo> preErr =
+                mActivityManager.getProcessesInErrorState();
 
         // Launch Activity
         mContext.startActivity(intent);
@@ -155,15 +174,43 @@
             // ignore
         }
 
-        // See if there are any errors.  We wait until down here to give ANRs as much time as
+        // See if there are any errors. We wait until down here to give ANRs as
+        // much time as
         // possible to occur.
         final Collection<ProcessErrorStateInfo> postErr =
                 mActivityManager.getProcessesInErrorState();
-        // Take the difference between the error processes we see now, and the ones that were
+        // Take the difference between the error processes we see now, and the
+        // ones that were
         // present when we started
         if (preErr != null && postErr != null) {
             postErr.removeAll(preErr);
         }
         return postErr;
     }
+
+    /**
+     * Determine if a given package is still running.
+     *
+     * @param packageName {@link String} package to look for
+     * @return True if package is running, false otherwise.
+     */
+    private boolean processStillUp(String packageName) {
+        try {
+            PackageInfo packageInfo = mPackageManager.getPackageInfo(packageName, 0);
+            String processName = packageInfo.applicationInfo.processName;
+            List<RunningAppProcessInfo> runningApps = mActivityManager.getRunningAppProcesses();
+            for (RunningAppProcessInfo app : runningApps) {
+                if (app.processName.equalsIgnoreCase(processName)) {
+                    Log.d(TAG, "Found process " + app.processName);
+                    return true;
+                }
+            }
+            Log.d(TAG, "Failed to find process " + processName + " with package name "
+                    + packageName);
+        } catch (NameNotFoundException e) {
+            Log.w(TAG, "Failed to find package " + packageName);
+            return false;
+        }
+        return false;
+    }
 }