Merge "Traverse layers in update order" into jb-mr2-dev
diff --git a/api/current.txt b/api/current.txt
index e001e1a..653e25a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2477,6 +2477,11 @@
     method public void setPropertyName(java.lang.String);
   }
 
+  public class RectEvaluator implements android.animation.TypeEvaluator {
+    ctor public RectEvaluator();
+    method public android.graphics.Rect evaluate(float, android.graphics.Rect, android.graphics.Rect);
+  }
+
   public class TimeAnimator extends android.animation.ValueAnimator {
     ctor public TimeAnimator();
     method public void setTimeListener(android.animation.TimeAnimator.TimeListener);
@@ -4851,7 +4856,7 @@
   }
 
   public final class BluetoothDevice implements android.os.Parcelable {
-    method public android.bluetooth.BluetoothGatt connectGattServer(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback);
+    method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback);
     method public android.bluetooth.BluetoothSocket createInsecureRfcommSocketToServiceRecord(java.util.UUID) throws java.io.IOException;
     method public android.bluetooth.BluetoothSocket createRfcommSocketToServiceRecord(java.util.UUID) throws java.io.IOException;
     method public int describeContents();
@@ -4860,6 +4865,7 @@
     method public android.bluetooth.BluetoothClass getBluetoothClass();
     method public int getBondState();
     method public java.lang.String getName();
+    method public int getType();
     method public android.os.ParcelUuid[] getUuids();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final java.lang.String ACTION_ACL_CONNECTED = "android.bluetooth.device.action.ACL_CONNECTED";
@@ -4874,6 +4880,10 @@
     field public static final int BOND_BONDING = 11; // 0xb
     field public static final int BOND_NONE = 10; // 0xa
     field public static final android.os.Parcelable.Creator CREATOR;
+    field public static final int DEVICE_TYPE_CLASSIC = 1; // 0x1
+    field public static final int DEVICE_TYPE_DUAL = 3; // 0x3
+    field public static final int DEVICE_TYPE_LE = 2; // 0x2
+    field public static final int DEVICE_TYPE_UNKNOWN = 0; // 0x0
     field public static final int ERROR = -2147483648; // 0x80000000
     field public static final java.lang.String EXTRA_BOND_STATE = "android.bluetooth.device.extra.BOND_STATE";
     field public static final java.lang.String EXTRA_CLASS = "android.bluetooth.device.extra.CLASS";
@@ -4887,11 +4897,14 @@
   public final class BluetoothGatt implements android.bluetooth.BluetoothProfile {
     method public void abortReliableWrite(android.bluetooth.BluetoothDevice);
     method public boolean beginReliableWrite();
+    method public void close();
+    method public boolean connect();
     method public void disconnect();
     method public boolean discoverServices();
     method public boolean executeReliableWrite();
     method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
     method public int getConnectionState(android.bluetooth.BluetoothDevice);
+    method public android.bluetooth.BluetoothDevice getDevice();
     method public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]);
     method public android.bluetooth.BluetoothGattService getService(java.util.UUID);
     method public java.util.List<android.bluetooth.BluetoothGattService> getServices();
@@ -4914,15 +4927,15 @@
 
   public abstract class BluetoothGattCallback {
     ctor public BluetoothGattCallback();
-    method public void onCharacteristicChanged(android.bluetooth.BluetoothGattCharacteristic);
-    method public void onCharacteristicRead(android.bluetooth.BluetoothGattCharacteristic, int);
-    method public void onCharacteristicWrite(android.bluetooth.BluetoothGattCharacteristic, int);
-    method public void onConnectionStateChange(android.bluetooth.BluetoothDevice, int, int);
-    method public void onDescriptorRead(android.bluetooth.BluetoothGattDescriptor, int);
-    method public void onDescriptorWrite(android.bluetooth.BluetoothGattDescriptor, int);
-    method public void onReadRemoteRssi(android.bluetooth.BluetoothDevice, int, int);
-    method public void onReliableWriteCompleted(android.bluetooth.BluetoothDevice, int);
-    method public void onServicesDiscovered(android.bluetooth.BluetoothDevice, int);
+    method public void onCharacteristicChanged(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic);
+    method public void onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int);
+    method public void onCharacteristicWrite(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int);
+    method public void onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int);
+    method public void onDescriptorRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattDescriptor, int);
+    method public void onDescriptorWrite(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattDescriptor, int);
+    method public void onReadRemoteRssi(android.bluetooth.BluetoothGatt, int, int);
+    method public void onReliableWriteCompleted(android.bluetooth.BluetoothGatt, int);
+    method public void onServicesDiscovered(android.bluetooth.BluetoothGatt, int);
   }
 
   public class BluetoothGattCharacteristic {
@@ -16940,6 +16953,7 @@
     method public android.os.StrictMode.VmPolicy build();
     method public android.os.StrictMode.VmPolicy.Builder detectActivityLeaks();
     method public android.os.StrictMode.VmPolicy.Builder detectAll();
+    method public android.os.StrictMode.VmPolicy.Builder detectFileUriExposure();
     method public android.os.StrictMode.VmPolicy.Builder detectLeakedClosableObjects();
     method public android.os.StrictMode.VmPolicy.Builder detectLeakedRegistrationObjects();
     method public android.os.StrictMode.VmPolicy.Builder detectLeakedSqlLiteObjects();
diff --git a/core/java/android/animation/RectEvaluator.java b/core/java/android/animation/RectEvaluator.java
new file mode 100644
index 0000000..10932bb
--- /dev/null
+++ b/core/java/android/animation/RectEvaluator.java
@@ -0,0 +1,45 @@
+/*
+ * 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.graphics.Rect;
+
+/**
+ * This evaluator can be used to perform type interpolation between <code>int</code> values.
+ */
+public class RectEvaluator implements TypeEvaluator<Rect> {
+
+    /**
+     * This function returns the result of linearly interpolating the start and
+     * end Rect values, with <code>fraction</code> representing the proportion
+     * between the start and end values. The calculation is a simple parametric
+     * calculation on each of the separate components in the Rect objects
+     * (left, top, right, and bottom).
+     *
+     * @param fraction   The fraction from the starting to the ending values
+     * @param startValue The start Rect
+     * @param endValue   The end Rect
+     * @return A linear interpolation between the start and end values, given the
+     *         <code>fraction</code> parameter.
+     */
+    @Override
+    public Rect evaluate(float fraction, Rect startValue, Rect endValue) {
+        return new Rect(startValue.left + (int)((endValue.left - startValue.left) * fraction),
+                startValue.top + (int)((endValue.top - startValue.top) * fraction),
+                startValue.right + (int)((endValue.right - startValue.right) * fraction),
+                startValue.bottom + (int)((endValue.bottom - startValue.bottom) * fraction));
+    }
+}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 87c2d8c..31074e2 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -3513,7 +3513,8 @@
         try {
             String resolvedType = null;
             if (fillInIntent != null) {
-                fillInIntent.setAllowFds(false);
+                fillInIntent.migrateExtraStreamToClipData();
+                fillInIntent.prepareToLeaveProcess();
                 resolvedType = fillInIntent.resolveTypeIfNeeded(getContentResolver());
             }
             int result = ActivityManagerNative.getDefault()
@@ -3738,7 +3739,8 @@
         if (mParent == null) {
             int result = ActivityManager.START_RETURN_INTENT_TO_CALLER;
             try {
-                intent.setAllowFds(false);
+                intent.migrateExtraStreamToClipData();
+                intent.prepareToLeaveProcess();
                 result = ActivityManagerNative.getDefault()
                     .startActivity(mMainThread.getApplicationThread(), getBasePackageName(),
                             intent, intent.resolveTypeIfNeeded(getContentResolver()),
@@ -3808,7 +3810,8 @@
     public boolean startNextMatchingActivity(Intent intent, Bundle options) {
         if (mParent == null) {
             try {
-                intent.setAllowFds(false);
+                intent.migrateExtraStreamToClipData();
+                intent.prepareToLeaveProcess();
                 return ActivityManagerNative.getDefault()
                     .startNextMatchingActivity(mToken, intent, options);
             } catch (RemoteException e) {
@@ -4162,7 +4165,7 @@
             if (false) Log.v(TAG, "Finishing self: token=" + mToken);
             try {
                 if (resultData != null) {
-                    resultData.setAllowFds(false);
+                    resultData.prepareToLeaveProcess();
                 }
                 if (ActivityManagerNative.getDefault()
                     .finishActivity(mToken, resultCode, resultData)) {
@@ -4314,7 +4317,7 @@
             int flags) {
         String packageName = getPackageName();
         try {
-            data.setAllowFds(false);
+            data.prepareToLeaveProcess();
             IIntentSender target =
                 ActivityManagerNative.getDefault().getIntentSender(
                         ActivityManager.INTENT_SENDER_ACTIVITY_RESULT, packageName,
@@ -4993,9 +4996,10 @@
                 resultData = mResultData;
             }
             if (resultData != null) {
-                resultData.setAllowFds(false);
+                resultData.prepareToLeaveProcess();
             }
             try {
+                upIntent.prepareToLeaveProcess();
                 return ActivityManagerNative.getDefault().navigateUpTo(mToken, upIntent,
                         resultCode, resultData);
             } catch (RemoteException e) {
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 459e49c..9bf8830 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1020,7 +1020,8 @@
         try {
             String resolvedType = null;
             if (fillInIntent != null) {
-                fillInIntent.setAllowFds(false);
+                fillInIntent.migrateExtraStreamToClipData();
+                fillInIntent.prepareToLeaveProcess();
                 resolvedType = fillInIntent.resolveTypeIfNeeded(getContentResolver());
             }
             int result = ActivityManagerNative.getDefault()
@@ -1040,7 +1041,7 @@
         warnIfCallingFromSystemProcess();
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
-            intent.setAllowFds(false);
+            intent.prepareToLeaveProcess();
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, null,
                 Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, false, false,
@@ -1054,7 +1055,7 @@
         warnIfCallingFromSystemProcess();
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
-            intent.setAllowFds(false);
+            intent.prepareToLeaveProcess();
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, null,
                 Activity.RESULT_OK, null, null, receiverPermission, AppOpsManager.OP_NONE,
@@ -1068,7 +1069,7 @@
         warnIfCallingFromSystemProcess();
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
-            intent.setAllowFds(false);
+            intent.prepareToLeaveProcess();
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, null,
                 Activity.RESULT_OK, null, null, receiverPermission, appOp, false, false,
@@ -1083,7 +1084,7 @@
         warnIfCallingFromSystemProcess();
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
-            intent.setAllowFds(false);
+            intent.prepareToLeaveProcess();
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, null,
                 Activity.RESULT_OK, null, null, receiverPermission, AppOpsManager.OP_NONE, true, false,
@@ -1126,7 +1127,7 @@
         }
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
-            intent.setAllowFds(false);
+            intent.prepareToLeaveProcess();
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, rd,
                 initialCode, initialData, initialExtras, receiverPermission, appOp,
@@ -1139,7 +1140,7 @@
     public void sendBroadcastAsUser(Intent intent, UserHandle user) {
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
-            intent.setAllowFds(false);
+            intent.prepareToLeaveProcess();
             ActivityManagerNative.getDefault().broadcastIntent(mMainThread.getApplicationThread(),
                     intent, resolvedType, null, Activity.RESULT_OK, null, null, null,
                     AppOpsManager.OP_NONE, false, false, user.getIdentifier());
@@ -1152,7 +1153,7 @@
             String receiverPermission) {
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
-            intent.setAllowFds(false);
+            intent.prepareToLeaveProcess();
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, null,
                 Activity.RESULT_OK, null, null, receiverPermission, AppOpsManager.OP_NONE, false, false,
@@ -1184,7 +1185,7 @@
         }
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
-            intent.setAllowFds(false);
+            intent.prepareToLeaveProcess();
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, rd,
                 initialCode, initialData, initialExtras, receiverPermission,
@@ -1198,7 +1199,7 @@
         warnIfCallingFromSystemProcess();
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
-            intent.setAllowFds(false);
+            intent.prepareToLeaveProcess();
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, null,
                 Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, false, true,
@@ -1232,7 +1233,7 @@
         }
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
-            intent.setAllowFds(false);
+            intent.prepareToLeaveProcess();
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, rd,
                 initialCode, initialData, initialExtras, null,
@@ -1249,7 +1250,7 @@
             intent.setDataAndType(intent.getData(), resolvedType);
         }
         try {
-            intent.setAllowFds(false);
+            intent.prepareToLeaveProcess();
             ActivityManagerNative.getDefault().unbroadcastIntent(
                     mMainThread.getApplicationThread(), intent, getUserId());
         } catch (RemoteException e) {
@@ -1260,7 +1261,7 @@
     public void sendStickyBroadcastAsUser(Intent intent, UserHandle user) {
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
-            intent.setAllowFds(false);
+            intent.prepareToLeaveProcess();
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, null,
                 Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, false, true, user.getIdentifier());
@@ -1292,7 +1293,7 @@
         }
         String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
         try {
-            intent.setAllowFds(false);
+            intent.prepareToLeaveProcess();
             ActivityManagerNative.getDefault().broadcastIntent(
                 mMainThread.getApplicationThread(), intent, resolvedType, rd,
                 initialCode, initialData, initialExtras, null,
@@ -1309,7 +1310,7 @@
             intent.setDataAndType(intent.getData(), resolvedType);
         }
         try {
-            intent.setAllowFds(false);
+            intent.prepareToLeaveProcess();
             ActivityManagerNative.getDefault().unbroadcastIntent(
                     mMainThread.getApplicationThread(), intent, user.getIdentifier());
         } catch (RemoteException e) {
@@ -1393,7 +1394,7 @@
     @Override
     public ComponentName startServiceAsUser(Intent service, UserHandle user) {
         try {
-            service.setAllowFds(false);
+            service.prepareToLeaveProcess();
             ComponentName cn = ActivityManagerNative.getDefault().startService(
                 mMainThread.getApplicationThread(), service,
                 service.resolveTypeIfNeeded(getContentResolver()), user.getIdentifier());
@@ -1417,7 +1418,7 @@
     @Override
     public boolean stopServiceAsUser(Intent service, UserHandle user) {
         try {
-            service.setAllowFds(false);
+            service.prepareToLeaveProcess();
             int res = ActivityManagerNative.getDefault().stopService(
                 mMainThread.getApplicationThread(), service,
                 service.resolveTypeIfNeeded(getContentResolver()), user.getIdentifier());
@@ -1459,7 +1460,7 @@
                     < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
                 flags |= BIND_WAIVE_PRIORITY;
             }
-            service.setAllowFds(false);
+            service.prepareToLeaveProcess();
             int res = ActivityManagerNative.getDefault().bindService(
                 mMainThread.getApplicationThread(), getActivityToken(),
                 service, service.resolveTypeIfNeeded(getContentResolver()),
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index e7bf305..e0dfb25 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1410,8 +1410,8 @@
             }
         }
         try {
-            intent.setAllowFds(false);
             intent.migrateExtraStreamToClipData();
+            intent.prepareToLeaveProcess();
             int result = ActivityManagerNative.getDefault()
                 .startActivity(whoThread, who.getBasePackageName(), intent,
                         intent.resolveTypeIfNeeded(who.getContentResolver()),
@@ -1467,7 +1467,8 @@
         try {
             String[] resolvedTypes = new String[intents.length];
             for (int i=0; i<intents.length; i++) {
-                intents[i].setAllowFds(false);
+                intents[i].migrateExtraStreamToClipData();
+                intents[i].prepareToLeaveProcess();
                 resolvedTypes[i] = intents[i].resolveTypeIfNeeded(who.getContentResolver());
             }
             int result = ActivityManagerNative.getDefault()
@@ -1526,8 +1527,8 @@
             }
         }
         try {
-            intent.setAllowFds(false);
             intent.migrateExtraStreamToClipData();
+            intent.prepareToLeaveProcess();
             int result = ActivityManagerNative.getDefault()
                 .startActivity(whoThread, who.getBasePackageName(), intent,
                         intent.resolveTypeIfNeeded(who.getContentResolver()),
@@ -1586,8 +1587,8 @@
             }
         }
         try {
-            intent.setAllowFds(false);
             intent.migrateExtraStreamToClipData();
+            intent.prepareToLeaveProcess();
             int result = ActivityManagerNative.getDefault()
                 .startActivityAsUser(whoThread, who.getBasePackageName(), intent,
                         intent.resolveTypeIfNeeded(who.getContentResolver()),
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 5e69128..dbafc78 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -21,6 +21,7 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.StrictMode;
 import android.os.UserHandle;
 import android.util.Log;
 
@@ -126,6 +127,9 @@
         String pkg = mContext.getPackageName();
         if (notification.sound != null) {
             notification.sound = notification.sound.getCanonicalUri();
+            if (StrictMode.vmFileUriExposureEnabled()) {
+                notification.sound.checkFileUriExposed("Notification.sound");
+            }
         }
         if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
         try {
@@ -148,6 +152,9 @@
         String pkg = mContext.getPackageName();
         if (notification.sound != null) {
             notification.sound = notification.sound.getCanonicalUri();
+            if (StrictMode.vmFileUriExposureEnabled()) {
+                notification.sound.checkFileUriExposed("Notification.sound");
+            }
         }
         if (localLOGV) Log.v(TAG, pkg + ": notify(" + id + ", " + notification + ")");
         try {
diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java
index 20114cc..25c790f 100644
--- a/core/java/android/app/PendingIntent.java
+++ b/core/java/android/app/PendingIntent.java
@@ -260,8 +260,8 @@
         String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
                 context.getContentResolver()) : null;
         try {
-            intent.setAllowFds(false);
             intent.migrateExtraStreamToClipData();
+            intent.prepareToLeaveProcess();
             IIntentSender target =
                 ActivityManagerNative.getDefault().getIntentSender(
                     ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
@@ -285,8 +285,8 @@
         String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
                 context.getContentResolver()) : null;
         try {
-            intent.setAllowFds(false);
             intent.migrateExtraStreamToClipData();
+            intent.prepareToLeaveProcess();
             IIntentSender target =
                 ActivityManagerNative.getDefault().getIntentSender(
                     ActivityManager.INTENT_SENDER_ACTIVITY, packageName,
@@ -401,7 +401,8 @@
         String packageName = context.getPackageName();
         String[] resolvedTypes = new String[intents.length];
         for (int i=0; i<intents.length; i++) {
-            intents[i].setAllowFds(false);
+            intents[i].migrateExtraStreamToClipData();
+            intents[i].prepareToLeaveProcess();
             resolvedTypes[i] = intents[i].resolveTypeIfNeeded(context.getContentResolver());
         }
         try {
@@ -426,7 +427,8 @@
         String packageName = context.getPackageName();
         String[] resolvedTypes = new String[intents.length];
         for (int i=0; i<intents.length; i++) {
-            intents[i].setAllowFds(false);
+            intents[i].migrateExtraStreamToClipData();
+            intents[i].prepareToLeaveProcess();
             resolvedTypes[i] = intents[i].resolveTypeIfNeeded(context.getContentResolver());
         }
         try {
@@ -482,7 +484,7 @@
         String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
                 context.getContentResolver()) : null;
         try {
-            intent.setAllowFds(false);
+            intent.prepareToLeaveProcess();
             IIntentSender target =
                 ActivityManagerNative.getDefault().getIntentSender(
                     ActivityManager.INTENT_SENDER_BROADCAST, packageName,
@@ -526,7 +528,7 @@
         String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
                 context.getContentResolver()) : null;
         try {
-            intent.setAllowFds(false);
+            intent.prepareToLeaveProcess();
             IIntentSender target =
                 ActivityManagerNative.getDefault().getIntentSender(
                     ActivityManager.INTENT_SENDER_SERVICE, packageName,
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 83e95ca..3c1ec90 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -262,6 +262,26 @@
     public static final String EXTRA_PAIRING_KEY = "android.bluetooth.device.extra.PAIRING_KEY";
 
     /**
+     * Bluetooth device type, Unknown
+     */
+    public static final int DEVICE_TYPE_UNKNOWN = 0;
+
+    /**
+     * Bluetooth device type, Classic - BR/EDR devices
+     */
+    public static final int DEVICE_TYPE_CLASSIC = 1;
+
+    /**
+     * Bluetooth device type, Low Energy - LE-only
+     */
+    public static final int DEVICE_TYPE_LE = 2;
+
+    /**
+     * Bluetooth device type, Dual Mode - BR/EDR/LE
+     */
+    public static final int DEVICE_TYPE_DUAL = 3;
+
+    /**
      * Broadcast Action: This intent is used to broadcast the {@link UUID}
      * wrapped as a {@link android.os.ParcelUuid} of the remote device after it
      * has been fetched. This intent is sent only when the UUIDs of the remote
@@ -602,6 +622,26 @@
     }
 
     /**
+     * Get the Bluetooth device type of the remote device.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+     *
+     * @return the device type {@link #DEVICE_TYPE_CLASSIC}, {@link #DEVICE_TYPE_LE}
+     *                         {@link #DEVICE_TYPE_DUAL}.
+     *         {@link #DEVICE_TYPE_UNKNOWN} if it's not available
+     */
+    public int getType() {
+        if (sService == null) {
+            Log.e(TAG, "BT not enabled. Cannot get Remote Device type");
+            return DEVICE_TYPE_UNKNOWN;
+        }
+        try {
+            return sService.getRemoteType(this);
+        } catch (RemoteException e) {Log.e(TAG, "", e);}
+        return DEVICE_TYPE_UNKNOWN;
+    }
+
+    /**
      * Get the Bluetooth alias of the remote device.
      * <p>Alias is the locally modified name of a remote device.
      *
@@ -1139,8 +1179,8 @@
      *                    device becomes available (true).
      * @throws IllegalArgumentException if callback is null
      */
-    public BluetoothGatt connectGattServer(Context context, boolean autoConnect,
-                                           BluetoothGattCallback callback) {
+    public BluetoothGatt connectGatt(Context context, boolean autoConnect,
+                                     BluetoothGattCallback callback) {
         // TODO(Bluetooth) check whether platform support BLE
         //     Do the check here or in GattServer?
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index f9ce6ea..bffe64b 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -43,7 +43,7 @@
  * with Bluetooth Smart or Smart Ready devices.
  *
  * <p>To connect to a remote peripheral device, create a {@link BluetoothGattCallback}
- * and call {@link BluetoothDevice#connectGattServer} to get a instance of this class.
+ * and call {@link BluetoothDevice#connectGatt} to get a instance of this class.
  * GATT capable devices can be discovered using the Bluetooth device discovery or BLE
  * scan process.
  */
@@ -66,6 +66,7 @@
     private static final int CONN_STATE_CONNECTING = 1;
     private static final int CONN_STATE_CONNECTED = 2;
     private static final int CONN_STATE_DISCONNECTING = 3;
+    private static final int CONN_STATE_CLOSED = 4;
 
     private List<BluetoothGattService> mServices;
 
@@ -135,7 +136,7 @@
                 }
                 mClientIf = clientIf;
                 if (status != GATT_SUCCESS) {
-                    mCallback.onConnectionStateChange(mDevice, GATT_FAILURE,
+                    mCallback.onConnectionStateChange(BluetoothGatt.this, GATT_FAILURE,
                                                       BluetoothProfile.STATE_DISCONNECTED);
                     synchronized(mStateLock) {
                         mConnState = CONN_STATE_IDLE;
@@ -164,7 +165,7 @@
                 int profileState = connected ? BluetoothProfile.STATE_CONNECTED :
                                                BluetoothProfile.STATE_DISCONNECTED;
                 try {
-                    mCallback.onConnectionStateChange(mDevice, status, profileState);
+                    mCallback.onConnectionStateChange(BluetoothGatt.this, status, profileState);
                 } catch (Exception ex) {
                     Log.w(TAG, "Unhandled exception: " + ex);
                 }
@@ -291,7 +292,7 @@
                     return;
                 }
                 try {
-                    mCallback.onServicesDiscovered(mDevice, status);
+                    mCallback.onServicesDiscovered(BluetoothGatt.this, status);
                 } catch (Exception ex) {
                     Log.w(TAG, "Unhandled exception: " + ex);
                 }
@@ -338,7 +339,7 @@
                 if (status == 0) characteristic.setValue(value);
 
                 try {
-                    mCallback.onCharacteristicRead(characteristic, status);
+                    mCallback.onCharacteristicRead(BluetoothGatt.this, characteristic, status);
                 } catch (Exception ex) {
                     Log.w(TAG, "Unhandled exception: " + ex);
                 }
@@ -384,7 +385,7 @@
                 mAuthRetry = false;
 
                 try {
-                    mCallback.onCharacteristicWrite(characteristic, status);
+                    mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic, status);
                 } catch (Exception ex) {
                     Log.w(TAG, "Unhandled exception: " + ex);
                 }
@@ -415,7 +416,7 @@
                 characteristic.setValue(value);
 
                 try {
-                    mCallback.onCharacteristicChanged(characteristic);
+                    mCallback.onCharacteristicChanged(BluetoothGatt.this, characteristic);
                 } catch (Exception ex) {
                     Log.w(TAG, "Unhandled exception: " + ex);
                 }
@@ -464,7 +465,7 @@
                 mAuthRetry = true;
 
                 try {
-                    mCallback.onDescriptorRead(descriptor, status);
+                    mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status);
                 } catch (Exception ex) {
                     Log.w(TAG, "Unhandled exception: " + ex);
                 }
@@ -512,7 +513,7 @@
                 mAuthRetry = false;
 
                 try {
-                    mCallback.onDescriptorWrite(descriptor, status);
+                    mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status);
                 } catch (Exception ex) {
                     Log.w(TAG, "Unhandled exception: " + ex);
                 }
@@ -529,7 +530,7 @@
                     return;
                 }
                 try {
-                    mCallback.onReliableWriteCompleted(mDevice, status);
+                    mCallback.onReliableWriteCompleted(BluetoothGatt.this, status);
                 } catch (Exception ex) {
                     Log.w(TAG, "Unhandled exception: " + ex);
                 }
@@ -546,7 +547,7 @@
                     return;
                 }
                 try {
-                    mCallback.onReadRemoteRssi(mDevice, rssi, status);
+                    mCallback.onReadRemoteRssi(BluetoothGatt.this, rssi, status);
                 } catch (Exception ex) {
                     Log.w(TAG, "Unhandled exception: " + ex);
                 }
@@ -563,12 +564,13 @@
     }
 
     /**
-     * Close the connection to the gatt service.
+     * Close this Bluetooth GATT client.
      */
-    /*package*/ void close() {
+    public void close() {
         if (DBG) Log.d(TAG, "close()");
 
         unregisterApp();
+        mConnState = CONN_STATE_CLOSED;
     }
 
     /**
@@ -694,7 +696,35 @@
         } catch (RemoteException e) {
             Log.e(TAG,"",e);
         }
-        // TBD deregister after conneciton is torn down
+    }
+
+    /**
+     * Connect back to remote device.
+     *
+     * <p>This method is used to re-connect to a remote device after the
+     * connection has been dropped. If the device is not in range, the
+     * re-connection will be triggered once the device is back in range.
+     *
+     * @return true, if the connection attempt was initiated successfully
+     */
+    public boolean connect() {
+        try {
+            mService.clientConnect(mClientIf, mDevice.getAddress(),
+                                   false); // autoConnect is inverse of "isDirect"
+            return true;
+        } catch (RemoteException e) {
+            Log.e(TAG,"",e);
+            return false;
+        }
+    }
+
+    /**
+     * Return the remote bluetooth device this GATT client targets to
+     *
+     * @return remote bluetooth device
+     */
+    public BluetoothDevice getDevice() {
+        return mDevice;
     }
 
     /**
diff --git a/core/java/android/bluetooth/BluetoothGattCallback.java b/core/java/android/bluetooth/BluetoothGattCallback.java
index c9e5fea..2259c1e 100644
--- a/core/java/android/bluetooth/BluetoothGattCallback.java
+++ b/core/java/android/bluetooth/BluetoothGattCallback.java
@@ -16,23 +16,22 @@
 
 package android.bluetooth;
 
-import android.bluetooth.BluetoothDevice;
-
 /**
  * This abstract class is used to implement {@link BluetoothGatt} callbacks.
  */
 public abstract class BluetoothGattCallback {
 
     /**
-     * Callback indicating when a remote device has been connected or disconnected.
+     * Callback indicating when GATT client has connected/disconnected to/from a remote
+     * GATT server.
      *
-     * @param device Remote device that has been connected or disconnected.
+     * @param gatt GATT client
      * @param status Status of the connect or disconnect operation.
      * @param newState Returns the new connection state. Can be one of
      *                  {@link BluetoothProfile#STATE_DISCONNECTED} or
      *                  {@link BluetoothProfile#STATE_CONNECTED}
      */
-    public void onConnectionStateChange(BluetoothDevice device, int status,
+    public void onConnectionStateChange(BluetoothGatt gatt, int status,
                                         int newState) {
     }
 
@@ -40,22 +39,23 @@
      * Callback invoked when the list of remote services, characteristics and descriptors
      * for the remote device have been updated, ie new services have been discovered.
      *
-     * @param device Remote device
+     * @param gatt GATT client invoked {@link BluetoothGatt#discoverServices}
      * @param status {@link BluetoothGatt#GATT_SUCCESS} if the remote device
      *               has been explored successfully.
      */
-    public void onServicesDiscovered(BluetoothDevice device, int status) {
+    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
     }
 
     /**
      * Callback reporting the result of a characteristic read operation.
      *
+     * @param gatt GATT client invoked {@link BluetoothGatt#readCharacteristic}
      * @param characteristic Characteristic that was read from the associated
      *                       remote device.
      * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation
      *               was completed successfully.
      */
-    public void onCharacteristicRead(BluetoothGattCharacteristic characteristic,
+    public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic,
                                      int status) {
     }
 
@@ -68,52 +68,59 @@
      * value to the desired value to be written. If the values don't match,
      * the application must abort the reliable write transaction.
      *
+     * @param gatt GATT client invoked {@link BluetoothGatt#writeCharacteristic}
      * @param characteristic Characteristic that was written to the associated
      *                       remote device.
      * @param status The result of the write operation
      */
-    public void onCharacteristicWrite(BluetoothGattCharacteristic characteristic,
-                                      int status) {
+    public void onCharacteristicWrite(BluetoothGatt gatt,
+                                      BluetoothGattCharacteristic characteristic, int status) {
     }
 
     /**
      * Callback triggered as a result of a remote characteristic notification.
      *
+     * @param gatt GATT client the characteristic is associated with
      * @param characteristic Characteristic that has been updated as a result
      *                       of a remote notification event.
      */
-    public void onCharacteristicChanged(BluetoothGattCharacteristic characteristic) {
+    public void onCharacteristicChanged(BluetoothGatt gatt,
+                                        BluetoothGattCharacteristic characteristic) {
     }
 
     /**
      * Callback reporting the result of a descriptor read operation.
      *
+     * @param gatt GATT client invoked {@link BluetoothGatt#readDescriptor}
      * @param descriptor Descriptor that was read from the associated
      *                   remote device.
      * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation
      *               was completed successfully
      */
-    public void onDescriptorRead(BluetoothGattDescriptor descriptor, int status) {
+    public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
+                                 int status) {
     }
 
     /**
      * Callback indicating the result of a descriptor write operation.
      *
+     * @param gatt GATT client invoked {@link BluetoothGatt#writeDescriptor}
      * @param descriptor Descriptor that was writte to the associated
      *                   remote device.
      * @param status The result of the write operation
      */
-    public void onDescriptorWrite(BluetoothGattDescriptor descriptor, int status) {
+    public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
+                                  int status) {
     }
 
     /**
      * Callback invoked when a reliable write transaction has been completed.
      *
-     * @param device Remote device
+     * @param gatt GATT client invoked {@link BluetoothGatt#executeReliableWrite}
      * @param status {@link BluetoothGatt#GATT_SUCCESS} if the reliable write
      *               transaction was executed successfully
      */
-    public void onReliableWriteCompleted(BluetoothDevice device, int status) {
+    public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
     }
 
     /**
@@ -122,10 +129,10 @@
      * This callback is triggered in response to the
      * {@link BluetoothGatt#readRemoteRssi} function.
      *
-     * @param device Identifies the remote device
+     * @param gatt GATT client invoked {@link BluetoothGatt#readRemoteRssi}
      * @param rssi The RSSI value for the remote device
      * @param status {@link BluetoothGatt#GATT_SUCCESS} if the RSSI was read successfully
      */
-    public void onReadRemoteRssi(BluetoothDevice device, int rssi, int status) {
+    public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
     }
 }
diff --git a/core/java/android/bluetooth/BluetoothGattCharacteristic.java b/core/java/android/bluetooth/BluetoothGattCharacteristic.java
index d63d97e..033f079 100644
--- a/core/java/android/bluetooth/BluetoothGattCharacteristic.java
+++ b/core/java/android/bluetooth/BluetoothGattCharacteristic.java
@@ -22,6 +22,10 @@
 
 /**
  * Represents a Bluetooth GATT Characteristic
+ *
+ * <p>A GATT characteristic is a basic data element used to construct a GATT service,
+ * {@link BluetoothGattService}. The characteristic contains a value as well as
+ * additional information and optional GATT descriptors, {@link BluetoothGattDescriptor}.
  */
 public class BluetoothGattCharacteristic {
 
diff --git a/core/java/android/bluetooth/BluetoothGattDescriptor.java b/core/java/android/bluetooth/BluetoothGattDescriptor.java
index 6ba2db7..1cd6878 100644
--- a/core/java/android/bluetooth/BluetoothGattDescriptor.java
+++ b/core/java/android/bluetooth/BluetoothGattDescriptor.java
@@ -20,6 +20,10 @@
 
 /**
  * Represents a Bluetooth GATT Descriptor
+ *
+ * <p> GATT Descriptors contain additional information and attributes of a GATT
+ * characteristic, {@link BluetoothGattCharacteristic}. They can be used to describe
+ * the characteristic's features or to control certain behaviours of the characteristic.
  */
 public class BluetoothGattDescriptor {
 
diff --git a/core/java/android/bluetooth/BluetoothGattService.java b/core/java/android/bluetooth/BluetoothGattService.java
index c3b3cfe..39a435b 100644
--- a/core/java/android/bluetooth/BluetoothGattService.java
+++ b/core/java/android/bluetooth/BluetoothGattService.java
@@ -23,6 +23,9 @@
 
 /**
  * Represents a Bluetooth GATT Service
+ *
+ * <p> Gatt Service contains a collection of {@link BluetoothGattCharacteristic},
+ * as well as referenced services.
  */
 public class BluetoothGattService {
 
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index d016c26..80806f9 100644
--- a/core/java/android/bluetooth/IBluetooth.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -60,6 +60,7 @@
     int getBondState(in BluetoothDevice device);
 
     String getRemoteName(in BluetoothDevice device);
+    int getRemoteType(in BluetoothDevice device);
     String getRemoteAlias(in BluetoothDevice device);
     boolean setRemoteAlias(in BluetoothDevice device, in String name);
     int getRemoteClass(in BluetoothDevice device);
diff --git a/core/java/android/content/BroadcastReceiver.java b/core/java/android/content/BroadcastReceiver.java
index 4f42d50..9a32fdf 100644
--- a/core/java/android/content/BroadcastReceiver.java
+++ b/core/java/android/content/BroadcastReceiver.java
@@ -520,7 +520,7 @@
         IActivityManager am = ActivityManagerNative.getDefault();
         IBinder binder = null;
         try {
-            service.setAllowFds(false);
+            service.prepareToLeaveProcess();
             binder = am.peekService(service, service.resolveTypeIfNeeded(
                     myContext.getContentResolver()));
         } catch (RemoteException e) {
diff --git a/core/java/android/content/ClipData.java b/core/java/android/content/ClipData.java
index 88f1a3d..50c4fed 100644
--- a/core/java/android/content/ClipData.java
+++ b/core/java/android/content/ClipData.java
@@ -21,6 +21,7 @@
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.StrictMode;
 import android.text.Html;
 import android.text.Spannable;
 import android.text.SpannableStringBuilder;
@@ -790,6 +791,24 @@
         return mItems.get(index);
     }
 
+    /**
+     * Prepare this {@link ClipData} to leave an app process.
+     *
+     * @hide
+     */
+    public void prepareToLeaveProcess() {
+        final int size = mItems.size();
+        for (int i = 0; i < size; i++) {
+            final Item item = mItems.get(i);
+            if (item.mIntent != null) {
+                item.mIntent.prepareToLeaveProcess();
+            }
+            if (item.mUri != null && StrictMode.vmFileUriExposureEnabled()) {
+                item.mUri.checkFileUriExposed("ClipData.Item.getUri()");
+            }
+        }
+    }
+
     @Override
     public String toString() {
         StringBuilder b = new StringBuilder(128);
diff --git a/core/java/android/content/ClipboardManager.java b/core/java/android/content/ClipboardManager.java
index 88a4229..69f9d4a 100644
--- a/core/java/android/content/ClipboardManager.java
+++ b/core/java/android/content/ClipboardManager.java
@@ -22,6 +22,7 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.ServiceManager;
+import android.os.StrictMode;
 import android.util.Log;
 
 import java.util.ArrayList;
@@ -118,6 +119,9 @@
      */
     public void setPrimaryClip(ClipData clip) {
         try {
+            if (clip != null) {
+                clip.prepareToLeaveProcess();
+            }
             getService().setPrimaryClip(clip, mContext.getBasePackageName());
         } catch (RemoteException e) {
         }
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index c9b9a1a..97ad7dd 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -32,6 +32,7 @@
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.StrictMode;
 import android.util.AttributeSet;
 import android.util.Log;
 
@@ -6966,6 +6967,32 @@
     }
 
     /**
+     * Prepare this {@link Intent} to leave an app process.
+     *
+     * @hide
+     */
+    public void prepareToLeaveProcess() {
+        setAllowFds(false);
+
+        if (mSelector != null) {
+            mSelector.prepareToLeaveProcess();
+        }
+        if (mClipData != null) {
+            mClipData.prepareToLeaveProcess();
+        }
+
+        if (mData != null && StrictMode.vmFileUriExposureEnabled()) {
+            // There are several ACTION_MEDIA_* broadcasts that send file://
+            // Uris, so only check common actions.
+            if (ACTION_VIEW.equals(mAction) ||
+                    ACTION_EDIT.equals(mAction) ||
+                    ACTION_ATTACH_DATA.equals(mAction)) {
+                mData.checkFileUriExposed("Intent.getData()");
+            }
+        }
+    }
+
+    /**
      * Migrate any {@link #EXTRA_STREAM} in {@link #ACTION_SEND} and
      * {@link #ACTION_SEND_MULTIPLE} to {@link ClipData}. Also inspects nested
      * intents in {@link #ACTION_CHOOSER}.
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 4835d05..49cea3a 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1398,6 +1398,17 @@
             pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES;
         }
 
+        /*
+         * b/8528162: Ignore the <uses-permission android:required> attribute if
+         * targetSdkVersion < JELLY_BEAN_MR2. There are lots of apps in the wild
+         * which are improperly using this attribute, even though it never worked.
+         */
+        if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR2) {
+            for (int i = 0; i < pkg.requestedPermissionsRequired.size(); i++) {
+                pkg.requestedPermissionsRequired.set(i, Boolean.TRUE);
+            }
+        }
+
         return pkg;
     }
 
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index cc6903d..4b022d9 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -20,6 +20,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.Environment.UserEnvironment;
+import android.os.StrictMode;
 import android.util.Log;
 import java.io.File;
 import java.io.IOException;
@@ -2326,4 +2327,16 @@
             return this;
         }
     }
+
+    /**
+     * If this is a {@code file://} Uri, it will be reported to
+     * {@link StrictMode}.
+     *
+     * @hide
+     */
+    public void checkFileUriExposed(String location) {
+        if ("file".equals(getScheme())) {
+            StrictMode.onFileUriExposed(location);
+        }
+    }
 }
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index f682abe..3267939 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -203,10 +203,15 @@
      */
     public static final int DETECT_VM_REGISTRATION_LEAKS = 0x2000;  // for VmPolicy
 
+    /**
+     * @hide
+     */
+    private static final int DETECT_VM_FILE_URI_EXPOSURE = 0x4000;  // for VmPolicy
+
     private static final int ALL_VM_DETECT_BITS =
             DETECT_VM_CURSOR_LEAKS | DETECT_VM_CLOSABLE_LEAKS |
             DETECT_VM_ACTIVITY_LEAKS | DETECT_VM_INSTANCE_LEAKS |
-            DETECT_VM_REGISTRATION_LEAKS;
+            DETECT_VM_REGISTRATION_LEAKS | DETECT_VM_FILE_URI_EXPOSURE;
 
     /**
      * @hide
@@ -628,7 +633,8 @@
              */
             public Builder detectAll() {
                 return enable(DETECT_VM_ACTIVITY_LEAKS | DETECT_VM_CURSOR_LEAKS
-                        | DETECT_VM_CLOSABLE_LEAKS | DETECT_VM_REGISTRATION_LEAKS);
+                        | DETECT_VM_CLOSABLE_LEAKS | DETECT_VM_REGISTRATION_LEAKS
+                        | DETECT_VM_FILE_URI_EXPOSURE);
             }
 
             /**
@@ -666,6 +672,16 @@
             }
 
             /**
+             * Detect when a {@code file://} {@link android.net.Uri} is exposed beyond this
+             * app. The receiving app may not have access to the sent path.
+             * Instead, when sharing files between apps, {@code content://}
+             * should be used with permission grants.
+             */
+            public Builder detectFileUriExposure() {
+                return enable(DETECT_VM_FILE_URI_EXPOSURE);
+            }
+
+            /**
              * Crashes the whole process on violation.  This penalty runs at
              * the end of all enabled penalties so yo you'll still get
              * your logging or other violations before the process dies.
@@ -1524,6 +1540,13 @@
     /**
      * @hide
      */
+    public static boolean vmFileUriExposureEnabled() {
+        return (sVmPolicyMask & DETECT_VM_FILE_URI_EXPOSURE) != 0;
+    }
+
+    /**
+     * @hide
+     */
     public static void onSqliteObjectLeaked(String message, Throwable originStack) {
         onVmPolicyViolation(message, originStack);
     }
@@ -1549,6 +1572,14 @@
         onVmPolicyViolation(null, originStack);
     }
 
+    /**
+     * @hide
+     */
+    public static void onFileUriExposed(String location) {
+        final String message = "file:// Uri exposed through " + location;
+        onVmPolicyViolation(message, new Throwable(message));
+    }
+
     // Map from VM violation fingerprint to uptime millis.
     private static final HashMap<Integer, Long> sLastVmViolationTime = new HashMap<Integer, Long>();
 
diff --git a/core/java/android/text/util/Linkify.java b/core/java/android/text/util/Linkify.java
index 9860588..2bc1c6a 100644
--- a/core/java/android/text/util/Linkify.java
+++ b/core/java/android/text/util/Linkify.java
@@ -16,6 +16,7 @@
 
 package android.text.util;
 
+import android.telephony.PhoneNumberUtils;
 import android.text.method.LinkMovementMethod;
 import android.text.method.MovementMethod;
 import android.text.style.URLSpan;
@@ -32,9 +33,14 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.Locale;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import com.android.i18n.phonenumbers.PhoneNumberMatch;
+import com.android.i18n.phonenumbers.PhoneNumberUtil;
+import com.android.i18n.phonenumbers.PhoneNumberUtil.Leniency;
+
 /**
  *  Linkify take a piece of text and a regular expression and turns all of the
  *  regex matches in the text into clickable links.  This is particularly
@@ -221,9 +227,7 @@
         }
 
         if ((mask & PHONE_NUMBERS) != 0) {
-            gatherLinks(links, text, Patterns.PHONE,
-                new String[] { "tel:" },
-                sPhoneNumberMatchFilter, sPhoneNumberTransformFilter);
+            gatherTelLinks(links, text);
         }
 
         if ((mask & MAP_ADDRESSES) != 0) {
@@ -443,6 +447,19 @@
         }
     }
 
+    private static final void gatherTelLinks(ArrayList<LinkSpec> links, Spannable s) {
+        PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
+        Iterable<PhoneNumberMatch> matches = phoneUtil.findNumbers(s.toString(),
+                Locale.getDefault().getCountry(), Leniency.POSSIBLE, Long.MAX_VALUE);
+        for (PhoneNumberMatch match : matches) {
+            LinkSpec spec = new LinkSpec();
+            spec.url = "tel:" + PhoneNumberUtils.normalizeNumber(match.rawString());
+            spec.start = match.start();
+            spec.end = match.end();
+            links.add(spec);
+        }
+    }
+
     private static final void gatherMapLinks(ArrayList<LinkSpec> links, Spannable s) {
         String string = s.toString();
         String address;
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 725502d..f615e1bc 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -406,11 +406,17 @@
     private View[] mChildren;
     // Number of valid children in the mChildren array, the rest should be null or not
     // considered as children
-
-    private boolean mLayoutSuppressed = false;
-
     private int mChildrenCount;
 
+    // Whether layout calls are currently being suppressed, controlled by calls to
+    // suppressLayout()
+    boolean mSuppressLayout = false;
+
+    // Whether any layout calls have actually been suppressed while mSuppressLayout
+    // has been true. This tracks whether we need to issue a requestLayout() when
+    // layout is later re-enabled.
+    private boolean mLayoutCalledWhileSuppressed = false;
+
     private static final int ARRAY_INITIAL_CAPACITY = 12;
     private static final int ARRAY_CAPACITY_INCREMENT = 12;
 
@@ -2564,7 +2570,7 @@
         exitHoverTargets();
 
         // In case view is detached while transition is running
-        mLayoutSuppressed = false;
+        mLayoutCalledWhileSuppressed = false;
 
         // Tear down our drag tracking
         mDragNotifiedChildren = null;
@@ -4525,7 +4531,7 @@
             super.layout(l, t, r, b);
         } else {
             // record the fact that we noop'd it; request layout when transition finishes
-            mLayoutSuppressed = true;
+            mLayoutCalledWhileSuppressed = true;
         }
     }
 
@@ -5201,9 +5207,9 @@
         @Override
         public void endTransition(LayoutTransition transition, ViewGroup container,
                 View view, int transitionType) {
-            if (mLayoutSuppressed && !transition.isChangingLayout()) {
+            if (mLayoutCalledWhileSuppressed && !transition.isChangingLayout()) {
                 requestLayout();
-                mLayoutSuppressed = false;
+                mLayoutCalledWhileSuppressed = false;
             }
             if (transitionType == LayoutTransition.DISAPPEARING && mTransitioningViews != null) {
                 endViewTransition(view);
@@ -5212,6 +5218,24 @@
     };
 
     /**
+     * Tells this ViewGroup to suppress all layout() calls until layout
+     * suppression is disabled with a later call to suppressLayout(false).
+     * When layout suppression is disabled, a requestLayout() call is sent
+     * if layout() was attempted while layout was being suppressed.
+     *
+     * @hide
+     */
+    public void suppressLayout(boolean suppress) {
+        mSuppressLayout = suppress;
+        if (!suppress) {
+            if (mLayoutCalledWhileSuppressed) {
+                requestLayout();
+                mLayoutCalledWhileSuppressed = false;
+            }
+        }
+    }
+
+    /**
      * {@inheritDoc}
      */
     @Override
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 7f9969c..855b6d4 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -325,7 +325,8 @@
 
     PendingEvent mPendingEventPool;
     int mPendingEventPoolSize;
-    PendingEvent mFirstPendingEvent;
+    PendingEvent mPendingEventHead;
+    PendingEvent mPendingEventTail;
 
     // -----------------------------------------------------------
     
@@ -366,18 +367,14 @@
                         if (mBindSequence < 0 || mBindSequence != res.sequence) {
                             Log.w(TAG, "Ignoring onBind: cur seq=" + mBindSequence
                                     + ", given seq=" + res.sequence);
-                            if (res.channel != null) {
+                            if (res.channel != null && res.channel != mCurChannel) {
                                 res.channel.dispose();
                             }
                             return;
                         }
-                        
-                        flushPendingEventsLocked();
+
+                        setInputChannelLocked(res.channel);
                         mCurMethod = res.method;
-                        if (mCurChannel != null) {
-                            mCurChannel.dispose();
-                        }
-                        mCurChannel = res.channel;
                         mCurId = res.id;
                         mBindSequence = res.sequence;
                     }
@@ -719,20 +716,26 @@
      */
     void clearBindingLocked() {
         clearConnectionLocked();
-        flushPendingEventsLocked();
+        setInputChannelLocked(null);
         mBindSequence = -1;
         mCurId = null;
         mCurMethod = null;
-        if (mCurSender != null) {
-            mCurSender.dispose();
-            mCurSender = null;
-        }
-        if (mCurChannel != null) {
-            mCurChannel.dispose();
-            mCurChannel = null;
+    }
+
+    void setInputChannelLocked(InputChannel channel) {
+        if (mCurChannel != channel) {
+            if (mCurSender != null) {
+                flushPendingEventsLocked();
+                mCurSender.dispose();
+                mCurSender = null;
+            }
+            if (mCurChannel != null) {
+                mCurChannel.dispose();
+            }
+            mCurChannel = channel;
         }
     }
-    
+
     /**
      * Reset all of the state associated with a served view being connected
      * to an input method
@@ -1174,15 +1177,12 @@
                 if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res);
                 if (res != null) {
                     if (res.id != null) {
+                        setInputChannelLocked(res.channel);
                         mBindSequence = res.sequence;
                         mCurMethod = res.method;
-                        if (mCurChannel != null) {
-                            mCurChannel.dispose();
-                        }
-                        mCurChannel = res.channel;
                         mCurId = res.id;
                     } else {
-                        if (res.channel != null) {
+                        if (res.channel != null && res.channel != mCurChannel) {
                             res.channel.dispose();
                         }
                         if (mCurMethod == null) {
@@ -1655,8 +1655,13 @@
     private void enqueuePendingEventLocked(
             long startTime, int seq, String inputMethodId, FinishedEventCallback callback) {
         PendingEvent p = obtainPendingEventLocked(startTime, seq, inputMethodId, callback);
-        p.mNext = mFirstPendingEvent;
-        mFirstPendingEvent = p;
+        if (mPendingEventTail != null) {
+            mPendingEventTail.mNext = p;
+            mPendingEventTail = p;
+        } else {
+            mPendingEventHead = p;
+            mPendingEventTail = p;
+        }
 
         Message msg = mH.obtainMessage(MSG_EVENT_TIMEOUT, seq, 0, p);
         msg.setAsynchronous(true);
@@ -1664,12 +1669,15 @@
     }
 
     private PendingEvent dequeuePendingEventLocked(int seq) {
-        PendingEvent p = mFirstPendingEvent;
+        PendingEvent p = mPendingEventHead;
         if (p == null) {
             return null;
         }
         if (p.mSeq == seq) {
-            mFirstPendingEvent = p.mNext;
+            mPendingEventHead = p.mNext;
+            if (mPendingEventHead == null) {
+                mPendingEventTail = null;
+            }
         } else {
             PendingEvent prev;
             do {
@@ -1680,6 +1688,9 @@
                 }
             } while (p.mSeq != seq);
             prev.mNext = p.mNext;
+            if (mPendingEventTail == p) {
+                mPendingEventTail = prev;
+            }
         }
         p.mNext = null;
         return p;
@@ -1716,25 +1727,13 @@
 
     private void flushPendingEventsLocked() {
         mH.removeMessages(MSG_EVENT_TIMEOUT);
-        PendingEvent curr, prev, next;
-        curr = mFirstPendingEvent;
-        prev = null;
-        while (curr != null) {
-            next = curr.mNext;
-            curr.mNext = prev;
-            prev = curr;
-            curr = next;
-        }
-        curr = prev;
-        prev = null;
-        while (curr != null) {
-            Message msg = mH.obtainMessage(MSG_EVENT_TIMEOUT, curr.mSeq, 0, curr);
+
+        PendingEvent p = mPendingEventHead;
+        while (p != null) {
+            Message msg = mH.obtainMessage(MSG_EVENT_TIMEOUT, p.mSeq, 0, p);
             msg.setAsynchronous(true);
             mH.sendMessage(msg);
-            next = curr.mNext;
-            curr.mNext = prev;
-            prev = curr;
-            curr = next;
+            p = p.mNext;
         }
     }
 
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 79fc51e..83e2e79 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -34,6 +34,7 @@
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.StrictMode;
 import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Log;
@@ -2263,8 +2264,13 @@
      * @param value The value to pass to the method.
      */
     public void setUri(int viewId, String methodName, Uri value) {
-        // Resolve any filesystem path before sending remotely
-        value = value.getCanonicalUri();
+        if (value != null) {
+            // Resolve any filesystem path before sending remotely
+            value = value.getCanonicalUri();
+            if (StrictMode.vmFileUriExposureEnabled()) {
+                value.checkFileUriExposed("RemoteViews.setUri()");
+            }
+        }
         addAction(new ReflectionAction(viewId, methodName, ReflectionAction.URI, value));
     }
 
diff --git a/core/java/android/widget/ShareActionProvider.java b/core/java/android/widget/ShareActionProvider.java
index 4045497..62afd2e 100644
--- a/core/java/android/widget/ShareActionProvider.java
+++ b/core/java/android/widget/ShareActionProvider.java
@@ -39,31 +39,26 @@
  * <p>
  * Here is how to use the action provider with custom backing file in a {@link MenuItem}:
  * </p>
- * <p>
  * <pre>
- * <code>
- *  // In Activity#onCreateOptionsMenu
- *  public boolean onCreateOptionsMenu(Menu menu) {
- *      // Get the menu item.
- *      MenuItem menuItem = menu.findItem(R.id.my_menu_item);
- *      // Get the provider and hold onto it to set/change the share intent.
- *      mShareActionProvider = (ShareActionProvider) menuItem.getActionProvider();
- *      // Set history different from the default before getting the action
- *      // view since a call to {@link MenuItem#getActionView() MenuItem.getActionView()} calls
- *      // {@link ActionProvider#onCreateActionView()} which uses the backing file name. Omit this
- *      // line if using the default share history file is desired.
- *      mShareActionProvider.setShareHistoryFileName("custom_share_history.xml");
- *      . . .
- *  }
+ * // In Activity#onCreateOptionsMenu
+ * public boolean onCreateOptionsMenu(Menu menu) {
+ *     // Get the menu item.
+ *     MenuItem menuItem = menu.findItem(R.id.my_menu_item);
+ *     // Get the provider and hold onto it to set/change the share intent.
+ *     mShareActionProvider = (ShareActionProvider) menuItem.getActionProvider();
+ *     // Set history different from the default before getting the action
+ *     // view since a call to {@link MenuItem#getActionView() MenuItem.getActionView()} calls
+ *     // {@link ActionProvider#onCreateActionView()} which uses the backing file name. Omit this
+ *     // line if using the default share history file is desired.
+ *     mShareActionProvider.setShareHistoryFileName("custom_share_history.xml");
+ *     . . .
+ * }
  *
- *  // Somewhere in the application.
- *  public void doShare(Intent shareIntent) {
- *      // When you want to share set the share intent.
- *      mShareActionProvider.setShareIntent(shareIntent);
- *  }
- * </pre>
- * </code>
- * </p>
+ * // Somewhere in the application.
+ * public void doShare(Intent shareIntent) {
+ *     // When you want to share set the share intent.
+ *     mShareActionProvider.setShareIntent(shareIntent);
+ * }</pre>
  * <p>
  * <strong>Note:</strong> While the sample snippet demonstrates how to use this provider
  * in the context of a menu item, the use of the provider is not limited to menu items.
@@ -245,9 +240,9 @@
      * call {@link android.app.Activity#invalidateOptionsMenu()} to recreate the
      * action view. You should <strong>not</strong> call
      * {@link android.app.Activity#invalidateOptionsMenu()} from
-     * {@link android.app.Activity#onCreateOptionsMenu(Menu)}."
-     * <p>
-     * <code>
+     * {@link android.app.Activity#onCreateOptionsMenu(Menu)}.
+     * </p>
+     * <pre>
      * private void doShare(Intent intent) {
      *     if (IMAGE.equals(intent.getMimeType())) {
      *         mShareActionProvider.setHistoryFileName(SHARE_IMAGE_HISTORY_FILE_NAME);
@@ -256,9 +251,7 @@
      *     }
      *     mShareActionProvider.setIntent(intent);
      *     invalidateOptionsMenu();
-     * }
-     * <code>
-     *
+     * }</pre>
      * @param shareHistoryFile The share history file name.
      */
     public void setShareHistoryFileName(String shareHistoryFile) {
@@ -269,16 +262,11 @@
     /**
      * Sets an intent with information about the share action. Here is a
      * sample for constructing a share intent:
-     * <p>
      * <pre>
-     * <code>
-     *  Intent shareIntent = new Intent(Intent.ACTION_SEND);
-     *  shareIntent.setType("image/*");
-     *  Uri uri = Uri.fromFile(new File(getFilesDir(), "foo.jpg"));
-     *  shareIntent.putExtra(Intent.EXTRA_STREAM, uri.toString());
-     * </pre>
-     * </code>
-     * </p>
+     * Intent shareIntent = new Intent(Intent.ACTION_SEND);
+     * shareIntent.setType("image/*");
+     * Uri uri = Uri.fromFile(new File(getFilesDir(), "foo.jpg"));
+     * shareIntent.putExtra(Intent.EXTRA_STREAM, uri.toString());</pre>
      *
      * @param shareIntent The share intent.
      *
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index d1db230..b99b39a 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -36,7 +36,6 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.res.Configuration;
 import android.content.res.TypedArray;
-import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -125,6 +124,7 @@
     private boolean mWasHomeEnabled; // Was it enabled before action view expansion?
 
     private MenuBuilder mOptionsMenu;
+    private boolean mMenuPrepared;
     
     private ActionBarContextView mContextView;
 
@@ -164,7 +164,10 @@
 
     private final OnClickListener mUpClickListener = new OnClickListener() {
         public void onClick(View v) {
-            mWindowCallback.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, mLogoNavItem);
+            if (mMenuPrepared) {
+                // Only invoke the window callback if the options menu has been initialized.
+                mWindowCallback.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, mLogoNavItem);
+            }
         }
     };
 
@@ -402,6 +405,10 @@
         mCallback = callback;
     }
 
+    public void setMenuPrepared() {
+        mMenuPrepared = true;
+    }
+
     public void setMenu(Menu menu, MenuPresenter.Callback cb) {
         if (menu == mOptionsMenu) return;
 
diff --git a/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_focused_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_focused_dark.9.png
new file mode 100644
index 0000000..7c5826f
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_focused_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_focused_light.9.png b/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_focused_light.9.png
new file mode 100644
index 0000000..974a292
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_focused_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_normal_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_normal_dark.9.png
new file mode 100644
index 0000000..b3196c9
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_normal_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_normal_light.9.png b/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_normal_light.9.png
new file mode 100644
index 0000000..1f833d3
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_normal_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_pressed_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_pressed_dark.9.png
new file mode 100644
index 0000000..e969abc
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_pressed_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_pressed_light.9.png b/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_pressed_light.9.png
new file mode 100644
index 0000000..3adbc84
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/quickcontact_badge_overlay_pressed_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_focused_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_focused_dark.9.png
new file mode 100644
index 0000000..a321836
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_focused_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_focused_light.9.png b/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_focused_light.9.png
new file mode 100644
index 0000000..4c5d692
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_focused_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_normal_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_normal_dark.9.png
new file mode 100644
index 0000000..6199dc5
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_normal_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_normal_light.9.png b/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_normal_light.9.png
new file mode 100644
index 0000000..1b0905a
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_normal_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_pressed_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_pressed_dark.9.png
new file mode 100644
index 0000000..c6d7868
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_pressed_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_pressed_light.9.png b/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_pressed_light.9.png
new file mode 100644
index 0000000..179644c
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/quickcontact_badge_overlay_pressed_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_focused_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_focused_dark.9.png
new file mode 100644
index 0000000..039a056
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_focused_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_focused_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_focused_light.9.png
new file mode 100644
index 0000000..c8d68c5
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_focused_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_normal_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_normal_dark.9.png
new file mode 100644
index 0000000..1fef1ad
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_normal_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_normal_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_normal_light.9.png
new file mode 100644
index 0000000..6b22d44
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_normal_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_pressed_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_pressed_dark.9.png
new file mode 100644
index 0000000..c219527
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_pressed_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_pressed_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_pressed_light.9.png
new file mode 100644
index 0000000..2a1d508
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/quickcontact_badge_overlay_pressed_light.9.png
Binary files differ
diff --git a/data/sounds/AllAudio.mk b/data/sounds/AllAudio.mk
index e403205..8b03bf7 100644
--- a/data/sounds/AllAudio.mk
+++ b/data/sounds/AllAudio.mk
@@ -1,5 +1,4 @@
-#
-# Copyright (C) 2009 The Android Open Source Project
+# Copyright 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.
@@ -12,13 +11,222 @@
 # 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.
-#
 
-$(call inherit-product, frameworks/base/data/sounds/OriginalAudio.mk)
-$(call inherit-product, frameworks/base/data/sounds/AudioPackage2.mk)
-$(call inherit-product, frameworks/base/data/sounds/AudioPackage3.mk)
-$(call inherit-product, frameworks/base/data/sounds/AudioPackage4.mk)
-$(call inherit-product, frameworks/base/data/sounds/AudioPackage5.mk)
-$(call inherit-product, frameworks/base/data/sounds/AudioPackage6.mk)
-$(call inherit-product, frameworks/base/data/sounds/AudioPackage7.mk)
-$(call inherit-product, frameworks/base/data/sounds/AudioPackage7alt.mk)
+LOCAL_PATH := frameworks/base/data/sounds
+
+PRODUCT_COPY_FILES += \
+    $(LOCAL_PATH)/Alarm_Beep_01.ogg:system/media/audio/alarms/Alarm_Beep_01.ogg \
+    $(LOCAL_PATH)/Alarm_Beep_02.ogg:system/media/audio/alarms/Alarm_Beep_02.ogg \
+    $(LOCAL_PATH)/Alarm_Beep_03.ogg:system/media/audio/alarms/Alarm_Beep_03.ogg \
+    $(LOCAL_PATH)/Alarm_Buzzer.ogg:system/media/audio/alarms/Alarm_Buzzer.ogg \
+    $(LOCAL_PATH)/Alarm_Classic.ogg:system/media/audio/alarms/Alarm_Classic.ogg \
+    $(LOCAL_PATH)/Alarm_Rooster_02.ogg:system/media/audio/alarms/Alarm_Rooster_02.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Argon.ogg:system/media/audio/alarms/Argon.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Barium.ogg:system/media/audio/alarms/Barium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Carbon.ogg:system/media/audio/alarms/Carbon.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Cesium.ogg:system/media/audio/alarms/Cesium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Fermium.ogg:system/media/audio/alarms/Fermium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Hassium.ogg:system/media/audio/alarms/Hassium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Helium.ogg:system/media/audio/alarms/Helium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Krypton.ogg:system/media/audio/alarms/Krypton.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Neon.ogg:system/media/audio/alarms/Neon.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Neptunium.ogg:system/media/audio/alarms/Neptunium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Nobelium.ogg:system/media/audio/alarms/Nobelium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Osmium.ogg:system/media/audio/alarms/Osmium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Oxygen.ogg:system/media/audio/alarms/Oxygen.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Platinum.ogg:system/media/audio/alarms/Platinum.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Plutonium.ogg:system/media/audio/alarms/Plutonium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Promethium.ogg:system/media/audio/alarms/Promethium.ogg \
+    $(LOCAL_PATH)/alarms/ogg/Scandium.ogg:system/media/audio/alarms/Scandium.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Adara.ogg:system/media/audio/notifications/Adara.ogg \
+    $(LOCAL_PATH)/notifications/Aldebaran.ogg:system/media/audio/notifications/Aldebaran.ogg \
+    $(LOCAL_PATH)/notifications/Altair.ogg:system/media/audio/notifications/Altair.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Alya.ogg:system/media/audio/notifications/Alya.ogg \
+    $(LOCAL_PATH)/notifications/Antares.ogg:system/media/audio/notifications/Antares.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Antimony.ogg:system/media/audio/notifications/Antimony.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Arcturus.ogg:system/media/audio/notifications/Arcturus.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Argon.ogg:system/media/audio/notifications/Argon.ogg \
+    $(LOCAL_PATH)/notifications/Beat_Box_Android.ogg:system/media/audio/notifications/Beat_Box_Android.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Bellatrix.ogg:system/media/audio/notifications/Bellatrix.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Beryllium.ogg:system/media/audio/notifications/Beryllium.ogg \
+    $(LOCAL_PATH)/notifications/Betelgeuse.ogg:system/media/audio/notifications/Betelgeuse.ogg \
+    $(LOCAL_PATH)/newwavelabs/CaffeineSnake.ogg:system/media/audio/notifications/CaffeineSnake.ogg \
+    $(LOCAL_PATH)/notifications/Canopus.ogg:system/media/audio/notifications/Canopus.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Capella.ogg:system/media/audio/notifications/Capella.ogg \
+    $(LOCAL_PATH)/notifications/Castor.ogg:system/media/audio/notifications/Castor.ogg \
+    $(LOCAL_PATH)/notifications/ogg/CetiAlpha.ogg:system/media/audio/notifications/CetiAlpha.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Cobalt.ogg:system/media/audio/notifications/Cobalt.ogg \
+    $(LOCAL_PATH)/notifications/Cricket.ogg:system/media/audio/notifications/Cricket.ogg \
+    $(LOCAL_PATH)/newwavelabs/DearDeer.ogg:system/media/audio/notifications/DearDeer.ogg \
+    $(LOCAL_PATH)/notifications/Deneb.ogg:system/media/audio/notifications/Deneb.ogg \
+    $(LOCAL_PATH)/notifications/Doink.ogg:system/media/audio/notifications/Doink.ogg \
+    $(LOCAL_PATH)/newwavelabs/DontPanic.ogg:system/media/audio/notifications/DontPanic.ogg \
+    $(LOCAL_PATH)/notifications/Drip.ogg:system/media/audio/notifications/Drip.ogg \
+    $(LOCAL_PATH)/notifications/Electra.ogg:system/media/audio/notifications/Electra.ogg \
+    $(LOCAL_PATH)/F1_MissedCall.ogg:system/media/audio/notifications/F1_MissedCall.ogg \
+    $(LOCAL_PATH)/F1_New_MMS.ogg:system/media/audio/notifications/F1_New_MMS.ogg \
+    $(LOCAL_PATH)/F1_New_SMS.ogg:system/media/audio/notifications/F1_New_SMS.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Fluorine.ogg:system/media/audio/notifications/Fluorine.ogg \
+    $(LOCAL_PATH)/notifications/Fomalhaut.ogg:system/media/audio/notifications/Fomalhaut.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Gallium.ogg:system/media/audio/notifications/Gallium.ogg \
+    $(LOCAL_PATH)/notifications/Heaven.ogg:system/media/audio/notifications/Heaven.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Helium.ogg:system/media/audio/notifications/Helium.ogg \
+    $(LOCAL_PATH)/newwavelabs/Highwire.ogg:system/media/audio/notifications/Highwire.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Hojus.ogg:system/media/audio/notifications/Hojus.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Iridium.ogg:system/media/audio/notifications/Iridium.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Krypton.ogg:system/media/audio/notifications/Krypton.ogg \
+    $(LOCAL_PATH)/newwavelabs/KzurbSonar.ogg:system/media/audio/notifications/KzurbSonar.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Lalande.ogg:system/media/audio/notifications/Lalande.ogg \
+    $(LOCAL_PATH)/notifications/Merope.ogg:system/media/audio/notifications/Merope.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Mira.ogg:system/media/audio/notifications/Mira.ogg \
+    $(LOCAL_PATH)/newwavelabs/OnTheHunt.ogg:system/media/audio/notifications/OnTheHunt.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Palladium.ogg:system/media/audio/notifications/Palladium.ogg \
+    $(LOCAL_PATH)/notifications/Plastic_Pipe.ogg:system/media/audio/notifications/Plastic_Pipe.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Polaris.ogg:system/media/audio/notifications/Polaris.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Pollux.ogg:system/media/audio/notifications/Pollux.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Procyon.ogg:system/media/audio/notifications/Procyon.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Proxima.ogg:system/media/audio/notifications/Proxima.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Radon.ogg:system/media/audio/notifications/Radon.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Rubidium.ogg:system/media/audio/notifications/Rubidium.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Selenium.ogg:system/media/audio/notifications/Selenium.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Shaula.ogg:system/media/audio/notifications/Shaula.ogg \
+    $(LOCAL_PATH)/notifications/Sirrah.ogg:system/media/audio/notifications/Sirrah.ogg \
+    $(LOCAL_PATH)/notifications/SpaceSeed.ogg:system/media/audio/notifications/SpaceSeed.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Spica.ogg:system/media/audio/notifications/Spica.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Strontium.ogg:system/media/audio/notifications/Strontium.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Syrma.ogg:system/media/audio/notifications/Syrma.ogg \
+    $(LOCAL_PATH)/notifications/TaDa.ogg:system/media/audio/notifications/TaDa.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Talitha.ogg:system/media/audio/notifications/Talitha.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Tejat.ogg:system/media/audio/notifications/Tejat.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Thallium.ogg:system/media/audio/notifications/Thallium.ogg \
+    $(LOCAL_PATH)/notifications/Tinkerbell.ogg:system/media/audio/notifications/Tinkerbell.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Upsilon.ogg:system/media/audio/notifications/Upsilon.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Vega.ogg:system/media/audio/notifications/Vega.ogg \
+    $(LOCAL_PATH)/newwavelabs/Voila.ogg:system/media/audio/notifications/Voila.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Xenon.ogg:system/media/audio/notifications/Xenon.ogg \
+    $(LOCAL_PATH)/notifications/ogg/Zirconium.ogg:system/media/audio/notifications/Zirconium.ogg \
+    $(LOCAL_PATH)/notifications/arcturus.ogg:system/media/audio/notifications/arcturus.ogg \
+    $(LOCAL_PATH)/notifications/moonbeam.ogg:system/media/audio/notifications/moonbeam.ogg \
+    $(LOCAL_PATH)/notifications/pixiedust.ogg:system/media/audio/notifications/pixiedust.ogg \
+    $(LOCAL_PATH)/notifications/pizzicato.ogg:system/media/audio/notifications/pizzicato.ogg \
+    $(LOCAL_PATH)/notifications/regulus.ogg:system/media/audio/notifications/regulus.ogg \
+    $(LOCAL_PATH)/notifications/sirius.ogg:system/media/audio/notifications/sirius.ogg \
+    $(LOCAL_PATH)/notifications/tweeters.ogg:system/media/audio/notifications/tweeters.ogg \
+    $(LOCAL_PATH)/notifications/vega.ogg:system/media/audio/notifications/vega.ogg \
+    $(LOCAL_PATH)/ringtones/ANDROMEDA.ogg:system/media/audio/ringtones/ANDROMEDA.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:system/media/audio/ringtones/Andromeda.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:system/media/audio/ringtones/Aquila.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:system/media/audio/ringtones/ArgoNavis.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Atria.ogg:system/media/audio/ringtones/Atria.ogg \
+    $(LOCAL_PATH)/ringtones/BOOTES.ogg:system/media/audio/ringtones/BOOTES.ogg \
+    $(LOCAL_PATH)/newwavelabs/Backroad.ogg:system/media/audio/ringtones/Backroad.ogg \
+    $(LOCAL_PATH)/newwavelabs/BeatPlucker.ogg:system/media/audio/ringtones/BeatPlucker.ogg \
+    $(LOCAL_PATH)/newwavelabs/BentleyDubs.ogg:system/media/audio/ringtones/BentleyDubs.ogg \
+    $(LOCAL_PATH)/newwavelabs/Big_Easy.ogg:system/media/audio/ringtones/Big_Easy.ogg \
+    $(LOCAL_PATH)/newwavelabs/BirdLoop.ogg:system/media/audio/ringtones/BirdLoop.ogg \
+    $(LOCAL_PATH)/newwavelabs/Bollywood.ogg:system/media/audio/ringtones/Bollywood.ogg \
+    $(LOCAL_PATH)/newwavelabs/BussaMove.ogg:system/media/audio/ringtones/BussaMove.ogg \
+    $(LOCAL_PATH)/ringtones/CANISMAJOR.ogg:system/media/audio/ringtones/CANISMAJOR.ogg \
+    $(LOCAL_PATH)/ringtones/CASSIOPEIA.ogg:system/media/audio/ringtones/CASSIOPEIA.ogg \
+    $(LOCAL_PATH)/newwavelabs/Cairo.ogg:system/media/audio/ringtones/Cairo.ogg \
+    $(LOCAL_PATH)/newwavelabs/Calypso_Steel.ogg:system/media/audio/ringtones/Calypso_Steel.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/CanisMajor.ogg:system/media/audio/ringtones/CanisMajor.ogg \
+    $(LOCAL_PATH)/newwavelabs/CaribbeanIce.ogg:system/media/audio/ringtones/CaribbeanIce.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Carina.ogg:system/media/audio/ringtones/Carina.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:system/media/audio/ringtones/Centaurus.ogg \
+    $(LOCAL_PATH)/newwavelabs/Champagne_Edition.ogg:system/media/audio/ringtones/Champagne_Edition.ogg \
+    $(LOCAL_PATH)/newwavelabs/Club_Cubano.ogg:system/media/audio/ringtones/Club_Cubano.ogg \
+    $(LOCAL_PATH)/newwavelabs/CrayonRock.ogg:system/media/audio/ringtones/CrayonRock.ogg \
+    $(LOCAL_PATH)/newwavelabs/CrazyDream.ogg:system/media/audio/ringtones/CrazyDream.ogg \
+    $(LOCAL_PATH)/newwavelabs/CurveBall.ogg:system/media/audio/ringtones/CurveBall.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Cygnus.ogg:system/media/audio/ringtones/Cygnus.ogg \
+    $(LOCAL_PATH)/newwavelabs/DancinFool.ogg:system/media/audio/ringtones/DancinFool.ogg \
+    $(LOCAL_PATH)/newwavelabs/Ding.ogg:system/media/audio/ringtones/Ding.ogg \
+    $(LOCAL_PATH)/newwavelabs/DonMessWivIt.ogg:system/media/audio/ringtones/DonMessWivIt.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Draco.ogg:system/media/audio/ringtones/Draco.ogg \
+    $(LOCAL_PATH)/newwavelabs/DreamTheme.ogg:system/media/audio/ringtones/DreamTheme.ogg \
+    $(LOCAL_PATH)/newwavelabs/Eastern_Sky.ogg:system/media/audio/ringtones/Eastern_Sky.ogg \
+    $(LOCAL_PATH)/newwavelabs/Enter_the_Nexus.ogg:system/media/audio/ringtones/Enter_the_Nexus.ogg \
+    $(LOCAL_PATH)/ringtones/Eridani.ogg:system/media/audio/ringtones/Eridani.ogg \
+    $(LOCAL_PATH)/newwavelabs/EtherShake.ogg:system/media/audio/ringtones/EtherShake.ogg \
+    $(LOCAL_PATH)/ringtones/FreeFlight.ogg:system/media/audio/ringtones/FreeFlight.ogg \
+    $(LOCAL_PATH)/newwavelabs/FriendlyGhost.ogg:system/media/audio/ringtones/FriendlyGhost.ogg \
+    $(LOCAL_PATH)/newwavelabs/Funk_Yall.ogg:system/media/audio/ringtones/Funk_Yall.ogg \
+    $(LOCAL_PATH)/newwavelabs/GameOverGuitar.ogg:system/media/audio/ringtones/GameOverGuitar.ogg \
+    $(LOCAL_PATH)/newwavelabs/Gimme_Mo_Town.ogg:system/media/audio/ringtones/Gimme_Mo_Town.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Girtab.ogg:system/media/audio/ringtones/Girtab.ogg \
+    $(LOCAL_PATH)/newwavelabs/Glacial_Groove.ogg:system/media/audio/ringtones/Glacial_Groove.ogg \
+    $(LOCAL_PATH)/newwavelabs/Growl.ogg:system/media/audio/ringtones/Growl.ogg \
+    $(LOCAL_PATH)/newwavelabs/HalfwayHome.ogg:system/media/audio/ringtones/HalfwayHome.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:system/media/audio/ringtones/Hydra.ogg \
+    $(LOCAL_PATH)/newwavelabs/InsertCoin.ogg:system/media/audio/ringtones/InsertCoin.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Kuma.ogg:system/media/audio/ringtones/Kuma.ogg \
+    $(LOCAL_PATH)/newwavelabs/LoopyLounge.ogg:system/media/audio/ringtones/LoopyLounge.ogg \
+    $(LOCAL_PATH)/newwavelabs/LoveFlute.ogg:system/media/audio/ringtones/LoveFlute.ogg \
+    $(LOCAL_PATH)/ringtones/Lyra.ogg:system/media/audio/ringtones/Lyra.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Machina.ogg:system/media/audio/ringtones/Machina.ogg \
+    $(LOCAL_PATH)/newwavelabs/MidEvilJaunt.ogg:system/media/audio/ringtones/MidEvilJaunt.ogg \
+    $(LOCAL_PATH)/newwavelabs/MildlyAlarming.ogg:system/media/audio/ringtones/MildlyAlarming.ogg \
+    $(LOCAL_PATH)/newwavelabs/Nairobi.ogg:system/media/audio/ringtones/Nairobi.ogg \
+    $(LOCAL_PATH)/newwavelabs/Nassau.ogg:system/media/audio/ringtones/Nassau.ogg \
+    $(LOCAL_PATH)/newwavelabs/NewPlayer.ogg:system/media/audio/ringtones/NewPlayer.ogg \
+    $(LOCAL_PATH)/newwavelabs/No_Limits.ogg:system/media/audio/ringtones/No_Limits.ogg \
+    $(LOCAL_PATH)/newwavelabs/Noises1.ogg:system/media/audio/ringtones/Noises1.ogg \
+    $(LOCAL_PATH)/newwavelabs/Noises2.ogg:system/media/audio/ringtones/Noises2.ogg \
+    $(LOCAL_PATH)/newwavelabs/Noises3.ogg:system/media/audio/ringtones/Noises3.ogg \
+    $(LOCAL_PATH)/newwavelabs/OrganDub.ogg:system/media/audio/ringtones/OrganDub.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Orion.ogg:system/media/audio/ringtones/Orion.ogg \
+    $(LOCAL_PATH)/ringtones/PERSEUS.ogg:system/media/audio/ringtones/PERSEUS.ogg \
+    $(LOCAL_PATH)/newwavelabs/Paradise_Island.ogg:system/media/audio/ringtones/Paradise_Island.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:system/media/audio/ringtones/Pegasus.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Perseus.ogg:system/media/audio/ringtones/Perseus.ogg \
+    $(LOCAL_PATH)/newwavelabs/Playa.ogg:system/media/audio/ringtones/Playa.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Pyxis.ogg:system/media/audio/ringtones/Pyxis.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Rasalas.ogg:system/media/audio/ringtones/Rasalas.ogg \
+    $(LOCAL_PATH)/newwavelabs/Revelation.ogg:system/media/audio/ringtones/Revelation.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Rigel.ogg:system/media/audio/ringtones/Rigel.ogg \
+    $(LOCAL_PATH)/Ring_Classic_02.ogg:system/media/audio/ringtones/Ring_Classic_02.ogg \
+    $(LOCAL_PATH)/Ring_Digital_02.ogg:system/media/audio/ringtones/Ring_Digital_02.ogg \
+    $(LOCAL_PATH)/Ring_Synth_02.ogg:system/media/audio/ringtones/Ring_Synth_02.ogg \
+    $(LOCAL_PATH)/Ring_Synth_04.ogg:system/media/audio/ringtones/Ring_Synth_04.ogg \
+    $(LOCAL_PATH)/newwavelabs/Road_Trip.ogg:system/media/audio/ringtones/Road_Trip.ogg \
+    $(LOCAL_PATH)/newwavelabs/RomancingTheTone.ogg:system/media/audio/ringtones/RomancingTheTone.ogg \
+    $(LOCAL_PATH)/newwavelabs/Safari.ogg:system/media/audio/ringtones/Safari.ogg \
+    $(LOCAL_PATH)/newwavelabs/Savannah.ogg:system/media/audio/ringtones/Savannah.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:system/media/audio/ringtones/Scarabaeus.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:system/media/audio/ringtones/Sceptrum.ogg \
+    $(LOCAL_PATH)/newwavelabs/Seville.ogg:system/media/audio/ringtones/Seville.ogg \
+    $(LOCAL_PATH)/newwavelabs/Shes_All_That.ogg:system/media/audio/ringtones/Shes_All_That.ogg \
+    $(LOCAL_PATH)/newwavelabs/SilkyWay.ogg:system/media/audio/ringtones/SilkyWay.ogg \
+    $(LOCAL_PATH)/newwavelabs/SitarVsSitar.ogg:system/media/audio/ringtones/SitarVsSitar.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:system/media/audio/ringtones/Solarium.ogg \
+    $(LOCAL_PATH)/newwavelabs/SpringyJalopy.ogg:system/media/audio/ringtones/SpringyJalopy.ogg \
+    $(LOCAL_PATH)/newwavelabs/Steppin_Out.ogg:system/media/audio/ringtones/Steppin_Out.ogg \
+    $(LOCAL_PATH)/newwavelabs/Terminated.ogg:system/media/audio/ringtones/Terminated.ogg \
+    $(LOCAL_PATH)/ringtones/Testudo.ogg:system/media/audio/ringtones/Testudo.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Themos.ogg:system/media/audio/ringtones/Themos.ogg \
+    $(LOCAL_PATH)/newwavelabs/Third_Eye.ogg:system/media/audio/ringtones/Third_Eye.ogg \
+    $(LOCAL_PATH)/newwavelabs/Thunderfoot.ogg:system/media/audio/ringtones/Thunderfoot.ogg \
+    $(LOCAL_PATH)/newwavelabs/TwirlAway.ogg:system/media/audio/ringtones/TwirlAway.ogg \
+    $(LOCAL_PATH)/ringtones/URSAMINOR.ogg:system/media/audio/ringtones/URSAMINOR.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/UrsaMinor.ogg:system/media/audio/ringtones/UrsaMinor.ogg \
+    $(LOCAL_PATH)/newwavelabs/VeryAlarmed.ogg:system/media/audio/ringtones/VeryAlarmed.ogg \
+    $(LOCAL_PATH)/ringtones/Vespa.ogg:system/media/audio/ringtones/Vespa.ogg \
+    $(LOCAL_PATH)/newwavelabs/World.ogg:system/media/audio/ringtones/World.ogg \
+    $(LOCAL_PATH)/ringtones/ogg/Zeta.ogg:system/media/audio/ringtones/Zeta.ogg \
+    $(LOCAL_PATH)/ringtones/hydra.ogg:system/media/audio/ringtones/hydra.ogg \
+    $(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \
+    $(LOCAL_PATH)/effects/ogg/Effect_Tick_48k.ogg:system/media/audio/ui/Effect_Tick.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressDelete_120_48k.ogg:system/media/audio/ui/KeypressDelete.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressReturn_120_48k.ogg:system/media/audio/ui/KeypressReturn.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressSpacebar_120_48k.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressStandard_120_48k.ogg:system/media/audio/ui/KeypressStandard.ogg \
+    $(LOCAL_PATH)/effects/ogg/Lock.ogg:system/media/audio/ui/Lock.ogg \
+    $(LOCAL_PATH)/effects/ogg/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
+    $(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg \
+    $(LOCAL_PATH)/effects/ogg/Unlock.ogg:system/media/audio/ui/Unlock.ogg \
+    $(LOCAL_PATH)/effects/ogg/VideoRecord_48k.ogg:system/media/audio/ui/VideoRecord.ogg \
+    $(LOCAL_PATH)/effects/ogg/WirelessChargingStarted.ogg:system/media/audio/ui/WirelessChargingStarted.ogg \
+    $(LOCAL_PATH)/effects/ogg/camera_click_48k.ogg:system/media/audio/ui/camera_click.ogg \
+    $(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
+
diff --git a/data/sounds/generate-all-audio.sh b/data/sounds/generate-all-audio.sh
new file mode 100755
index 0000000..6f42f5a
--- /dev/null
+++ b/data/sounds/generate-all-audio.sh
@@ -0,0 +1,59 @@
+#!/usr/bin/env bash
+
+# Copyright 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.
+
+# This script regenerates AllAudio.mk based on the content of the other
+# makefiles.
+
+# It needs to be run from its location in the source tree.
+
+cat > AllAudio.mk << EOF
+# Copyright 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.
+
+LOCAL_PATH := frameworks/base/data/sounds
+
+PRODUCT_COPY_FILES += \\
+EOF
+
+cat OriginalAudio.mk AudioPackage*.mk |
+  grep \\\$\(LOCAL_PATH\).*: |
+  cut -d : -f 2 |
+  cut -d \  -f 1 |
+  sort -u |
+  while read DEST
+  do
+    echo -n \ \ \ \  >> AllAudio.mk
+    cat *.mk |
+      grep \\\$\(LOCAL_PATH\).*:$DEST |
+      tr -d \ \\t |
+      cut -d : -f 1 |
+      sort -u |
+      tail -n 1 |
+      tr -d \\n >> AllAudio.mk
+    echo :$DEST\ \\ >> AllAudio.mk
+  done
+echo >> AllAudio.mk
diff --git a/docs/html/design/patterns/widgets.jd b/docs/html/design/patterns/widgets.jd
index a5979ce..f2b0f4a 100644
--- a/docs/html/design/patterns/widgets.jd
+++ b/docs/html/design/patterns/widgets.jd
@@ -16,7 +16,7 @@
 <div class="layout-content-row">
   <div class="layout-content-col span-6">
     <h3>Collection widgets</h3>
-    <p>As the name implies, collection widgets specialize on displaying multitude elements of the same type, such as a collection of pictures from a gallery app, a collection of articles from a news app or a collection of emails/messages from a communication app. Collection widgets typically focus on two use cases: browsing the collection, and opening an element of the collection to its detail view for consumption. Collection widgets can scroll vertically.</p>
+    <p>As the name implies, collection widgets specialize in displaying multitude elements of the same type, such as a collection of pictures from a gallery app, a collection of articles from a news app or a collection of emails/messages from a communication app. Collection widgets typically focus on two use cases: browsing the collection, and opening an element of the collection to its detail view for consumption. Collection widgets can scroll vertically.</p>
   </div>
   <div class="layout-content-col span-3">
     <img src="{@docRoot}design/media/widgets_collection_gmail.png">
@@ -137,4 +137,4 @@
   <li>Choose the right widget type for your purpose.</li>
   <li>For resizable widgets, plan how the content for your widget should adapt to different sizes.</li>
   <li>Make your widget orientation and device independent by ensuring that the layout is capable of stretching and contracting.</li>
-</ul>
\ No newline at end of file
+</ul>
diff --git a/docs/html/guide/topics/manifest/meta-data-element.jd b/docs/html/guide/topics/manifest/meta-data-element.jd
index 85a871d..56a214c 100644
--- a/docs/html/guide/topics/manifest/meta-data-element.jd
+++ b/docs/html/guide/topics/manifest/meta-data-element.jd
@@ -80,7 +80,7 @@
 </tr><tr>
   <td>Color value, in the form "{@code #rgb}", "{@code #argb}", 
       "{@code #rrggbb}", or "{@code #aarrggbb}"</td>
-  <td>{@link android.os.Bundle#getString(String) getString()}</td>
+  <td>{@link android.os.Bundle#getInt(String) getInt()}</td>
 </tr><tr>
   <td>Float value, such as "{@code 1.23}"</td>
   <td>{@link android.os.Bundle#getFloat(String) getFloat()}</td>
diff --git a/docs/html/guide/topics/resources/layout-resource.jd b/docs/html/guide/topics/resources/layout-resource.jd
index 380ab15..366ddc8 100644
--- a/docs/html/guide/topics/resources/layout-resource.jd
+++ b/docs/html/guide/topics/resources/layout-resource.jd
@@ -135,7 +135,7 @@
     </dd>
   <dt id="requestfocus-element"><code>&lt;requestFocus&gt;</code></dt>
     <dd>Any element representing a {@link android.view.View} object can include this empty element,
-    which gives it's parent initial focus on the screen. You can have only one of these
+    which gives its parent initial focus on the screen. You can have only one of these
     elements per file.</dd>
 
   <dt id="include-element"><code>&lt;include&gt;</code></dt>
diff --git a/docs/html/training/basics/network-ops/connecting.jd b/docs/html/training/basics/network-ops/connecting.jd
index ac8d993..50a9e1b 100644
--- a/docs/html/training/basics/network-ops/connecting.jd
+++ b/docs/html/training/basics/network-ops/connecting.jd
@@ -136,7 +136,7 @@
             getSystemService(Context.CONNECTIVITY_SERVICE);
         NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
         if (networkInfo != null &amp;&amp; networkInfo.isConnected()) {
-            new DownloadWebpageText().execute(stringUrl);
+            new DownloadWebpageTask().execute(stringUrl);
         } else {
             textView.setText("No network connection available.");
         }
@@ -147,7 +147,7 @@
      // has been established, the AsyncTask downloads the contents of the webpage as
      // an InputStream. Finally, the InputStream is converted into a string, which is
      // displayed in the UI by the AsyncTask's onPostExecute method.
-     private class DownloadWebpageText extends AsyncTask<String, Void, String> {
+     private class DownloadWebpageTask extends AsyncTask&lt;String, Void, String&gt; {
         &#64;Override
         protected String doInBackground(String... urls) {
               
diff --git a/libs/hwui/Extensions.cpp b/libs/hwui/Extensions.cpp
index edc90fb..51aec8d 100644
--- a/libs/hwui/Extensions.cpp
+++ b/libs/hwui/Extensions.cpp
@@ -64,10 +64,32 @@
     mHas4BitStencil = hasExtension("GL_OES_stencil4");
 
     mExtensions = strdup(buffer);
+
+    const char* version = (const char*) glGetString(GL_VERSION);
+    mVersion = strdup(version);
+
+    // Section 6.1.5 of the OpenGL ES specification indicates the GL version
+    // string strictly follows this format:
+    //
+    // OpenGL<space>ES<space><version number><space><vendor-specific information>
+    //
+    // In addition section 6.1.5 describes the version number thusly:
+    //
+    // "The version number is either of the form major number.minor number or
+    // major number.minor number.release number, where the numbers all have one
+    // or more digits. The release number and vendor specific information are
+    // optional."
+
+    if (sscanf(version, "OpenGL ES %d.%d", &mVersionMajor, &mVersionMinor) !=2) {
+        // If we cannot parse the version number, assume OpenGL ES 2.0
+        mVersionMajor = 2;
+        mVersionMinor = 0;
+    }
 }
 
 Extensions::~Extensions() {
    free(mExtensions);
+   free(mVersion);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -80,6 +102,7 @@
 }
 
 void Extensions::dump() const {
+   ALOGD("%s", mVersion);
    ALOGD("Supported extensions:\n%s", mExtensions);
 }
 
diff --git a/libs/hwui/Extensions.h b/libs/hwui/Extensions.h
index a069a6a..54a3987 100644
--- a/libs/hwui/Extensions.h
+++ b/libs/hwui/Extensions.h
@@ -45,6 +45,9 @@
     inline bool has1BitStencil() const { return mHas1BitStencil; }
     inline bool has4BitStencil() const { return mHas4BitStencil; }
 
+    inline int getMajorGlVersion() const { return mVersionMajor; }
+    inline int getMinorGlVersion() const { return mVersionMinor; }
+
     bool hasExtension(const char* extension) const;
 
     void dump() const;
@@ -55,6 +58,7 @@
     SortedVector<String8> mExtensionList;
 
     char* mExtensions;
+    char* mVersion;
 
     bool mHasNPot;
     bool mHasFramebufferFetch;
@@ -64,6 +68,9 @@
     bool mHasTiledRendering;
     bool mHas1BitStencil;
     bool mHas4BitStencil;
+
+    int mVersionMajor;
+    int mVersionMinor;
 }; // class Extensions
 
 }; // namespace uirenderer
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index ad5e20b..6b28e8e 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -380,6 +380,15 @@
             st.createdPanelView = cb.onCreatePanelView(st.featureId);
         }
 
+        final boolean isActionBarMenu =
+                (st.featureId == FEATURE_OPTIONS_PANEL || st.featureId == FEATURE_ACTION_BAR);
+
+        if (isActionBarMenu && mActionBar != null) {
+            // Enforce ordering guarantees around events so that the action bar never
+            // dispatches menu-related events before the panel is prepared.
+            mActionBar.setMenuPrepared();
+        }
+
         if (st.createdPanelView == null) {
             // Init the panel state's menu--return false if init failed
             if (st.menu == null || st.refreshMenuContent) {
@@ -389,7 +398,7 @@
                     }
                 }
 
-                if (mActionBar != null) {
+                if (isActionBarMenu && mActionBar != null) {
                     if (mActionMenuPresenterCallback == null) {
                         mActionMenuPresenterCallback = new ActionMenuPresenterCallback();
                     }
@@ -405,7 +414,7 @@
                     // Ditch the menu created above
                     st.setMenu(null);
 
-                    if (mActionBar != null) {
+                    if (isActionBarMenu && mActionBar != null) {
                         // Don't show it in the action bar either
                         mActionBar.setMenu(null, mActionMenuPresenterCallback);
                     }
@@ -430,7 +439,7 @@
             }
 
             if (!cb.onPreparePanel(st.featureId, st.createdPanelView, st.menu)) {
-                if (mActionBar != null) {
+                if (isActionBarMenu && mActionBar != null) {
                     // The app didn't want to show the menu for now but it still exists.
                     // Clear it out of the action bar.
                     mActionBar.setMenu(null, mActionMenuPresenterCallback);
diff --git a/services/java/com/android/server/IntentResolver.java b/services/java/com/android/server/IntentResolver.java
index 9b19008..35345f5 100644
--- a/services/java/com/android/server/IntentResolver.java
+++ b/services/java/com/android/server/IntentResolver.java
@@ -117,7 +117,7 @@
             boolean printedHeader = false;
             F filter;
             for (int i=0; i<N && (filter=a[i]) != null; i++) {
-                if (packageName != null && !packageName.equals(packageForFilter(filter))) {
+                if (packageName != null && !isPackageForFilter(packageName, filter)) {
                     continue;
                 }
                 if (title != null) {
@@ -357,11 +357,11 @@
     }
 
     /**
-     * Return the package that owns this filter.  This must be implemented to
-     * provide correct filtering of Intents that have specified a package name
-     * they are to be delivered to.
+     * Returns whether this filter is owned by this package. This must be
+     * implemented to provide correct filtering of Intents that have
+     * specified a package name they are to be delivered to.
      */
-    protected abstract String packageForFilter(F filter);
+    protected abstract boolean isPackageForFilter(String packageName, F filter);
 
     protected abstract F[] newArray(int size);
 
@@ -556,7 +556,7 @@
             }
 
             // Is delivery being limited to filters owned by a particular package?
-            if (packageName != null && !packageName.equals(packageForFilter(filter))) {
+            if (packageName != null && !isPackageForFilter(packageName, filter)) {
                 if (debug) {
                     Slog.v(TAG, "  Filter is not from package " + packageName + "; skipping");
                 }
@@ -710,8 +710,8 @@
     }
 
     private final IntentResolverOld<F, R> mOldResolver = new IntentResolverOld<F, R>() {
-        @Override protected String packageForFilter(F filter) {
-            return IntentResolver.this.packageForFilter(filter);
+        @Override protected boolean isPackageForFilter(String packageName, F filter) {
+            return IntentResolver.this.isPackageForFilter(packageName, filter);
         }
         @Override protected boolean allowFilterResult(F filter, List<R> dest) {
             return IntentResolver.this.allowFilterResult(filter, dest);
diff --git a/services/java/com/android/server/IntentResolverOld.java b/services/java/com/android/server/IntentResolverOld.java
index 4dd77ce..94a2379 100644
--- a/services/java/com/android/server/IntentResolverOld.java
+++ b/services/java/com/android/server/IntentResolverOld.java
@@ -106,7 +106,7 @@
             boolean printedHeader = false;
             for (int i=0; i<N; i++) {
                 F filter = a.get(i);
-                if (packageName != null && !packageName.equals(packageForFilter(filter))) {
+                if (packageName != null && !isPackageForFilter(packageName, filter)) {
                     continue;
                 }
                 if (title != null) {
@@ -336,11 +336,11 @@
     }
 
     /**
-     * Return the package that owns this filter.  This must be implemented to
-     * provide correct filtering of Intents that have specified a package name
-     * they are to be delivered to.
+     * Returns whether this filter is owned by this package. This must be
+     * implemented to provide correct filtering of Intents that have
+     * specified a package name they are to be delivered to.
      */
-    protected abstract String packageForFilter(F filter);
+    protected abstract boolean isPackageForFilter(String packageName, F filter);
     
     @SuppressWarnings("unchecked")
     protected R newResult(F filter, int match, int userId) {
@@ -529,7 +529,7 @@
             }
 
             // Is delivery being limited to filters owned by a particular package?
-            if (packageName != null && !packageName.equals(packageForFilter(filter))) {
+            if (packageName != null && !isPackageForFilter(packageName, filter)) {
                 if (debug) {
                     Slog.v(TAG, "  Filter is not from package " + packageName + "; skipping");
                 }
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index 5cf1c28..44d730c 100644
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -237,9 +237,15 @@
         ArrayDeque<StatusBarNotification> mBuffer = new ArrayDeque<StatusBarNotification>(BUFFER_SIZE);
 
         public Archive() {
-
         }
+
         public void record(StatusBarNotification nr) {
+            // Nuke heavy parts of notification before storing in archive
+            nr.notification.tickerView = null;
+            nr.notification.contentView = null;
+            nr.notification.bigContentView = null;
+            nr.notification.largeIcon = null;
+
             if (mBuffer.size() == BUFFER_SIZE) {
                 mBuffer.removeFirst();
             }
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 97fbb9c..7710f13 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -30,6 +30,7 @@
 import com.android.server.SystemServer;
 import com.android.server.Watchdog;
 import com.android.server.am.ActivityStack.ActivityState;
+import com.android.server.firewall.IntentFirewall;
 import com.android.server.pm.UserManagerService;
 import com.android.server.wm.AppTransition;
 import com.android.server.wm.WindowManagerService;
@@ -274,6 +275,8 @@
 
     public ActivityStack mMainStack;
 
+    public IntentFirewall mIntentFirewall;
+
     private final boolean mHeadless;
 
     // Whether we should show our dialogs (ANR, crash, etc) or just perform their
@@ -570,8 +573,8 @@
         }
 
         @Override
-        protected String packageForFilter(BroadcastFilter filter) {
-            return filter.packageName;
+        protected boolean isPackageForFilter(String packageName, BroadcastFilter filter) {
+            return packageName.equals(filter.packageName);
         }
     };
 
@@ -1472,7 +1475,8 @@
         m.mContext = context;
         m.mFactoryTest = factoryTest;
         m.mMainStack = new ActivityStack(m, context, true, thr.mLooper);
-        
+        m.mIntentFirewall = new IntentFirewall(m.new IntentFirewallInterface());
+
         m.mBatteryStatsService.publish(context);
         m.mUsageStatsService.publish(context);
         m.mAppOpsService.publish(context);
@@ -4948,6 +4952,14 @@
         }
     }
 
+    class IntentFirewallInterface implements IntentFirewall.AMSInterface {
+        public int checkComponentPermission(String permission, int pid, int uid,
+                int owningUid, boolean exported) {
+            return ActivityManagerService.this.checkComponentPermission(permission, pid, uid,
+                    owningUid, exported);
+        }
+    }
+
     /**
      * This can be called with or without the global lock held.
      */
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 526b24f..3d7da7b 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -2489,6 +2489,7 @@
         int err = ActivityManager.START_SUCCESS;
 
         ProcessRecord callerApp = null;
+
         if (caller != null) {
             callerApp = mService.getRecordForAppLocked(caller);
             if (callerApp != null) {
@@ -2592,34 +2593,37 @@
             throw new SecurityException(msg);
         }
 
+        boolean abort = !mService.mIntentFirewall.checkStartActivity(intent,
+                callerApp==null?null:callerApp.info, callingPackage, callingUid, callingPid,
+                resolvedType, aInfo);
+
         if (mMainStack) {
             if (mService.mController != null) {
-                boolean abort = false;
                 try {
                     // The Intent we give to the watcher has the extra data
                     // stripped off, since it can contain private information.
                     Intent watchIntent = intent.cloneFilter();
-                    abort = !mService.mController.activityStarting(watchIntent,
+                    abort |= !mService.mController.activityStarting(watchIntent,
                             aInfo.applicationInfo.packageName);
                 } catch (RemoteException e) {
                     mService.mController = null;
                 }
-    
-                if (abort) {
-                    if (resultRecord != null) {
-                        sendActivityResultLocked(-1,
-                            resultRecord, resultWho, requestCode,
-                            Activity.RESULT_CANCELED, null);
-                    }
-                    // We pretend to the caller that it was really started, but
-                    // they will just get a cancel result.
-                    mDismissKeyguardOnNextActivity = false;
-                    ActivityOptions.abort(options);
-                    return ActivityManager.START_SUCCESS;
-                }
             }
         }
 
+        if (abort) {
+            if (resultRecord != null) {
+                sendActivityResultLocked(-1,
+                    resultRecord, resultWho, requestCode,
+                    Activity.RESULT_CANCELED, null);
+            }
+            // We pretend to the caller that it was really started, but
+            // they will just get a cancel result.
+            mDismissKeyguardOnNextActivity = false;
+            ActivityOptions.abort(options);
+            return ActivityManager.START_SUCCESS;
+        }
+
         ActivityRecord r = new ActivityRecord(mService, this, callerApp, callingUid, callingPackage,
                 intent, resolvedType, aInfo, mService.mConfiguration,
                 resultRecord, resultWho, requestCode, componentSpecified);
diff --git a/services/java/com/android/server/firewall/AndFilter.java b/services/java/com/android/server/firewall/AndFilter.java
new file mode 100644
index 0000000..cabf00b
--- /dev/null
+++ b/services/java/com/android/server/firewall/AndFilter.java
@@ -0,0 +1,47 @@
+/*
+ * 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 com.android.server.firewall;
+
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+class AndFilter extends FilterList {
+    @Override
+    public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
+            String callerPackage, int callerUid, int callerPid, String resolvedType,
+            ApplicationInfo resolvedApp) {
+        for (int i=0; i<children.size(); i++) {
+            if (!children.get(i).matches(ifw, intent, callerApp, callerPackage, callerUid,
+                    callerPid, resolvedType, resolvedApp)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public static final FilterFactory FACTORY = new FilterFactory("and") {
+        @Override
+        public Filter newFilter(XmlPullParser parser)
+                throws IOException, XmlPullParserException {
+            return new AndFilter().readFromXml(parser);
+        }
+    };
+}
diff --git a/services/java/com/android/server/firewall/CategoryFilter.java b/services/java/com/android/server/firewall/CategoryFilter.java
new file mode 100644
index 0000000..d5e9fe8
--- /dev/null
+++ b/services/java/com/android/server/firewall/CategoryFilter.java
@@ -0,0 +1,58 @@
+/*
+ * 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 com.android.server.firewall;
+
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.Set;
+
+class CategoryFilter implements Filter {
+    private static final String ATTR_NAME = "name";
+
+    private final String mCategoryName;
+
+    private CategoryFilter(String categoryName) {
+        mCategoryName = categoryName;
+    }
+
+    @Override
+    public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp, String callerPackage,
+            int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) {
+        Set<String> categories = intent.getCategories();
+        if (categories == null) {
+            return false;
+        }
+        return categories.contains(mCategoryName);
+    }
+
+    public static final FilterFactory FACTORY = new FilterFactory("category") {
+        @Override
+        public Filter newFilter(XmlPullParser parser)
+                throws IOException, XmlPullParserException {
+            String categoryName = parser.getAttributeValue(null, ATTR_NAME);
+            if (categoryName == null) {
+                throw new XmlPullParserException("Category name must be specified.",
+                        parser, null);
+            }
+            return new CategoryFilter(categoryName);
+        }
+    };
+}
diff --git a/services/java/com/android/server/firewall/Filter.java b/services/java/com/android/server/firewall/Filter.java
new file mode 100644
index 0000000..7639466
--- /dev/null
+++ b/services/java/com/android/server/firewall/Filter.java
@@ -0,0 +1,42 @@
+/*
+ * 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 com.android.server.firewall;
+
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+
+interface Filter {
+    /**
+     * Does the given intent + context info match this filter?
+     *
+     * @param ifw The IntentFirewall instance
+     * @param intent The intent being started/bound/broadcast
+     * @param callerApp An ApplicationInfo of an application in the caller's process. This may not
+ *                  be the specific app that is actually sending the intent. This also may be
+ *                  null, if the caller is the system process, or an unrecognized process (e.g.
+ *                  am start)
+     * @param callerPackage The package name of the component sending the intent. This value is
+*                      provided by the caller and might be forged/faked.
+     * @param callerUid
+     * @param callerPid
+     * @param resolvedType The resolved mime type of the intent
+     * @param resolvedApp The application that contains the resolved component that the intent is
+     */
+    boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
+            String callerPackage, int callerUid, int callerPid, String resolvedType,
+            ApplicationInfo resolvedApp);
+}
diff --git a/services/java/com/android/server/firewall/FilterFactory.java b/services/java/com/android/server/firewall/FilterFactory.java
new file mode 100644
index 0000000..dea8b40
--- /dev/null
+++ b/services/java/com/android/server/firewall/FilterFactory.java
@@ -0,0 +1,40 @@
+/*
+ * 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 com.android.server.firewall;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+public abstract class FilterFactory {
+    private final String mTag;
+
+    protected FilterFactory(String tag) {
+        if (tag == null) {
+            throw new NullPointerException();
+        }
+        mTag = tag;
+    }
+
+    public String getTagName() {
+        return mTag;
+    }
+
+    public abstract Filter newFilter(XmlPullParser parser)
+            throws IOException, XmlPullParserException;
+}
diff --git a/services/java/com/android/server/firewall/FilterList.java b/services/java/com/android/server/firewall/FilterList.java
new file mode 100644
index 0000000..d34b203
--- /dev/null
+++ b/services/java/com/android/server/firewall/FilterList.java
@@ -0,0 +1,41 @@
+/*
+ * 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 com.android.server.firewall;
+
+import com.android.internal.util.XmlUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+abstract class FilterList implements Filter {
+    protected final ArrayList<Filter> children = new ArrayList<Filter>();
+
+    public FilterList readFromXml(XmlPullParser parser) throws IOException, XmlPullParserException {
+        int outerDepth = parser.getDepth();
+        while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+            readChild(parser);
+        }
+        return this;
+    }
+
+    protected void readChild(XmlPullParser parser) throws IOException, XmlPullParserException {
+        Filter filter = IntentFirewall.parseFilter(parser);
+        children.add(filter);
+    }
+}
diff --git a/services/java/com/android/server/firewall/IntentFirewall.java b/services/java/com/android/server/firewall/IntentFirewall.java
new file mode 100644
index 0000000..ebbbd86
--- /dev/null
+++ b/services/java/com/android/server/firewall/IntentFirewall.java
@@ -0,0 +1,315 @@
+/*
+ * 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 com.android.server.firewall;
+
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Environment;
+import android.os.ServiceManager;
+import android.util.Slog;
+import android.util.Xml;
+import com.android.internal.util.XmlUtils;
+import com.android.server.IntentResolver;
+import com.android.server.pm.PackageManagerService;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+public class IntentFirewall {
+    private static final String TAG = "IntentFirewall";
+
+    private static final String RULES_FILENAME = "ifw.xml";
+
+    private static final String TAG_RULES = "rules";
+    private static final String TAG_ACTIVITY = "activity";
+    private static final String TAG_SERVICE = "service";
+    private static final String TAG_BROADCAST = "broadcast";
+
+    private static final HashMap<String, FilterFactory> factoryMap;
+
+    private final AMSInterface mAms;
+
+    private final IntentResolver<FirewallIntentFilter, Rule> mActivityResolver =
+            new FirewallIntentResolver();
+    private final IntentResolver<FirewallIntentFilter, Rule> mServiceResolver =
+            new FirewallIntentResolver();
+    private final IntentResolver<FirewallIntentFilter, Rule> mBroadcastResolver =
+            new FirewallIntentResolver();
+
+    static {
+        FilterFactory[] factories = new FilterFactory[] {
+                AndFilter.FACTORY,
+                OrFilter.FACTORY,
+                NotFilter.FACTORY,
+
+                StringFilter.ACTION,
+                StringFilter.COMPONENT,
+                StringFilter.COMPONENT_NAME,
+                StringFilter.COMPONENT_PACKAGE,
+                StringFilter.DATA,
+                StringFilter.HOST,
+                StringFilter.MIME_TYPE,
+                StringFilter.PATH,
+                StringFilter.SENDER_PACKAGE,
+                StringFilter.SSP,
+
+                CategoryFilter.FACTORY,
+                SenderFilter.FACTORY,
+                SenderPermissionFilter.FACTORY,
+                PortFilter.FACTORY
+        };
+
+        // load factor ~= .75
+        factoryMap = new HashMap<String, FilterFactory>(factories.length * 4 / 3);
+        for (int i=0; i<factories.length; i++) {
+            FilterFactory factory = factories[i];
+            factoryMap.put(factory.getTagName(), factory);
+        }
+    }
+
+    public IntentFirewall(AMSInterface ams) {
+        mAms = ams;
+        File dataSystemDir = new File(Environment.getDataDirectory(), "system");
+        File rulesFile = new File(dataSystemDir, RULES_FILENAME);
+        readRules(rulesFile);
+    }
+
+    public boolean checkStartActivity(Intent intent, ApplicationInfo callerApp,
+            String callerPackage, int callerUid, int callerPid, String resolvedType,
+            ActivityInfo resolvedActivity) {
+        List<Rule> matchingRules = mActivityResolver.queryIntent(intent, resolvedType, false, 0);
+        boolean log = false;
+        boolean block = false;
+
+        for (int i=0; i< matchingRules.size(); i++) {
+            Rule rule = matchingRules.get(i);
+            if (rule.matches(this, intent, callerApp, callerPackage, callerUid, callerPid,
+                    resolvedType, resolvedActivity.applicationInfo)) {
+                block |= rule.getBlock();
+                log |= rule.getLog();
+
+                // if we've already determined that we should both block and log, there's no need
+                // to continue trying rules
+                if (block && log) {
+                    break;
+                }
+            }
+        }
+
+        if (log) {
+            // TODO: log info about intent to event log
+        }
+
+        return !block;
+    }
+
+    private void readRules(File rulesFile) {
+        FileInputStream fis;
+        try {
+            fis = new FileInputStream(rulesFile);
+        } catch (FileNotFoundException ex) {
+            // Nope, no rules. Nothing else to do!
+            return;
+        }
+
+        try {
+            XmlPullParser parser = Xml.newPullParser();
+
+            parser.setInput(fis, null);
+
+            XmlUtils.beginDocument(parser, TAG_RULES);
+
+            int outerDepth = parser.getDepth();
+            while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+                IntentResolver<FirewallIntentFilter, Rule> resolver = null;
+                String tagName = parser.getName();
+                if (tagName.equals(TAG_ACTIVITY)) {
+                    resolver = mActivityResolver;
+                } else if (tagName.equals(TAG_SERVICE)) {
+                    resolver = mServiceResolver;
+                } else if (tagName.equals(TAG_BROADCAST)) {
+                    resolver = mBroadcastResolver;
+                }
+
+                if (resolver != null) {
+                    Rule rule = new Rule();
+
+                    try {
+                        rule.readFromXml(parser);
+                    } catch (XmlPullParserException ex) {
+                        Slog.e(TAG, "Error reading intent firewall rule", ex);
+                        continue;
+                    } catch (IOException ex) {
+                        Slog.e(TAG, "Error reading intent firewall rule", ex);
+                        continue;
+                    }
+
+                    for (int i=0; i<rule.getIntentFilterCount(); i++) {
+                        resolver.addFilter(rule.getIntentFilter(i));
+                    }
+                }
+            }
+        } catch (XmlPullParserException ex) {
+            Slog.e(TAG, "Error reading intent firewall rules", ex);
+        } catch (IOException ex) {
+            Slog.e(TAG, "Error reading intent firewall rules", ex);
+        } finally {
+            try {
+                fis.close();
+            } catch (IOException ex) {
+                Slog.e(TAG, "Error while closing " + rulesFile, ex);
+            }
+        }
+    }
+
+    static Filter parseFilter(XmlPullParser parser) throws IOException, XmlPullParserException {
+        String elementName = parser.getName();
+
+        FilterFactory factory = factoryMap.get(elementName);
+
+        if (factory == null) {
+            throw new XmlPullParserException("Unknown element in filter list: " + elementName);
+        }
+        return factory.newFilter(parser);
+    }
+
+    private static class Rule extends AndFilter {
+        private static final String TAG_INTENT_FILTER = "intent-filter";
+
+        private static final String ATTR_BLOCK = "block";
+        private static final String ATTR_LOG = "log";
+
+        private final ArrayList<FirewallIntentFilter> mIntentFilters =
+                new ArrayList<FirewallIntentFilter>(1);
+        private boolean block;
+        private boolean log;
+
+        @Override
+        public Rule readFromXml(XmlPullParser parser) throws IOException, XmlPullParserException {
+            block = Boolean.parseBoolean(parser.getAttributeValue(null, ATTR_BLOCK));
+            log = Boolean.parseBoolean(parser.getAttributeValue(null, ATTR_LOG));
+
+            super.readFromXml(parser);
+            return this;
+        }
+
+        @Override
+        protected void readChild(XmlPullParser parser) throws IOException, XmlPullParserException {
+            if (parser.getName().equals(TAG_INTENT_FILTER)) {
+                FirewallIntentFilter intentFilter = new FirewallIntentFilter(this);
+                intentFilter.readFromXml(parser);
+                mIntentFilters.add(intentFilter);
+            } else {
+                super.readChild(parser);
+            }
+        }
+
+        public int getIntentFilterCount() {
+            return mIntentFilters.size();
+        }
+
+        public FirewallIntentFilter getIntentFilter(int index) {
+            return mIntentFilters.get(index);
+        }
+
+        public boolean getBlock() {
+            return block;
+        }
+
+        public boolean getLog() {
+            return log;
+        }
+    }
+
+    private static class FirewallIntentFilter extends IntentFilter {
+        private final Rule rule;
+
+        public FirewallIntentFilter(Rule rule) {
+            this.rule = rule;
+        }
+    }
+
+    private static class FirewallIntentResolver
+            extends IntentResolver<FirewallIntentFilter, Rule> {
+        @Override
+        protected boolean allowFilterResult(FirewallIntentFilter filter, List<Rule> dest) {
+            return !dest.contains(filter.rule);
+        }
+
+        @Override
+        protected boolean isPackageForFilter(String packageName, FirewallIntentFilter filter) {
+            return true;
+        }
+
+        @Override
+        protected FirewallIntentFilter[] newArray(int size) {
+            return new FirewallIntentFilter[size];
+        }
+
+        @Override
+        protected Rule newResult(FirewallIntentFilter filter, int match, int userId) {
+            return filter.rule;
+        }
+
+        @Override
+        protected void sortResults(List<Rule> results) {
+            // there's no need to sort the results
+            return;
+        }
+    }
+
+    /**
+     * This interface contains the methods we need from ActivityManagerService. This allows AMS to
+     * export these methods to us without making them public, and also makes it easier to test this
+     * component.
+     */
+    public interface AMSInterface {
+        int checkComponentPermission(String permission, int pid, int uid,
+                int owningUid, boolean exported);
+    }
+
+    /**
+     * Checks if the caller has access to a component
+     *
+     * @param permission If present, the caller must have this permission
+     * @param pid The pid of the caller
+     * @param uid The uid of the caller
+     * @param owningUid The uid of the application that owns the component
+     * @param exported Whether the component is exported
+     * @return True if the caller can access the described component
+     */
+    boolean checkComponentPermission(String permission, int pid, int uid, int owningUid,
+            boolean exported) {
+        return mAms.checkComponentPermission(permission, pid, uid, owningUid, exported) ==
+                PackageManager.PERMISSION_GRANTED;
+    }
+
+    boolean signaturesMatch(int uid1, int uid2) {
+        PackageManagerService pm = (PackageManagerService)ServiceManager.getService("package");
+        return pm.checkUidSignatures(uid1, uid2) == PackageManager.SIGNATURE_MATCH;
+    }
+}
diff --git a/services/java/com/android/server/firewall/NotFilter.java b/services/java/com/android/server/firewall/NotFilter.java
new file mode 100644
index 0000000..2ff108a1
--- /dev/null
+++ b/services/java/com/android/server/firewall/NotFilter.java
@@ -0,0 +1,60 @@
+/*
+ * 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 com.android.server.firewall;
+
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import com.android.internal.util.XmlUtils;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+class NotFilter implements Filter {
+    private final Filter mChild;
+
+    private NotFilter(Filter child) {
+        mChild = child;
+    }
+
+    @Override
+    public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
+            String callerPackage, int callerUid, int callerPid, String resolvedType,
+            ApplicationInfo resolvedApp) {
+        return !mChild.matches(ifw, intent, callerApp, callerPackage, callerUid, callerPid,
+                resolvedType, resolvedApp);
+    }
+
+    public static final FilterFactory FACTORY = new FilterFactory("not") {
+        @Override
+        public Filter newFilter(XmlPullParser parser)
+                throws IOException, XmlPullParserException {
+            Filter child = null;
+            int outerDepth = parser.getDepth();
+            while (XmlUtils.nextElementWithin(parser, outerDepth)) {
+                Filter filter = IntentFirewall.parseFilter(parser);
+                if (child == null) {
+                    child = filter;
+                } else {
+                    throw new XmlPullParserException(
+                            "<not> tag can only contain a single child filter.", parser, null);
+                }
+            }
+            return new NotFilter(child);
+        }
+    };
+}
diff --git a/services/java/com/android/server/firewall/OrFilter.java b/services/java/com/android/server/firewall/OrFilter.java
new file mode 100644
index 0000000..1ed1c85
--- /dev/null
+++ b/services/java/com/android/server/firewall/OrFilter.java
@@ -0,0 +1,47 @@
+/*
+ * 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 com.android.server.firewall;
+
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+class OrFilter extends FilterList {
+    @Override
+    public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
+            String callerPackage, int callerUid, int callerPid, String resolvedType,
+            ApplicationInfo resolvedApp) {
+        for (int i=0; i<children.size(); i++) {
+            if (children.get(i).matches(ifw, intent, callerApp, callerPackage, callerUid, callerPid,
+                    resolvedType, resolvedApp)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public static final FilterFactory FACTORY = new FilterFactory("or") {
+        @Override
+        public Filter newFilter(XmlPullParser parser)
+                throws IOException, XmlPullParserException {
+            return new OrFilter().readFromXml(parser);
+        }
+    };
+}
diff --git a/services/java/com/android/server/firewall/PortFilter.java b/services/java/com/android/server/firewall/PortFilter.java
new file mode 100644
index 0000000..2b2a198
--- /dev/null
+++ b/services/java/com/android/server/firewall/PortFilter.java
@@ -0,0 +1,111 @@
+/*
+ * 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 com.android.server.firewall;
+
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.net.Uri;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+class PortFilter implements Filter {
+    private static final String ATTR_EQUALS = "equals";
+    private static final String ATTR_MIN = "min";
+    private static final String ATTR_MAX = "max";
+
+    private static final int NO_BOUND = -1;
+
+    // both bounds are inclusive
+    private final int mLowerBound;
+    private final int mUpperBound;
+
+    private PortFilter(int lowerBound, int upperBound) {
+        mLowerBound = lowerBound;
+        mUpperBound = upperBound;
+    }
+
+    @Override
+    public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
+            String callerPackage, int callerUid, int callerPid, String resolvedType,
+            ApplicationInfo resolvedApp) {
+        int port = -1;
+        Uri uri = intent.getData();
+        if (uri != null) {
+            port = uri.getPort();
+        }
+        return port != -1 &&
+                (mLowerBound == NO_BOUND || mLowerBound <= port) &&
+                (mUpperBound == NO_BOUND || mUpperBound >= port);
+    }
+
+    public static final FilterFactory FACTORY = new FilterFactory("port") {
+        @Override
+        public Filter newFilter(XmlPullParser parser)
+                throws IOException, XmlPullParserException {
+            int lowerBound = NO_BOUND;
+            int upperBound = NO_BOUND;
+
+            String equalsValue = parser.getAttributeValue(null, ATTR_EQUALS);
+            if (equalsValue != null) {
+                int value;
+                try {
+                    value = Integer.parseInt(equalsValue);
+                } catch (NumberFormatException ex) {
+                    throw new XmlPullParserException("Invalid port value: " + equalsValue,
+                            parser, null);
+                }
+                lowerBound = value;
+                upperBound = value;
+            }
+
+            String lowerBoundString = parser.getAttributeValue(null, ATTR_MIN);
+            String upperBoundString = parser.getAttributeValue(null, ATTR_MAX);
+            if (lowerBoundString != null || upperBoundString != null) {
+                if (equalsValue != null) {
+                    throw new XmlPullParserException(
+                            "Port filter cannot use both equals and range filtering",
+                            parser, null);
+                }
+
+                if (lowerBoundString != null) {
+                    try {
+                        lowerBound = Integer.parseInt(lowerBoundString);
+                    } catch (NumberFormatException ex) {
+                        throw new XmlPullParserException(
+                                "Invalid minimum port value: " + lowerBoundString,
+                                parser, null);
+                    }
+                }
+
+                if (upperBoundString != null) {
+                    try {
+                        upperBound = Integer.parseInt(upperBoundString);
+                    } catch (NumberFormatException ex) {
+                        throw new XmlPullParserException(
+                                "Invalid maximum port value: " + upperBoundString,
+                                parser, null);
+                    }
+                }
+            }
+
+            // an empty port filter is explicitly allowed, and checks for the existence of a port
+            return new PortFilter(lowerBound, upperBound);
+        }
+    };
+}
diff --git a/services/java/com/android/server/firewall/SenderFilter.java b/services/java/com/android/server/firewall/SenderFilter.java
new file mode 100644
index 0000000..0b790bd
--- /dev/null
+++ b/services/java/com/android/server/firewall/SenderFilter.java
@@ -0,0 +1,115 @@
+/*
+ * 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 com.android.server.firewall;
+
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.os.Process;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+class SenderFilter {
+    private static final String ATTR_TYPE = "type";
+
+    private static final String VAL_SIGNATURE = "signature";
+    private static final String VAL_SYSTEM = "system";
+    private static final String VAL_SYSTEM_OR_SIGNATURE = "system|signature";
+    private static final String VAL_USER_ID = "userId";
+
+    static boolean isSystemApp(ApplicationInfo callerApp, int callerUid, int callerPid) {
+        if (callerUid == Process.SYSTEM_UID ||
+                callerPid == Process.myPid()) {
+            return true;
+        }
+        if (callerApp == null) {
+            return false;
+        }
+        return (callerApp.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
+    }
+
+    public static final FilterFactory FACTORY = new FilterFactory("sender") {
+        @Override
+        public Filter newFilter(XmlPullParser parser) throws IOException, XmlPullParserException {
+            String typeString = parser.getAttributeValue(null, ATTR_TYPE);
+            if (typeString == null) {
+                throw new XmlPullParserException("type attribute must be specified for <sender>",
+                        parser, null);
+            }
+            if (typeString.equals(VAL_SYSTEM)) {
+                return SYSTEM;
+            } else if (typeString.equals(VAL_SIGNATURE)) {
+                return SIGNATURE;
+            } else if (typeString.equals(VAL_SYSTEM_OR_SIGNATURE)) {
+                return SYSTEM_OR_SIGNATURE;
+            } else if (typeString.equals(VAL_USER_ID)) {
+                return USER_ID;
+            }
+            throw new XmlPullParserException(
+                    "Invalid type attribute for <sender>: " + typeString, parser, null);
+        }
+    };
+
+    private static final Filter SIGNATURE = new Filter() {
+        @Override
+        public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
+                String callerPackage, int callerUid, int callerPid, String resolvedType,
+                ApplicationInfo resolvedApp) {
+            if (callerApp == null) {
+                return false;
+            }
+            return ifw.signaturesMatch(callerUid, resolvedApp.uid);
+        }
+    };
+
+    private static final Filter SYSTEM = new Filter() {
+        @Override
+        public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
+                String callerPackage, int callerUid, int callerPid, String resolvedType,
+                ApplicationInfo resolvedApp) {
+            if (callerApp == null) {
+                // if callerApp is null, the caller is the system process
+                return false;
+            }
+            return isSystemApp(callerApp, callerUid, callerPid);
+        }
+    };
+
+    private static final Filter SYSTEM_OR_SIGNATURE = new Filter() {
+        @Override
+        public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
+                String callerPackage, int callerUid, int callerPid, String resolvedType,
+                ApplicationInfo resolvedApp) {
+            return isSystemApp(callerApp, callerUid, callerPid) ||
+                    ifw.signaturesMatch(callerUid, resolvedApp.uid);
+        }
+    };
+
+    private static final Filter USER_ID = new Filter() {
+        @Override
+        public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
+                String callerPackage, int callerUid, int callerPid, String resolvedType,
+                ApplicationInfo resolvedApp) {
+            // This checks whether the caller is either the system process, or has the same user id
+            // I.e. the same app, or an app that uses the same shared user id.
+            // This is the same set of applications that would be able to access the component if
+            // it wasn't exported.
+            return ifw.checkComponentPermission(null, callerPid, callerUid, resolvedApp.uid, false);
+        }
+    };
+}
diff --git a/services/java/com/android/server/firewall/SenderPermissionFilter.java b/services/java/com/android/server/firewall/SenderPermissionFilter.java
new file mode 100644
index 0000000..02d8b15
--- /dev/null
+++ b/services/java/com/android/server/firewall/SenderPermissionFilter.java
@@ -0,0 +1,58 @@
+/*
+ * 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 com.android.server.firewall;
+
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+
+class SenderPermissionFilter implements Filter {
+    private static final String ATTR_NAME = "name";
+
+    private final String mPermission;
+
+    private SenderPermissionFilter(String permission) {
+        mPermission = permission;
+    }
+
+    @Override
+    public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp,
+            String callerPackage, int callerUid, int callerPid, String resolvedType,
+            ApplicationInfo resolvedApp) {
+        // We assume the component is exported here. If the component is not exported, then
+        // ActivityManager would only resolve to this component for callers from the same uid.
+        // In this case, it doesn't matter whether the component is exported or not.
+        return ifw.checkComponentPermission(mPermission, callerPid, callerUid, resolvedApp.uid,
+                true);
+    }
+
+    public static final FilterFactory FACTORY = new FilterFactory("sender-permission") {
+        @Override
+        public Filter newFilter(XmlPullParser parser)
+                throws IOException, XmlPullParserException {
+            String permission = parser.getAttributeValue(null, ATTR_NAME);
+            if (permission == null) {
+                throw new XmlPullParserException("Permission name must be specified.",
+                        parser, null);
+            }
+            return new SenderPermissionFilter(permission);
+        }
+    };
+}
diff --git a/services/java/com/android/server/firewall/StringFilter.java b/services/java/com/android/server/firewall/StringFilter.java
new file mode 100644
index 0000000..de5a69f
--- /dev/null
+++ b/services/java/com/android/server/firewall/StringFilter.java
@@ -0,0 +1,353 @@
+/*
+ * 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 com.android.server.firewall;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.net.Uri;
+import android.os.PatternMatcher;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.regex.Pattern;
+
+abstract class StringFilter implements Filter {
+    private static final String ATTR_EQUALS = "equals";
+    private static final String ATTR_STARTS_WITH = "startsWith";
+    private static final String ATTR_CONTAINS = "contains";
+    private static final String ATTR_PATTERN = "pattern";
+    private static final String ATTR_REGEX = "regex";
+    private static final String ATTR_IS_NULL = "isNull";
+
+    private final ValueProvider mValueProvider;
+
+    private StringFilter(ValueProvider valueProvider) {
+        this.mValueProvider = valueProvider;
+    }
+
+    /**
+     * Constructs a new StringFilter based on the string filter attribute on the current
+     * element, and the given StringValueMatcher.
+     *
+     * The current node should contain exactly 1 string filter attribute. E.g. equals,
+     * contains, etc. Otherwise, an XmlPullParserException will be thrown.
+     *
+     * @param parser      An XmlPullParser object positioned at an element that should
+     *                    contain a string filter attribute
+     * @return This StringFilter object
+     */
+    public static StringFilter readFromXml(ValueProvider valueProvider, XmlPullParser parser)
+            throws IOException, XmlPullParserException {
+        StringFilter filter = null;
+
+        for (int i=0; i<parser.getAttributeCount(); i++) {
+            StringFilter newFilter = getFilter(valueProvider, parser, i);
+            if (newFilter != null) {
+                if (filter != null) {
+                    throw new XmlPullParserException("Multiple string filter attributes found");
+                }
+                filter = newFilter;
+            }
+        }
+
+        if (filter == null) {
+            // if there are no string filter attributes, we default to isNull="false" so that an
+            // empty filter is equivalent to an existence check
+            filter = new IsNullFilter(valueProvider, false);
+        }
+
+        return filter;
+    }
+
+    private static StringFilter getFilter(ValueProvider valueProvider, XmlPullParser parser,
+            int attributeIndex) {
+        String attributeName = parser.getAttributeName(attributeIndex);
+
+        switch (attributeName.charAt(0)) {
+            case 'e':
+                if (!attributeName.equals(ATTR_EQUALS)) {
+                    return null;
+                }
+                return new EqualsFilter(valueProvider, parser.getAttributeValue(attributeIndex));
+            case 'i':
+                if (!attributeName.equals(ATTR_IS_NULL)) {
+                    return null;
+                }
+                return new IsNullFilter(valueProvider, parser.getAttributeValue(attributeIndex));
+            case 's':
+                if (!attributeName.equals(ATTR_STARTS_WITH)) {
+                    return null;
+                }
+                return new StartsWithFilter(valueProvider,
+                        parser.getAttributeValue(attributeIndex));
+            case 'c':
+                if (!attributeName.equals(ATTR_CONTAINS)) {
+                    return null;
+                }
+                return new ContainsFilter(valueProvider, parser.getAttributeValue(attributeIndex));
+            case 'p':
+                if (!attributeName.equals(ATTR_PATTERN)) {
+                    return null;
+                }
+                return new PatternStringFilter(valueProvider,
+                        parser.getAttributeValue(attributeIndex));
+            case 'r':
+                if (!attributeName.equals(ATTR_REGEX)) {
+                    return null;
+                }
+                return new RegexFilter(valueProvider, parser.getAttributeValue(attributeIndex));
+        }
+        return null;
+    }
+
+    protected abstract boolean matchesValue(String value);
+
+    @Override
+    public boolean matches(IntentFirewall ifw, Intent intent, ApplicationInfo callerApp, String callerPackage,
+            int callerUid, int callerPid, String resolvedType, ApplicationInfo resolvedApp) {
+        String value = mValueProvider.getValue(intent, callerApp, callerPackage, resolvedType,
+                resolvedApp);
+        return matchesValue(value);
+    }
+
+    private static abstract class ValueProvider extends FilterFactory {
+        protected ValueProvider(String tag) {
+            super(tag);
+        }
+
+        public Filter newFilter(XmlPullParser parser)
+                throws IOException, XmlPullParserException {
+            return StringFilter.readFromXml(this, parser);
+        }
+
+        public abstract String getValue(Intent intent, ApplicationInfo callerApp,
+                String callerPackage, String resolvedType, ApplicationInfo resolvedApp);
+    }
+
+    private static class EqualsFilter extends StringFilter {
+        private final String mFilterValue;
+
+        public EqualsFilter(ValueProvider valueProvider, String attrValue) {
+            super(valueProvider);
+            mFilterValue = attrValue;
+        }
+
+        @Override
+        public boolean matchesValue(String value) {
+            return value != null && value.equals(mFilterValue);
+        }
+    }
+
+    private static class ContainsFilter extends StringFilter {
+        private final String mFilterValue;
+
+        public ContainsFilter(ValueProvider valueProvider, String attrValue) {
+            super(valueProvider);
+            mFilterValue = attrValue;
+        }
+
+        @Override
+        public boolean matchesValue(String value) {
+            return value != null && value.contains(mFilterValue);
+        }
+    }
+
+    private static class StartsWithFilter extends StringFilter {
+        private final String mFilterValue;
+
+        public StartsWithFilter(ValueProvider valueProvider, String attrValue) {
+            super(valueProvider);
+            mFilterValue = attrValue;
+        }
+
+        @Override
+        public boolean matchesValue(String value) {
+            return value != null && value.startsWith(mFilterValue);
+        }
+    }
+
+    private static class PatternStringFilter extends StringFilter {
+        private final PatternMatcher mPattern;
+
+        public PatternStringFilter(ValueProvider valueProvider, String attrValue) {
+            super(valueProvider);
+            mPattern = new PatternMatcher(attrValue, PatternMatcher.PATTERN_SIMPLE_GLOB);
+        }
+
+        @Override
+        public boolean matchesValue(String value) {
+            return value != null && mPattern.match(value);
+        }
+    }
+
+    private static class RegexFilter extends StringFilter {
+        private final Pattern mPattern;
+
+        public RegexFilter(ValueProvider valueProvider, String attrValue) {
+            super(valueProvider);
+            this.mPattern = Pattern.compile(attrValue);
+        }
+
+        @Override
+        public boolean matchesValue(String value) {
+            return value != null && mPattern.matcher(value).matches();
+        }
+    }
+
+    private static class IsNullFilter extends StringFilter {
+        private final boolean mIsNull;
+
+        public IsNullFilter(ValueProvider valueProvider, String attrValue) {
+            super(valueProvider);
+            mIsNull = Boolean.parseBoolean(attrValue);
+        }
+
+        public IsNullFilter(ValueProvider valueProvider, boolean isNull) {
+            super(valueProvider);
+            mIsNull = isNull;
+        }
+
+        @Override
+        public boolean matchesValue(String value) {
+            return (value == null) == mIsNull;
+        }
+    }
+
+    public static final ValueProvider COMPONENT = new ValueProvider("component") {
+        @Override
+        public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
+                String resolvedType, ApplicationInfo resolvedApp) {
+            ComponentName cn = intent.getComponent();
+            if (cn != null) {
+                return cn.flattenToString();
+            }
+            return null;
+        }
+    };
+
+    public static final ValueProvider COMPONENT_NAME = new ValueProvider("component-name") {
+        @Override
+        public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
+                String resolvedType, ApplicationInfo resolvedApp) {
+            ComponentName cn = intent.getComponent();
+            if (cn != null) {
+                return cn.getClassName();
+            }
+            return null;
+        }
+    };
+
+    public static final ValueProvider COMPONENT_PACKAGE = new ValueProvider("component-package") {
+        @Override
+        public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
+                String resolvedType, ApplicationInfo resolvedApp) {
+            ComponentName cn = intent.getComponent();
+            if (cn != null) {
+                return cn.getPackageName();
+            }
+            return null;
+        }
+    };
+
+    public static final ValueProvider SENDER_PACKAGE = new ValueProvider("sender-package") {
+        @Override
+        public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
+                String resolvedType, ApplicationInfo resolvedApp) {
+            // TODO: We can't trust this value, so maybe should check all packages in the caller process?
+            return callerPackage;
+        }
+    };
+
+
+    public static final FilterFactory ACTION = new ValueProvider("action") {
+        @Override
+        public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
+                String resolvedType, ApplicationInfo resolvedApp) {
+            return intent.getAction();
+        }
+    };
+
+    public static final ValueProvider DATA = new ValueProvider("data") {
+        @Override
+        public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
+                String resolvedType, ApplicationInfo resolvedApp) {
+            Uri data = intent.getData();
+            if (data != null) {
+                return data.toString();
+            }
+            return null;
+        }
+    };
+
+    public static final ValueProvider MIME_TYPE = new ValueProvider("mime-type") {
+        @Override
+        public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
+                String resolvedType, ApplicationInfo resolvedApp) {
+            return resolvedType;
+        }
+    };
+
+    public static final ValueProvider SCHEME = new ValueProvider("scheme") {
+        @Override
+        public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
+                String resolvedType, ApplicationInfo resolvedApp) {
+            Uri data = intent.getData();
+            if (data != null) {
+                return data.getScheme();
+            }
+            return null;
+        }
+    };
+
+    public static final ValueProvider SSP = new ValueProvider("scheme-specific-part") {
+        @Override
+        public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
+                String resolvedType, ApplicationInfo resolvedApp) {
+            Uri data = intent.getData();
+            if (data != null) {
+                return data.getSchemeSpecificPart();
+            }
+            return null;
+        }
+    };
+
+    public static final ValueProvider HOST = new ValueProvider("host") {
+        @Override
+        public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
+                String resolvedType, ApplicationInfo resolvedApp) {
+            Uri data = intent.getData();
+            if (data != null) {
+                return data.getHost();
+            }
+            return null;
+        }
+    };
+
+    public static final ValueProvider PATH = new ValueProvider("path") {
+        @Override
+        public String getValue(Intent intent, ApplicationInfo callerApp, String callerPackage,
+                String resolvedType, ApplicationInfo resolvedApp) {
+            Uri data = intent.getData();
+            if (data != null) {
+                return data.getPath();
+            }
+            return null;
+        }
+    };
+}
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index a3ab431..ca7bba2 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -5135,9 +5135,16 @@
             final int level = bp.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
             if (level == PermissionInfo.PROTECTION_NORMAL
                     || level == PermissionInfo.PROTECTION_DANGEROUS) {
-                // If the permission is required, or it's optional and was previously
-                // granted to the application, then allow it. Otherwise deny.
-                allowed = (required || origPermissions.contains(perm));
+                // We grant a normal or dangerous permission if any of the following
+                // are true:
+                // 1) The permission is required
+                // 2) The permission is optional, but was granted in the past
+                // 3) The permission is optional, but was requested by an
+                //    app in /system (not /data)
+                //
+                // Otherwise, reject the permission.
+                allowed = (required || origPermissions.contains(perm)
+                        || (isSystemApp(ps) && !isUpdatedSystemApp(ps)));
             } else if (bp.packageSetting == null) {
                 // This permission is invalid; skip it.
                 allowed = false;
@@ -5155,8 +5162,7 @@
                 }
             }
             if (allowed) {
-                if ((ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0
-                        && ps.permissionsFixed) {
+                if (!isSystemApp(ps) && ps.permissionsFixed) {
                     // If this is an existing, non-system package, then
                     // we can't add any new permissions to it.
                     if (!allowedSig && !gp.grantedPermissions.contains(perm)) {
@@ -5199,8 +5205,7 @@
         }
 
         if ((changedPermission || replace) && !ps.permissionsFixed &&
-                ((ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) ||
-                ((ps.pkgFlags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0)){
+                !isSystemApp(ps) || isUpdatedSystemApp(ps)){
             // This is the first that we have heard about this package, so the
             // permissions we have now selected are fixed until explicitly
             // changed.
@@ -5405,8 +5410,9 @@
         }
 
         @Override
-        protected String packageForFilter(PackageParser.ActivityIntentInfo info) {
-            return info.activity.owner.packageName;
+        protected boolean isPackageForFilter(String packageName,
+                PackageParser.ActivityIntentInfo info) {
+            return packageName.equals(info.activity.owner.packageName);
         }
         
         @Override
@@ -5602,8 +5608,9 @@
         }
 
         @Override
-        protected String packageForFilter(PackageParser.ServiceIntentInfo info) {
-            return info.service.owner.packageName;
+        protected boolean isPackageForFilter(String packageName,
+                PackageParser.ServiceIntentInfo info) {
+            return packageName.equals(info.service.owner.packageName);
         }
         
         @Override
@@ -8381,6 +8388,10 @@
         return (ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0;
     }
 
+    private static boolean isUpdatedSystemApp(PackageSetting ps) {
+        return (ps.pkgFlags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
+    }
+
     private static boolean isUpdatedSystemApp(PackageParser.Package pkg) {
         return (pkg.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0;
     }
diff --git a/services/java/com/android/server/pm/PreferredIntentResolver.java b/services/java/com/android/server/pm/PreferredIntentResolver.java
index 3f1e50c..7fe6a05 100644
--- a/services/java/com/android/server/pm/PreferredIntentResolver.java
+++ b/services/java/com/android/server/pm/PreferredIntentResolver.java
@@ -27,8 +27,8 @@
         return new PreferredActivity[size];
     }
     @Override
-    protected String packageForFilter(PreferredActivity filter) {
-        return filter.mPref.mComponent.getPackageName();
+    protected boolean isPackageForFilter(String packageName, PreferredActivity filter) {
+        return packageName.equals(filter.mPref.mComponent.getPackageName());
     }
     @Override
     protected void dumpFilter(PrintWriter out, String prefix,