Merge "make new API to retrieve group identifier level1" into jb-mr2-dev
diff --git a/Android.mk b/Android.mk
index 8090448..2ad7a72 100644
--- a/Android.mk
+++ b/Android.mk
@@ -195,7 +195,6 @@
 	core/java/com/android/internal/view/IInputContext.aidl \
 	core/java/com/android/internal/view/IInputContextCallback.aidl \
 	core/java/com/android/internal/view/IInputMethod.aidl \
-	core/java/com/android/internal/view/IInputMethodCallback.aidl \
 	core/java/com/android/internal/view/IInputMethodClient.aidl \
 	core/java/com/android/internal/view/IInputMethodManager.aidl \
 	core/java/com/android/internal/view/IInputMethodSession.aidl \
@@ -307,7 +306,6 @@
 	frameworks/base/core/java/com/android/internal/textservice/ITextServicesSessionListener.aidl \
 	frameworks/base/core/java/com/android/internal/view/IInputContext.aidl \
 	frameworks/base/core/java/com/android/internal/view/IInputMethod.aidl \
-	frameworks/base/core/java/com/android/internal/view/IInputMethodCallback.aidl \
 	frameworks/base/core/java/com/android/internal/view/IInputMethodClient.aidl \
 	frameworks/base/core/java/com/android/internal/view/IInputMethodManager.aidl \
 	frameworks/base/core/java/com/android/internal/view/IInputMethodSession.aidl \
diff --git a/CleanSpec.mk b/CleanSpec.mk
index e715a9f..fc63866 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -157,6 +157,8 @@
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/telephony/java/com/android/internal/telephony/SmsRawData.*)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/com/android/internal/view/IInputMethodCallback.*)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/com/android/internal/view/IInputMethodSession.*)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/com/android/internal/view/IInputMethodCallback.*)
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
 # ************************************************
diff --git a/api/current.txt b/api/current.txt
index 4caa189..e001e1a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -339,8 +339,10 @@
     field public static final int checkedTextViewStyle = 16843720; // 0x10103c8
     field public static final int childDivider = 16843025; // 0x1010111
     field public static final int childIndicator = 16843020; // 0x101010c
+    field public static final int childIndicatorEnd = 16843732; // 0x10103d4
     field public static final int childIndicatorLeft = 16843023; // 0x101010f
     field public static final int childIndicatorRight = 16843024; // 0x1010110
+    field public static final int childIndicatorStart = 16843731; // 0x10103d3
     field public static final int choiceMode = 16843051; // 0x101012b
     field public static final int clearTaskOnLaunch = 16842773; // 0x1010015
     field public static final int clickable = 16842981; // 0x10100e5
@@ -574,8 +576,10 @@
     field public static final int indeterminateDuration = 16843069; // 0x101013d
     field public static final int indeterminateOnly = 16843066; // 0x101013a
     field public static final int indeterminateProgressStyle = 16843544; // 0x1010318
+    field public static final int indicatorEnd = 16843730; // 0x10103d2
     field public static final int indicatorLeft = 16843021; // 0x101010d
     field public static final int indicatorRight = 16843022; // 0x101010e
+    field public static final int indicatorStart = 16843729; // 0x10103d1
     field public static final int inflatedId = 16842995; // 0x10100f3
     field public static final int initOrder = 16842778; // 0x101001a
     field public static final int initialKeyguardLayout = 16843714; // 0x10103c2
@@ -854,6 +858,7 @@
     field public static final int resource = 16842789; // 0x1010025
     field public static final int restoreAnyVersion = 16843450; // 0x10102ba
     field public static final deprecated int restoreNeedsApplication = 16843421; // 0x101029d
+    field public static final int restrictedAccountType = 16843733; // 0x10103d5
     field public static final int right = 16843183; // 0x10101af
     field public static final int ringtonePreferenceStyle = 16842899; // 0x1010093
     field public static final int ringtoneType = 16843257; // 0x10101f9
@@ -2205,6 +2210,7 @@
     field public static final java.lang.String AUTHENTICATOR_ATTRIBUTES_NAME = "account-authenticator";
     field public static final java.lang.String AUTHENTICATOR_META_DATA_NAME = "android.accounts.AccountAuthenticator";
     field public static final int ERROR_CODE_BAD_ARGUMENTS = 7; // 0x7
+    field public static final int ERROR_CODE_BAD_AUTHENTICATION = 9; // 0x9
     field public static final int ERROR_CODE_BAD_REQUEST = 8; // 0x8
     field public static final int ERROR_CODE_CANCELED = 4; // 0x4
     field public static final int ERROR_CODE_INVALID_RESPONSE = 5; // 0x5
@@ -4333,6 +4339,7 @@
     method public boolean hasGrantedPolicy(android.content.ComponentName, int);
     method public boolean isActivePasswordSufficient();
     method public boolean isAdminActive(android.content.ComponentName);
+    method public boolean isDeviceOwner(java.lang.String);
     method public void lockNow();
     method public void removeActiveAdmin(android.content.ComponentName);
     method public boolean resetPassword(java.lang.String, int);
@@ -4607,8 +4614,13 @@
     method public boolean isEnabled();
     method public android.bluetooth.BluetoothServerSocket listenUsingInsecureRfcommWithServiceRecord(java.lang.String, java.util.UUID) throws java.io.IOException;
     method public android.bluetooth.BluetoothServerSocket listenUsingRfcommWithServiceRecord(java.lang.String, java.util.UUID) throws java.io.IOException;
+    method public boolean registerCallback(android.bluetooth.BluetoothAdapterCallback);
     method public boolean setName(java.lang.String);
     method public boolean startDiscovery();
+    method public boolean startLeScan();
+    method public boolean startLeScan(java.util.UUID[]);
+    method public void stopLeScan();
+    method public boolean unRegisterCallback(android.bluetooth.BluetoothAdapterCallback);
     field public static final java.lang.String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.adapter.action.CONNECTION_STATE_CHANGED";
     field public static final java.lang.String ACTION_DISCOVERY_FINISHED = "android.bluetooth.adapter.action.DISCOVERY_FINISHED";
     field public static final java.lang.String ACTION_DISCOVERY_STARTED = "android.bluetooth.adapter.action.DISCOVERY_STARTED";
@@ -4639,6 +4651,14 @@
     field public static final int STATE_TURNING_ON = 11; // 0xb
   }
 
+  public abstract class BluetoothAdapterCallback {
+    ctor public BluetoothAdapterCallback();
+    method public void onCallbackRegistration(int);
+    method public void onLeScan(android.bluetooth.BluetoothDevice, int, byte[]);
+    field public static final int CALLBACK_REGISTERED = 0; // 0x0
+    field public static final int CALLBACK_REGISTRATION_FAILURE = 1; // 0x1
+  }
+
   public class BluetoothAssignedNumbers {
     field public static final int ACCEL_SEMICONDUCTOR = 74; // 0x4a
     field public static final int ALCATEL = 36; // 0x24
@@ -4831,6 +4851,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.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();
@@ -4863,6 +4884,159 @@
     field public static final java.lang.String EXTRA_UUID = "android.bluetooth.device.extra.UUID";
   }
 
+  public final class BluetoothGatt implements android.bluetooth.BluetoothProfile {
+    method public void abortReliableWrite(android.bluetooth.BluetoothDevice);
+    method public boolean beginReliableWrite();
+    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 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();
+    method public boolean readCharacteristic(android.bluetooth.BluetoothGattCharacteristic);
+    method public boolean readDescriptor(android.bluetooth.BluetoothGattDescriptor);
+    method public boolean readRemoteRssi();
+    method public boolean setCharacteristicNotification(android.bluetooth.BluetoothGattCharacteristic, boolean);
+    method public boolean writeCharacteristic(android.bluetooth.BluetoothGattCharacteristic);
+    method public boolean writeDescriptor(android.bluetooth.BluetoothGattDescriptor);
+    field public static final int GATT_FAILURE = 0; // 0x0
+    field public static final int GATT_INSUFFICIENT_AUTHENTICATION = 5; // 0x5
+    field public static final int GATT_INSUFFICIENT_ENCRYPTION = 15; // 0xf
+    field public static final int GATT_INVALID_ATTRIBUTE_LENGTH = 13; // 0xd
+    field public static final int GATT_INVALID_OFFSET = 7; // 0x7
+    field public static final int GATT_READ_NOT_PERMITTED = 2; // 0x2
+    field public static final int GATT_REQUEST_NOT_SUPPORTED = 6; // 0x6
+    field public static final int GATT_SUCCESS = 0; // 0x0
+    field public static final int GATT_WRITE_NOT_PERMITTED = 3; // 0x3
+  }
+
+  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);
+  }
+
+  public class BluetoothGattCharacteristic {
+    ctor public BluetoothGattCharacteristic(java.util.UUID, int, int);
+    method public boolean addDescriptor(android.bluetooth.BluetoothGattDescriptor);
+    method public android.bluetooth.BluetoothGattDescriptor getDescriptor(java.util.UUID);
+    method public java.util.List<android.bluetooth.BluetoothGattDescriptor> getDescriptors();
+    method public java.lang.Float getFloatValue(int, int);
+    method public int getInstanceId();
+    method public java.lang.Integer getIntValue(int, int);
+    method public int getPermissions();
+    method public int getProperties();
+    method public android.bluetooth.BluetoothGattService getService();
+    method public java.lang.String getStringValue(int);
+    method public java.util.UUID getUuid();
+    method public byte[] getValue();
+    method public int getWriteType();
+    method public boolean setValue(byte[]);
+    method public boolean setValue(int, int, int);
+    method public boolean setValue(int, int, int, int);
+    method public boolean setValue(java.lang.String);
+    method public void setWriteType(int);
+    field public static final int FORMAT_FLOAT = 52; // 0x34
+    field public static final int FORMAT_SFLOAT = 50; // 0x32
+    field public static final int FORMAT_SINT16 = 34; // 0x22
+    field public static final int FORMAT_SINT32 = 36; // 0x24
+    field public static final int FORMAT_SINT8 = 33; // 0x21
+    field public static final int FORMAT_UINT16 = 18; // 0x12
+    field public static final int FORMAT_UINT32 = 20; // 0x14
+    field public static final int FORMAT_UINT8 = 17; // 0x11
+    field public static final int PERMISSION_READ = 1; // 0x1
+    field public static final int PERMISSION_READ_ENCRYPTED = 2; // 0x2
+    field public static final int PERMISSION_READ_ENCRYPTED_MITM = 4; // 0x4
+    field public static final int PERMISSION_WRITE = 16; // 0x10
+    field public static final int PERMISSION_WRITE_ENCRYPTED = 32; // 0x20
+    field public static final int PERMISSION_WRITE_ENCRYPTED_MITM = 64; // 0x40
+    field public static final int PERMISSION_WRITE_SIGNED = 128; // 0x80
+    field public static final int PERMISSION_WRITE_SIGNED_MITM = 256; // 0x100
+    field public static final int PROPERTY_BROADCAST = 1; // 0x1
+    field public static final int PROPERTY_EXTENDED_PROPS = 128; // 0x80
+    field public static final int PROPERTY_INDICATE = 32; // 0x20
+    field public static final int PROPERTY_NOTIFY = 16; // 0x10
+    field public static final int PROPERTY_READ = 2; // 0x2
+    field public static final int PROPERTY_SIGNED_WRITE = 64; // 0x40
+    field public static final int PROPERTY_WRITE = 8; // 0x8
+    field public static final int PROPERTY_WRITE_NO_RESPONSE = 4; // 0x4
+    field public static final int WRITE_TYPE_DEFAULT = 2; // 0x2
+    field public static final int WRITE_TYPE_NO_RESPONSE = 1; // 0x1
+    field public static final int WRITE_TYPE_SIGNED = 4; // 0x4
+    field protected java.util.List mDescriptors;
+  }
+
+  public class BluetoothGattDescriptor {
+    ctor public BluetoothGattDescriptor(java.util.UUID, int);
+    method public android.bluetooth.BluetoothGattCharacteristic getCharacteristic();
+    method public int getPermissions();
+    method public java.util.UUID getUuid();
+    method public byte[] getValue();
+    method public boolean setValue(byte[]);
+    field public static final byte[] DISABLE_NOTIFICATION_VALUE;
+    field public static final byte[] ENABLE_INDICATION_VALUE;
+    field public static final byte[] ENABLE_NOTIFICATION_VALUE;
+    field public static final int PERMISSION_READ = 1; // 0x1
+    field public static final int PERMISSION_READ_ENCRYPTED = 2; // 0x2
+    field public static final int PERMISSION_READ_ENCRYPTED_MITM = 4; // 0x4
+    field public static final int PERMISSION_WRITE = 16; // 0x10
+    field public static final int PERMISSION_WRITE_ENCRYPTED = 32; // 0x20
+    field public static final int PERMISSION_WRITE_ENCRYPTED_MITM = 64; // 0x40
+    field public static final int PERMISSION_WRITE_SIGNED = 128; // 0x80
+    field public static final int PERMISSION_WRITE_SIGNED_MITM = 256; // 0x100
+  }
+
+  public final class BluetoothGattServer implements android.bluetooth.BluetoothProfile {
+    method public boolean addService(android.bluetooth.BluetoothGattService);
+    method public void cancelConnection(android.bluetooth.BluetoothDevice);
+    method public void clearServices();
+    method public boolean connect(android.bluetooth.BluetoothDevice, boolean);
+    method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+    method public int getConnectionState(android.bluetooth.BluetoothDevice);
+    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();
+    method public boolean notifyCharacteristicChanged(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothGattCharacteristic, boolean);
+    method public boolean removeService(android.bluetooth.BluetoothGattService);
+    method public boolean sendResponse(android.bluetooth.BluetoothDevice, int, int, int, byte[]);
+  }
+
+  public abstract class BluetoothGattServerCallback {
+    ctor public BluetoothGattServerCallback();
+    method public void onCharacteristicReadRequest(android.bluetooth.BluetoothDevice, int, int, android.bluetooth.BluetoothGattCharacteristic);
+    method public void onCharacteristicWriteRequest(android.bluetooth.BluetoothDevice, int, android.bluetooth.BluetoothGattCharacteristic, boolean, boolean, int, byte[]);
+    method public void onConnectionStateChange(android.bluetooth.BluetoothDevice, int, int);
+    method public void onDescriptorReadRequest(android.bluetooth.BluetoothDevice, int, int, android.bluetooth.BluetoothGattDescriptor);
+    method public void onDescriptorWriteRequest(android.bluetooth.BluetoothDevice, int, android.bluetooth.BluetoothGattDescriptor, boolean, boolean, int, byte[]);
+    method public void onExecuteWrite(android.bluetooth.BluetoothDevice, int, boolean);
+    method public void onServiceAdded(int, android.bluetooth.BluetoothGattService);
+  }
+
+  public class BluetoothGattService {
+    ctor public BluetoothGattService(java.util.UUID, int);
+    method public boolean addCharacteristic(android.bluetooth.BluetoothGattCharacteristic);
+    method public boolean addService(android.bluetooth.BluetoothGattService);
+    method public android.bluetooth.BluetoothGattCharacteristic getCharacteristic(java.util.UUID);
+    method public java.util.List<android.bluetooth.BluetoothGattCharacteristic> getCharacteristics();
+    method public java.util.List<android.bluetooth.BluetoothGattService> getIncludedServices();
+    method public int getInstanceId();
+    method public int getType();
+    method public java.util.UUID getUuid();
+    field public static final int SERVICE_TYPE_PRIMARY = 0; // 0x0
+    field public static final int SERVICE_TYPE_SECONDARY = 1; // 0x1
+    field protected java.util.List mCharacteristics;
+    field protected java.util.List mIncludedServices;
+  }
+
   public final class BluetoothHeadset implements android.bluetooth.BluetoothProfile {
     method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
     method public int getConnectionState(android.bluetooth.BluetoothDevice);
@@ -4925,6 +5099,14 @@
     method public void onHealthChannelStateChange(android.bluetooth.BluetoothHealthAppConfiguration, android.bluetooth.BluetoothDevice, int, int, android.os.ParcelFileDescriptor, int);
   }
 
+  public final class BluetoothManager {
+    method public android.bluetooth.BluetoothAdapter getAdapter();
+    method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(int);
+    method public int getConnectionState(android.bluetooth.BluetoothDevice, int);
+    method public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int, int[]);
+    method public android.bluetooth.BluetoothGattServer openGattServer(android.content.Context, android.bluetooth.BluetoothGattServerCallback);
+  }
+
   public abstract interface BluetoothProfile {
     method public abstract java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
     method public abstract int getConnectionState(android.bluetooth.BluetoothDevice);
@@ -4932,6 +5114,8 @@
     field public static final int A2DP = 2; // 0x2
     field public static final java.lang.String EXTRA_PREVIOUS_STATE = "android.bluetooth.profile.extra.PREVIOUS_STATE";
     field public static final java.lang.String EXTRA_STATE = "android.bluetooth.profile.extra.STATE";
+    field public static final int GATT = 7; // 0x7
+    field public static final int GATT_SERVER = 8; // 0x8
     field public static final int HEADSET = 1; // 0x1
     field public static final int HEALTH = 3; // 0x3
     field public static final int STATE_CONNECTED = 2; // 0x2
@@ -5489,6 +5673,7 @@
     field public static final int BIND_IMPORTANT = 64; // 0x40
     field public static final int BIND_NOT_FOREGROUND = 4; // 0x4
     field public static final int BIND_WAIVE_PRIORITY = 32; // 0x20
+    field public static final java.lang.String BLUETOOTH_SERVICE = "bluetooth";
     field public static final java.lang.String CLIPBOARD_SERVICE = "clipboard";
     field public static final java.lang.String CONNECTIVITY_SERVICE = "connectivity";
     field public static final int CONTEXT_IGNORE_SECURITY = 2; // 0x2
@@ -5993,6 +6178,7 @@
     field public static final java.lang.String EXTRA_REMOTE_INTENT_TOKEN = "android.intent.extra.remote_intent_token";
     field public static final java.lang.String EXTRA_REPLACING = "android.intent.extra.REPLACING";
     field public static final java.lang.String EXTRA_RESTRICTIONS = "android.intent.extra.restrictions";
+    field public static final java.lang.String EXTRA_RESTRICTIONS_INTENT = "android.intent.extra.restrictions_intent";
     field public static final java.lang.String EXTRA_RETURN_RESULT = "android.intent.extra.RETURN_RESULT";
     field public static final java.lang.String EXTRA_SHORTCUT_ICON = "android.intent.extra.shortcut.ICON";
     field public static final java.lang.String EXTRA_SHORTCUT_ICON_RESOURCE = "android.intent.extra.shortcut.ICON_RESOURCE";
@@ -6241,12 +6427,25 @@
     ctor public RestrictionEntry(java.lang.String, java.lang.String[]);
     ctor public RestrictionEntry(android.os.Parcel);
     method public int describeContents();
-    method public boolean getBooleanValue();
-    method public java.lang.String[] getMultipleValues();
-    method public java.lang.String getStringValue();
-    method public void setMultipleValues(java.lang.String[]);
-    method public void setValue(java.lang.String);
-    method public void setValue(boolean);
+    method public java.lang.String[] getAllSelectedStrings();
+    method public java.lang.String[] getChoiceEntries();
+    method public java.lang.String[] getChoiceValues();
+    method public java.lang.String getDescription();
+    method public java.lang.String getKey();
+    method public boolean getSelectedState();
+    method public java.lang.String getSelectedString();
+    method public java.lang.String getTitle();
+    method public int getType();
+    method public void setAllSelectedStrings(java.lang.String[]);
+    method public void setChoiceEntries(java.lang.String[]);
+    method public void setChoiceEntries(android.content.Context, int);
+    method public void setChoiceValues(java.lang.String[]);
+    method public void setChoiceValues(android.content.Context, int);
+    method public void setDescription(java.lang.String);
+    method public void setSelectedState(boolean);
+    method public void setSelectedString(java.lang.String);
+    method public void setTitle(java.lang.String);
+    method public void setType(int);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator CREATOR;
     field public static final int TYPE_BOOLEAN = 1; // 0x1
@@ -6254,12 +6453,6 @@
     field public static final int TYPE_CHOICE_LEVEL = 3; // 0x3
     field public static final int TYPE_MULTI_SELECT = 4; // 0x4
     field public static final int TYPE_NULL = 0; // 0x0
-    field public java.lang.String[] choices;
-    field public java.lang.String description;
-    field public java.lang.String key;
-    field public java.lang.String title;
-    field public int type;
-    field public java.lang.String[] values;
   }
 
   public class SearchRecentSuggestionsProvider extends android.content.ContentProvider {
@@ -6652,6 +6845,7 @@
     method public abstract boolean addPermission(android.content.pm.PermissionInfo);
     method public abstract boolean addPermissionAsync(android.content.pm.PermissionInfo);
     method public abstract deprecated void addPreferredActivity(android.content.IntentFilter, int, android.content.ComponentName[], android.content.ComponentName);
+    method public android.content.Intent buildPermissionRequestIntent(java.lang.String...);
     method public abstract java.lang.String[] canonicalToCurrentPackageNames(java.lang.String[]);
     method public abstract int checkPermission(java.lang.String, java.lang.String);
     method public abstract int checkSignatures(java.lang.String, java.lang.String);
@@ -6726,6 +6920,7 @@
     field public static final int DONT_KILL_APP = 1; // 0x1
     field public static final java.lang.String EXTRA_VERIFICATION_ID = "android.content.pm.extra.VERIFICATION_ID";
     field public static final java.lang.String EXTRA_VERIFICATION_RESULT = "android.content.pm.extra.VERIFICATION_RESULT";
+    field public static final java.lang.String FEATURE_APP_WIDGETS = "android.software.app_widgets";
     field public static final java.lang.String FEATURE_AUDIO_LOW_LATENCY = "android.hardware.audio.low_latency";
     field public static final java.lang.String FEATURE_BLUETOOTH = "android.hardware.bluetooth";
     field public static final java.lang.String FEATURE_CAMERA = "android.hardware.camera";
@@ -6736,6 +6931,8 @@
     field public static final java.lang.String FEATURE_FAKETOUCH = "android.hardware.faketouch";
     field public static final java.lang.String FEATURE_FAKETOUCH_MULTITOUCH_DISTINCT = "android.hardware.faketouch.multitouch.distinct";
     field public static final java.lang.String FEATURE_FAKETOUCH_MULTITOUCH_JAZZHAND = "android.hardware.faketouch.multitouch.jazzhand";
+    field public static final java.lang.String FEATURE_HOME_SCREEN = "android.software.home_screen";
+    field public static final java.lang.String FEATURE_INPUT_METHODS = "android.software.input_methods";
     field public static final java.lang.String FEATURE_LIVE_WALLPAPER = "android.software.live_wallpaper";
     field public static final java.lang.String FEATURE_LOCATION = "android.hardware.location";
     field public static final java.lang.String FEATURE_LOCATION_GPS = "android.hardware.location.gps";
@@ -10297,10 +10494,12 @@
   }
 
   public class UsbDeviceConnection {
-    method public int bulkTransfer(android.hardware.usb.UsbEndpoint, byte[], int, int);
+    method public deprecated int bulkTransfer(android.hardware.usb.UsbEndpoint, byte[], int, int);
+    method public int bulkTransfer(android.hardware.usb.UsbEndpoint, byte[], int, int, int);
     method public boolean claimInterface(android.hardware.usb.UsbInterface, boolean);
     method public void close();
-    method public int controlTransfer(int, int, int, int, byte[], int, int);
+    method public deprecated int controlTransfer(int, int, int, int, byte[], int, int);
+    method public int controlTransfer(int, int, int, int, byte[], int, int, int);
     method public int getFileDescriptor();
     method public byte[] getRawDescriptors();
     method public java.lang.String getSerial();
@@ -10956,6 +11155,7 @@
     method public void playSoundEffect(int);
     method public void playSoundEffect(int, float);
     method public void registerMediaButtonEventReceiver(android.content.ComponentName);
+    method public void registerMediaButtonEventReceiver(android.app.PendingIntent);
     method public void registerRemoteControlClient(android.media.RemoteControlClient);
     method public int requestAudioFocus(android.media.AudioManager.OnAudioFocusChangeListener, int, int);
     method public deprecated void setBluetoothA2dpOn(boolean);
@@ -10976,6 +11176,7 @@
     method public void stopBluetoothSco();
     method public void unloadSoundEffects();
     method public void unregisterMediaButtonEventReceiver(android.content.ComponentName);
+    method public void unregisterMediaButtonEventReceiver(android.app.PendingIntent);
     method public void unregisterRemoteControlClient(android.media.RemoteControlClient);
     field public static final java.lang.String ACTION_AUDIO_BECOMING_NOISY = "android.media.AUDIO_BECOMING_NOISY";
     field public static final deprecated java.lang.String ACTION_SCO_AUDIO_STATE_CHANGED = "android.media.SCO_AUDIO_STATE_CHANGED";
@@ -12236,6 +12437,14 @@
     field public static final int CONTENT_TYPE_VOICE = 3; // 0x3
     field public static final java.lang.String EFFECT_AUXILIARY = "Auxiliary";
     field public static final java.lang.String EFFECT_INSERT = "Insert";
+    field public static final java.util.UUID EFFECT_TYPE_AEC;
+    field public static final java.util.UUID EFFECT_TYPE_AGC;
+    field public static final java.util.UUID EFFECT_TYPE_BASS_BOOST;
+    field public static final java.util.UUID EFFECT_TYPE_ENV_REVERB;
+    field public static final java.util.UUID EFFECT_TYPE_EQUALIZER;
+    field public static final java.util.UUID EFFECT_TYPE_NS;
+    field public static final java.util.UUID EFFECT_TYPE_PRESET_REVERB;
+    field public static final java.util.UUID EFFECT_TYPE_VIRTUALIZER;
     field public static final int ERROR = -1; // 0xffffffff
     field public static final int ERROR_BAD_VALUE = -4; // 0xfffffffc
     field public static final int ERROR_DEAD_OBJECT = -7; // 0xfffffff9
@@ -16300,7 +16509,7 @@
     method public abstract android.os.IBinder asBinder();
   }
 
-  public class Looper {
+  public final class Looper {
     method public void dump(android.util.Printer, java.lang.String);
     method public static android.os.Looper getMainLooper();
     method public java.lang.Thread getThread();
@@ -16355,9 +16564,9 @@
     field public int what;
   }
 
-  public class MessageQueue {
-    method public final void addIdleHandler(android.os.MessageQueue.IdleHandler);
-    method public final void removeIdleHandler(android.os.MessageQueue.IdleHandler);
+  public final class MessageQueue {
+    method public void addIdleHandler(android.os.MessageQueue.IdleHandler);
+    method public void removeIdleHandler(android.os.MessageQueue.IdleHandler);
   }
 
   public static abstract interface MessageQueue.IdleHandler {
@@ -16775,13 +16984,28 @@
   }
 
   public class UserManager {
+    method public static synchronized android.os.UserManager get(android.content.Context);
     method public long getSerialNumberForUser(android.os.UserHandle);
     method public int getUserCount();
     method public android.os.UserHandle getUserForSerialNumber(long);
     method public java.lang.String getUserName();
+    method public android.os.Bundle getUserRestrictions();
+    method public android.os.Bundle getUserRestrictions(android.os.UserHandle);
     method public boolean isUserAGoat();
+    method public boolean isUserRestricted();
     method public boolean isUserRunning(android.os.UserHandle);
     method public boolean isUserRunningOrStopping(android.os.UserHandle);
+    method public void setUserRestriction(java.lang.String, boolean);
+    method public void setUserRestrictions(android.os.Bundle);
+    method public void setUserRestrictions(android.os.Bundle, android.os.UserHandle);
+    field public static final java.lang.String DISALLOW_CONFIG_BLUETOOTH = "no_config_bluetooth";
+    field public static final java.lang.String DISALLOW_CONFIG_WIFI = "no_config_wifi";
+    field public static final java.lang.String DISALLOW_INSTALL_APPS = "no_install_apps";
+    field public static final java.lang.String DISALLOW_INSTALL_UNKNOWN_SOURCES = "no_install_unknown_sources";
+    field public static final java.lang.String DISALLOW_MODIFY_ACCOUNTS = "no_modify_accounts";
+    field public static final java.lang.String DISALLOW_SHARE_LOCATION = "no_share_location";
+    field public static final java.lang.String DISALLOW_UNINSTALL_APPS = "no_uninstall_apps";
+    field public static final java.lang.String DISALLOW_USB_FILE_TRANSFER = "no_usb_file_transfer";
   }
 
   public abstract class Vibrator {
@@ -17914,6 +18138,13 @@
     field public static final java.lang.String TYPE = "data2";
   }
 
+  public static final class ContactsContract.CommonDataKinds.Contactables implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
+    ctor public ContactsContract.CommonDataKinds.Contactables();
+    field public static final android.net.Uri CONTENT_FILTER_URI;
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final java.lang.String VISIBLE_CONTACTS_ONLY = "visible_contacts_only";
+  }
+
   public static final class ContactsContract.CommonDataKinds.Email implements android.provider.ContactsContract.CommonDataKinds.CommonColumns android.provider.ContactsContract.DataColumnsWithJoins {
     method public static final java.lang.CharSequence getTypeLabel(android.content.res.Resources, int, java.lang.CharSequence);
     method public static final int getTypeLabelResource(int);
@@ -18207,6 +18438,7 @@
     method public static android.net.Uri getContactLookupUri(android.content.ContentResolver, android.net.Uri);
     field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/data";
     field public static final android.net.Uri CONTENT_URI;
+    field public static final java.lang.String VISIBLE_CONTACTS_ONLY = "visible_contacts_only";
   }
 
   protected static abstract interface ContactsContract.DataColumns {
@@ -18237,7 +18469,7 @@
     field public static final java.lang.String SYNC4 = "data_sync4";
   }
 
-  protected static abstract interface ContactsContract.DataColumnsWithJoins implements android.provider.BaseColumns android.provider.ContactsContract.ContactNameColumns android.provider.ContactsContract.ContactOptionsColumns android.provider.ContactsContract.ContactStatusColumns android.provider.ContactsContract.ContactsColumns android.provider.ContactsContract.DataColumns android.provider.ContactsContract.RawContactsColumns android.provider.ContactsContract.StatusColumns {
+  protected static abstract interface ContactsContract.DataColumnsWithJoins implements android.provider.BaseColumns android.provider.ContactsContract.ContactNameColumns android.provider.ContactsContract.ContactOptionsColumns android.provider.ContactsContract.ContactStatusColumns android.provider.ContactsContract.ContactsColumns android.provider.ContactsContract.DataColumns android.provider.ContactsContract.DataUsageStatColumns android.provider.ContactsContract.RawContactsColumns android.provider.ContactsContract.StatusColumns {
   }
 
   public static final class ContactsContract.DataUsageFeedback {
@@ -18250,6 +18482,15 @@
     field public static final java.lang.String USAGE_TYPE_SHORT_TEXT = "short_text";
   }
 
+  protected static abstract interface ContactsContract.DataUsageStatColumns {
+    field public static final java.lang.String LAST_TIME_USED = "last_time_used";
+    field public static final java.lang.String TIMES_USED = "times_used";
+    field public static final java.lang.String USAGE_TYPE = "usage_type";
+    field public static final int USAGE_TYPE_CALL = 0; // 0x0
+    field public static final int USAGE_TYPE_LONG_TEXT = 1; // 0x1
+    field public static final int USAGE_TYPE_SHORT_TEXT = 2; // 0x2
+  }
+
   public static final class ContactsContract.Directory implements android.provider.BaseColumns {
     method public static void notifyDirectoryChange(android.content.ContentResolver);
     field public static final java.lang.String ACCOUNT_NAME = "accountName";
@@ -20515,6 +20756,8 @@
     method public static android.content.Intent createInstallIntent();
     method public static java.security.cert.X509Certificate[] getCertificateChain(android.content.Context, java.lang.String) throws java.lang.InterruptedException, android.security.KeyChainException;
     method public static java.security.PrivateKey getPrivateKey(android.content.Context, java.lang.String) throws java.lang.InterruptedException, android.security.KeyChainException;
+    method public static boolean isBoundKeyAlgorithm(java.lang.String);
+    method public static boolean isKeyAlgorithmSupported(java.lang.String);
     field public static final java.lang.String ACTION_STORAGE_CHANGED = "android.security.STORAGE_CHANGED";
     field public static final java.lang.String EXTRA_CERTIFICATE = "CERT";
     field public static final java.lang.String EXTRA_NAME = "name";
@@ -22563,6 +22806,7 @@
     method public static java.lang.CharSequence format(java.lang.CharSequence, long);
     method public static java.lang.CharSequence format(java.lang.CharSequence, java.util.Date);
     method public static java.lang.CharSequence format(java.lang.CharSequence, java.util.Calendar);
+    method public static java.lang.String getBestDateTimePattern(java.util.Locale, java.lang.String);
     method public static java.text.DateFormat getDateFormat(android.content.Context);
     method public static char[] getDateFormatOrder(android.content.Context);
     method public static java.text.DateFormat getLongDateFormat(android.content.Context);
@@ -26133,6 +26377,8 @@
     method public void addOnPreDrawListener(android.view.ViewTreeObserver.OnPreDrawListener);
     method public void addOnScrollChangedListener(android.view.ViewTreeObserver.OnScrollChangedListener);
     method public void addOnTouchModeChangeListener(android.view.ViewTreeObserver.OnTouchModeChangeListener);
+    method public void addOnWindowAttachListener(android.view.ViewTreeObserver.OnWindowAttachListener);
+    method public void addOnWindowFocusChangeListener(android.view.ViewTreeObserver.OnWindowFocusChangeListener);
     method public final void dispatchOnDraw();
     method public final void dispatchOnGlobalLayout();
     method public final boolean dispatchOnPreDraw();
@@ -26144,6 +26390,8 @@
     method public void removeOnPreDrawListener(android.view.ViewTreeObserver.OnPreDrawListener);
     method public void removeOnScrollChangedListener(android.view.ViewTreeObserver.OnScrollChangedListener);
     method public void removeOnTouchModeChangeListener(android.view.ViewTreeObserver.OnTouchModeChangeListener);
+    method public void removeOnWindowAttachListener(android.view.ViewTreeObserver.OnWindowAttachListener);
+    method public void removeOnWindowFocusChangeListener(android.view.ViewTreeObserver.OnWindowFocusChangeListener);
   }
 
   public static abstract interface ViewTreeObserver.OnDrawListener {
@@ -26170,6 +26418,15 @@
     method public abstract void onTouchModeChanged(boolean);
   }
 
+  public static abstract interface ViewTreeObserver.OnWindowAttachListener {
+    method public abstract void onWindowAttached();
+    method public abstract void onWindowDetached();
+  }
+
+  public static abstract interface ViewTreeObserver.OnWindowFocusChangeListener {
+    method public abstract void onWindowFocusChanged(boolean);
+  }
+
   public abstract class Window {
     ctor public Window(android.content.Context);
     method public abstract void addContentView(android.view.View, android.view.ViewGroup.LayoutParams);
@@ -28599,8 +28856,10 @@
     method public void setChildDivider(android.graphics.drawable.Drawable);
     method public void setChildIndicator(android.graphics.drawable.Drawable);
     method public void setChildIndicatorBounds(int, int);
+    method public void setChildIndicatorBoundsRelative(int, int);
     method public void setGroupIndicator(android.graphics.drawable.Drawable);
     method public void setIndicatorBounds(int, int);
+    method public void setIndicatorBoundsRelative(int, int);
     method public void setOnChildClickListener(android.widget.ExpandableListView.OnChildClickListener);
     method public void setOnGroupClickListener(android.widget.ExpandableListView.OnGroupClickListener);
     method public void setOnGroupCollapseListener(android.widget.ExpandableListView.OnGroupCollapseListener);
diff --git a/cmds/interrupter/Android.mk b/cmds/interrupter/Android.mk
new file mode 100644
index 0000000..e324627
--- /dev/null
+++ b/cmds/interrupter/Android.mk
@@ -0,0 +1,21 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+    interrupter.c
+LOCAL_MODULE := interrupter
+LOCAL_MODULE_TAGS := eng tests
+LOCAL_LDFLAGS := -ldl
+
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+    interrupter.c
+LOCAL_MODULE := interrupter
+LOCAL_MODULE_TAGS := eng tests
+LOCAL_LDFLAGS := -ldl
+
+include $(BUILD_HOST_SHARED_LIBRARY)
\ No newline at end of file
diff --git a/cmds/interrupter/interrupter.c b/cmds/interrupter/interrupter.c
new file mode 100644
index 0000000..ae55515
--- /dev/null
+++ b/cmds/interrupter/interrupter.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2012, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/**
+ * The probability of a syscall failing from 0.0 to 1.0
+ */
+#define PROBABILITY 0.9
+
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+/* for various intercepted calls */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+/* For builds on glibc */
+#define __USE_GNU
+#include <dlfcn.h>
+
+#include "interrupter.h"
+
+static int probability = PROBABILITY * RAND_MAX;
+
+static int maybe_interrupt() {
+    if (rand() < probability) {
+        return 1;
+    }
+    return 0;
+}
+
+DEFINE_INTERCEPT(read, ssize_t, int, void*, size_t);
+DEFINE_INTERCEPT(write, ssize_t, int, const void*, size_t);
+DEFINE_INTERCEPT(accept, int, int, struct sockaddr*, socklen_t*);
+DEFINE_INTERCEPT(creat, int, const char*, mode_t);
diff --git a/cmds/interrupter/interrupter.h b/cmds/interrupter/interrupter.h
new file mode 100644
index 0000000..9ad0277
--- /dev/null
+++ b/cmds/interrupter/interrupter.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2012, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define CONCATENATE(arg1, arg2)   CONCATENATE1(arg1, arg2)
+#define CONCATENATE1(arg1, arg2)  CONCATENATE2(arg1, arg2)
+#define CONCATENATE2(arg1, arg2)  arg1##arg2
+
+#define INTERRUPTER(sym) \
+    if (real_##sym == NULL) \
+        __init_##sym(); \
+    if (maybe_interrupt()) { \
+        errno = EINTR; \
+        return -1; \
+    }
+
+#define CALL_FUNCTION_1(sym, ret, type1) \
+ret (*real_##sym)(type1) = NULL; \
+ret sym(type1 arg1) { \
+    INTERRUPTER(sym) \
+    return real_##sym(arg1); \
+}
+
+#define CALL_FUNCTION_2(sym, ret, type1, type2) \
+ret (*real_##sym)(type1, type2) = NULL; \
+ret sym(type1 arg1, type2 arg2) { \
+    INTERRUPTER(sym) \
+    return real_##sym(arg1, arg2); \
+}
+
+#define CALL_FUNCTION_3(sym, ret, type1, type2, type3) \
+ret (*real_##sym)(type1, type2, type3) = NULL; \
+ret sym(type1 arg1, type2 arg2, type3 arg3) { \
+    INTERRUPTER(sym) \
+    return real_##sym(arg1, arg2, arg3); \
+}
+
+#define CALL_FUNCTION_4(sym, ret, type1, type2, type3, type4) \
+ret (*real_##sym)(type1, type2, type3, type4) = NULL; \
+ret sym(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \
+    INTERRUPTER(sym) \
+    return real_##sym(arg1, arg2, arg3, arg4); \
+}
+
+#define CALL_FUNCTION_5(sym, ret, type1, type2, type3, type4, type5) \
+ret (*real_##sym)(type1, type2, type3, type4, type5) = NULL; \
+ret sym(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) { \
+    INTERRUPTER(sym) \
+    return real_##sym(arg1, arg2, arg3, arg4, arg5); \
+}
+
+#define DEFINE_INTERCEPT_N(N, sym, ret, ...) \
+static void __init_##sym(void); \
+CONCATENATE(CALL_FUNCTION_, N)(sym, ret, __VA_ARGS__) \
+static void __init_##sym(void) { \
+    real_##sym = dlsym(RTLD_NEXT, #sym); \
+    if (real_##sym == NULL) { \
+        fprintf(stderr, "Error hooking " #sym ": %s\n", dlerror()); \
+    } \
+}
+
+#define INTERCEPT_NARG(...) INTERCEPT_NARG_N(__VA_ARGS__, INTERCEPT_RSEQ_N())
+#define INTERCEPT_NARG_N(...) INTERCEPT_ARG_N(__VA_ARGS__)
+#define INTERCEPT_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N
+#define INTERCEPT_RSEQ_N() 8, 7, 6, 5, 4, 3, 2, 1, 0
+
+#define DEFINE_INTERCEPT(sym, ret, ...) DEFINE_INTERCEPT_N(INTERCEPT_NARG(__VA_ARGS__), sym, ret, __VA_ARGS__)
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 98c82b5..224945a 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -1218,7 +1218,8 @@
         ComponentName cn = ComponentName.unflattenFromString(pkg);
         if (cn == null) {
             try {
-                mPm.setApplicationEnabledSetting(pkg, state, 0, userId);
+                mPm.setApplicationEnabledSetting(pkg, state, 0, userId,
+                        "shell:" + android.os.Process.myUid());
                 System.err.println("Package " + pkg + " new state: "
                         + enabledSettingToString(
                         mPm.getApplicationEnabledSetting(pkg, userId)));
diff --git a/cmds/wm/src/com/android/commands/wm/Wm.java b/cmds/wm/src/com/android/commands/wm/Wm.java
index f48764f..31eba96 100644
--- a/cmds/wm/src/com/android/commands/wm/Wm.java
+++ b/cmds/wm/src/com/android/commands/wm/Wm.java
@@ -19,6 +19,7 @@
 package com.android.commands.wm;
 
 import android.content.Context;
+import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -87,9 +88,22 @@
     }
 
     private void runDisplaySize() throws Exception {
-        String size = nextArgRequired();
+        String size = nextArg();
         int w, h;
-        if ("reset".equals(size)) {
+        if (size == null) {
+            Point initialSize = new Point();
+            Point baseSize = new Point();
+            try {
+                mWm.getInitialDisplaySize(Display.DEFAULT_DISPLAY, initialSize);
+                mWm.getBaseDisplaySize(Display.DEFAULT_DISPLAY, baseSize);
+                System.out.println("Physical size: " + initialSize.x + "x" + initialSize.y);
+                if (!initialSize.equals(baseSize)) {
+                    System.out.println("Override size: " + baseSize.x + "x" + baseSize.y);
+                }
+            } catch (RemoteException e) {
+            }
+            return;
+        } else if ("reset".equals(size)) {
             w = h = -1;
         } else {
             int div = size.indexOf('x');
@@ -120,9 +134,20 @@
     }
 
     private void runDisplayDensity() throws Exception {
-        String densityStr = nextArgRequired();
+        String densityStr = nextArg();
         int density;
-        if ("reset".equals(densityStr)) {
+        if (densityStr == null) {
+            try {
+                int initialDensity = mWm.getInitialDisplayDensity(Display.DEFAULT_DISPLAY);
+                int baseDensity = mWm.getBaseDisplayDensity(Display.DEFAULT_DISPLAY);
+                System.out.println("Physical density: " + initialDensity);
+                if (initialDensity != baseDensity) {
+                    System.out.println("Override density: " + baseDensity);
+                }
+            } catch (RemoteException e) {
+            }
+            return;
+        } else if ("reset".equals(densityStr)) {
             density = -1;
         } else {
             try {
@@ -231,7 +256,7 @@
                 "       wm density [reset|DENSITY]\n" +
                 "       wm overscan [reset|LEFT,TOP,RIGHT,BOTTOM]\n" +
                 "\n" +
-                "wm size: override display size.\n" +
+                "wm size: return or override display size.\n" +
                 "\n" +
                 "wm density: override display density.\n" +
                 "\n" +
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index 313260f..241a64a 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -151,6 +151,7 @@
     public static final int ERROR_CODE_UNSUPPORTED_OPERATION = 6;
     public static final int ERROR_CODE_BAD_ARGUMENTS = 7;
     public static final int ERROR_CODE_BAD_REQUEST = 8;
+    public static final int ERROR_CODE_BAD_AUTHENTICATION = 9;
 
     /** @hide */
     public static final int ERROR_CODE_USER_RESTRICTED = 100;
@@ -387,6 +388,23 @@
     }
 
     /**
+     * @hide
+     * For use by internal activities. Returns the list of accounts that the calling package
+     * is authorized to use, particularly for shared accounts.
+     * @param packageName package name of the calling app.
+     * @param uid the uid of the calling app.
+     * @return the accounts that are available to this package and user.
+     */
+    public Account[] getAccountsForPackage(String packageName, int uid) {
+        try {
+            return mService.getAccountsForPackage(packageName, uid);
+        } catch (RemoteException re) {
+            // possible security exception
+            throw new RuntimeException(re);
+        }
+    }
+
+    /**
      * Lists all accounts of a particular type.  The account type is a
      * string token corresponding to the authenticator and useful domain
      * of the account.  For example, there are types corresponding to Google
@@ -574,7 +592,7 @@
     public boolean addAccountExplicitly(Account account, String password, Bundle userdata) {
         if (account == null) throw new IllegalArgumentException("account is null");
         try {
-            return mService.addAccount(account, password, userdata);
+            return mService.addAccountExplicitly(account, password, userdata);
         } catch (RemoteException e) {
             // won't ever happen
             throw new RuntimeException(e);
@@ -964,10 +982,10 @@
      */
     @Deprecated
     public AccountManagerFuture<Bundle> getAuthToken(
-            final Account account, final String authTokenType, 
+            final Account account, final String authTokenType,
             final boolean notifyAuthFailure,
             AccountManagerCallback<Bundle> callback, Handler handler) {
-        return getAuthToken(account, authTokenType, null, notifyAuthFailure, callback, 
+        return getAuthToken(account, authTokenType, null, notifyAuthFailure, callback,
                 handler);
     }
 
@@ -1122,7 +1140,7 @@
 
         return new AmsTask(activity, handler, callback) {
             public void doWork() throws RemoteException {
-                mService.addAcount(mResponse, accountType, authTokenType,
+                mService.addAccount(mResponse, accountType, authTokenType,
                         requiredFeatures, activity != null, optionsIn);
             }
         }.start();
@@ -1913,7 +1931,7 @@
      * <p>
      * The most common case is to call this with one account type, e.g.:
      * <p>
-     * <pre>  newChooseAccountsIntent(null, null, new String[]{"com.google"}, false, null,
+     * <pre>  newChooseAccountIntent(null, null, new String[]{"com.google"}, false, null,
      * null, null, null);</pre>
      * @param selectedAccount if specified, indicates that the {@link Account} is the currently
      * selected one, according to the caller's definition of selected.
diff --git a/core/java/android/accounts/ChooseTypeAndAccountActivity.java b/core/java/android/accounts/ChooseTypeAndAccountActivity.java
index 5358bc7..2aba163 100644
--- a/core/java/android/accounts/ChooseTypeAndAccountActivity.java
+++ b/core/java/android/accounts/ChooseTypeAndAccountActivity.java
@@ -18,9 +18,14 @@
 import com.google.android.collect.Sets;
 
 import android.app.Activity;
+import android.app.ActivityManagerNative;
 import android.content.Intent;
 import android.os.Bundle;
+import android.os.IBinder;
 import android.os.Parcelable;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.View;
@@ -29,6 +34,7 @@
 import android.widget.Button;
 import android.widget.ListView;
 import android.widget.TextView;
+import android.widget.Toast;
 
 import com.android.internal.R;
 
@@ -119,6 +125,9 @@
     private Parcelable[] mExistingAccounts = null;
     private int mSelectedItemIndex;
     private Button mOkButton;
+    private int mCallingUid;
+    private String mCallingPackage;
+    private boolean mDisallowAddAccounts;
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
@@ -128,6 +137,24 @@
                     + savedInstanceState + ")");
         }
 
+        String message = null;
+
+        try {
+            IBinder activityToken = getActivityToken();
+            mCallingUid = ActivityManagerNative.getDefault().getLaunchedFromUid(activityToken);
+            mCallingPackage = ActivityManagerNative.getDefault().getLaunchedFromPackage(
+                    activityToken);
+            if (mCallingUid != 0 && mCallingPackage != null) {
+                Bundle restrictions = UserManager.get(this)
+                        .getUserRestrictions(new UserHandle(UserHandle.getUserId(mCallingUid)));
+                mDisallowAddAccounts =
+                        restrictions.getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS, false);
+            }
+        } catch (RemoteException re) {
+            // Couldn't figure out caller details
+            Log.w(getClass().getSimpleName(), "Unable to get caller identity \n" + re);
+        }
+
         // save some items we use frequently
         final Intent intent = getIntent();
 
@@ -179,6 +206,11 @@
             // If there are no relevant accounts and only one relevant account type go directly to
             // add account. Otherwise let the user choose.
             if (mAccounts.isEmpty()) {
+                if (mDisallowAddAccounts) {
+                    setContentView(R.layout.app_not_authorized);
+                    setTitle(R.string.error_message_title);
+                    return;
+                }
                 if (mSetOfRelevantAccountTypes.size() == 1) {
                     runAddAccountForAuthenticator(mSetOfRelevantAccountTypes.iterator().next());
                 } else {
@@ -296,7 +328,8 @@
                 }
 
                 if (accountName == null || accountType == null) {
-                    Account[] currentAccounts = AccountManager.get(this).getAccounts();
+                    Account[] currentAccounts = AccountManager.get(this).getAccountsForPackage(
+                            mCallingPackage, mCallingUid);
                     Set<Account> preExistingAccounts = new HashSet<Account>();
                     for (Parcelable accountParcel : mExistingAccounts) {
                         preExistingAccounts.add((Account) accountParcel);
@@ -347,7 +380,8 @@
                     AccountManager.KEY_INTENT);
             if (intent != null) {
                 mPendingRequest = REQUEST_ADD_ACCOUNT;
-                mExistingAccounts = AccountManager.get(this).getAccounts();
+                mExistingAccounts = AccountManager.get(this).getAccountsForPackage(mCallingPackage,
+                        mCallingUid);
                 intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_NEW_TASK);
                 startActivityForResult(intent, REQUEST_ADD_ACCOUNT);
                 return;
@@ -424,12 +458,14 @@
     private String[] getListOfDisplayableOptions(ArrayList<Account> accounts) {
       // List of options includes all accounts found together with "Add new account" as the
       // last item in the list.
-      String[] listItems = new String[accounts.size() + 1];
+      String[] listItems = new String[accounts.size() + (mDisallowAddAccounts ? 0 : 1)];
       for (int i = 0; i < accounts.size(); i++) {
           listItems[i] = accounts.get(i).name;
       }
-      listItems[accounts.size()] = getResources().getString(
-              R.string.add_account_button_label);
+      if (!mDisallowAddAccounts) {
+          listItems[accounts.size()] = getResources().getString(
+                  R.string.add_account_button_label);
+      }
       return listItems;
     }
 
@@ -439,7 +475,8 @@
      * allowable accounts, if provided.
      */
     private ArrayList<Account> getAcceptableAccountChoices(AccountManager accountManager) {
-      final Account[] accounts = accountManager.getAccounts();
+      final Account[] accounts = accountManager.getAccountsForPackage(mCallingPackage,
+              mCallingUid);
       ArrayList<Account> accountsToPopulate = new ArrayList<Account>(accounts.length);
       for (Account account : accounts) {
           if (mSetOfAllowableAccounts != null
diff --git a/core/java/android/accounts/IAccountManager.aidl b/core/java/android/accounts/IAccountManager.aidl
index 47b257d..8141813 100644
--- a/core/java/android/accounts/IAccountManager.aidl
+++ b/core/java/android/accounts/IAccountManager.aidl
@@ -31,10 +31,11 @@
     String getUserData(in Account account, String key);
     AuthenticatorDescription[] getAuthenticatorTypes();
     Account[] getAccounts(String accountType);
+    Account[] getAccountsForPackage(String packageName, int uid);
     Account[] getAccountsAsUser(String accountType, int userId);
     void hasFeatures(in IAccountManagerResponse response, in Account account, in String[] features);
     void getAccountsByFeatures(in IAccountManagerResponse response, String accountType, in String[] features);
-    boolean addAccount(in Account account, String password, in Bundle extras);
+    boolean addAccountExplicitly(in Account account, String password, in Bundle extras);
     void removeAccount(in IAccountManagerResponse response, in Account account);
     void invalidateAuthToken(String accountType, String authToken);
     String peekAuthToken(in Account account, String authTokenType);
@@ -47,7 +48,7 @@
     void getAuthToken(in IAccountManagerResponse response, in Account account,
         String authTokenType, boolean notifyOnAuthFailure, boolean expectActivityLaunch,
         in Bundle options);
-    void addAcount(in IAccountManagerResponse response, String accountType,
+    void addAccount(in IAccountManagerResponse response, String accountType,
         String authTokenType, in String[] requiredFeatures, boolean expectActivityLaunch,
         in Bundle options);
     void updateCredentials(in IAccountManagerResponse response, in Account account,
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 944a533..bb9e19f 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -896,7 +896,7 @@
      * @param taskId The identifier of the task to be moved, as found in
      * {@link RunningTaskInfo} or {@link RecentTaskInfo}.
      * @param flags Additional operational flags, 0 or more of
-     * {@link #MOVE_TASK_WITH_HOME}.
+     * {@link #MOVE_TASK_WITH_HOME}, {@link #MOVE_TASK_NO_USER_ACTION}.
      */
     public void moveTaskToFront(int taskId, int flags) {
         moveTaskToFront(taskId, flags, null);
@@ -911,7 +911,7 @@
      * @param taskId The identifier of the task to be moved, as found in
      * {@link RunningTaskInfo} or {@link RecentTaskInfo}.
      * @param flags Additional operational flags, 0 or more of
-     * {@link #MOVE_TASK_WITH_HOME}.
+     * {@link #MOVE_TASK_WITH_HOME}, {@link #MOVE_TASK_NO_USER_ACTION}.
      * @param options Additional options for the operation, either null or
      * as per {@link Context#startActivity(Intent, android.os.Bundle)
      * Context.startActivity(Intent, Bundle)}.
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index bb73cf4..ae0671b 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -4533,6 +4533,8 @@
                                 + "snatched provider from the jaws of death");
                     }
                     prc.removePending = false;
+                    // There is a race! It fails to remove the message, which
+                    // will be handled in completeRemoveProvider().
                     mH.removeMessages(H.REMOVE_PROVIDER, prc);
                 } else {
                     unstableDelta = 0;
@@ -4712,6 +4714,11 @@
                 return;
             }
 
+            // More complicated race!! Some client managed to acquire the
+            // provider and release it before the removal was completed.
+            // Continue the removal, and abort the next remove message.
+            prc.removePending = false;
+
             final IBinder jBinder = prc.holder.provider.asBinder();
             ProviderRefCount existingPrc = mProviderRefCountMap.get(jBinder);
             if (existingPrc == prc) {
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 6d55dd5..271494f 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1279,7 +1279,8 @@
     public void setApplicationEnabledSetting(String packageName,
                                              int newState, int flags) {
         try {
-            mPM.setApplicationEnabledSetting(packageName, newState, flags, mContext.getUserId());
+            mPM.setApplicationEnabledSetting(packageName, newState, flags,
+                    mContext.getUserId(), mContext.getBasePackageName());
         } catch (RemoteException e) {
             // Should never happen!
         }
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 734d435..459e49c 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -19,7 +19,7 @@
 import com.android.internal.policy.PolicyManager;
 import com.android.internal.util.Preconditions;
 
-import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -319,7 +319,7 @@
 
         registerService(BLUETOOTH_SERVICE, new ServiceFetcher() {
                 public Object createService(ContextImpl ctx) {
-                    return BluetoothAdapter.getDefaultAdapter();
+                    return new BluetoothManager(ctx);
                 }});
 
         registerService(CLIPBOARD_SERVICE, new ServiceFetcher() {
diff --git a/core/java/android/app/DownloadManager.java b/core/java/android/app/DownloadManager.java
index 26dc60d..165c3db 100644
--- a/core/java/android/app/DownloadManager.java
+++ b/core/java/android/app/DownloadManager.java
@@ -461,39 +461,63 @@
         }
 
         /**
-         * Set the local destination for the downloaded file to a path within the application's
-         * external files directory (as returned by {@link Context#getExternalFilesDir(String)}.
+         * Set the local destination for the downloaded file to a path within
+         * the application's external files directory (as returned by
+         * {@link Context#getExternalFilesDir(String)}.
          * <p>
-         * The downloaded file is not scanned by MediaScanner.
-         * But it can be made scannable by calling {@link #allowScanningByMediaScanner()}.
+         * The downloaded file is not scanned by MediaScanner. But it can be
+         * made scannable by calling {@link #allowScanningByMediaScanner()}.
          *
-         * @param context the {@link Context} to use in determining the external files directory
-         * @param dirType the directory type to pass to {@link Context#getExternalFilesDir(String)}
-         * @param subPath the path within the external directory, including the destination filename
+         * @param context the {@link Context} to use in determining the external
+         *            files directory
+         * @param dirType the directory type to pass to
+         *            {@link Context#getExternalFilesDir(String)}
+         * @param subPath the path within the external directory, including the
+         *            destination filename
          * @return this object
+         * @throws IllegalStateException If the external storage directory
+         *             cannot be found or created.
          */
         public Request setDestinationInExternalFilesDir(Context context, String dirType,
                 String subPath) {
-            setDestinationFromBase(context.getExternalFilesDir(dirType), subPath);
+            final File file = context.getExternalFilesDir(dirType);
+            if (file == null) {
+                throw new IllegalStateException("Failed to get external storage files directory");
+            } else if (file.exists()) {
+                if (!file.isDirectory()) {
+                    throw new IllegalStateException(file.getAbsolutePath() +
+                            " already exists and is not a directory");
+                }
+            } else {
+                if (!file.mkdirs()) {
+                    throw new IllegalStateException("Unable to create directory: "+
+                            file.getAbsolutePath());
+                }
+            }
+            setDestinationFromBase(file, subPath);
             return this;
         }
 
         /**
-         * Set the local destination for the downloaded file to a path within the public external
-         * storage directory (as returned by
-         * {@link Environment#getExternalStoragePublicDirectory(String)}.
-         *<p>
-         * The downloaded file is not scanned by MediaScanner.
-         * But it can be made scannable by calling {@link #allowScanningByMediaScanner()}.
+         * Set the local destination for the downloaded file to a path within
+         * the public external storage directory (as returned by
+         * {@link Environment#getExternalStoragePublicDirectory(String)}).
+         * <p>
+         * The downloaded file is not scanned by MediaScanner. But it can be
+         * made scannable by calling {@link #allowScanningByMediaScanner()}.
          *
-         * @param dirType the directory type to pass to
-         *        {@link Environment#getExternalStoragePublicDirectory(String)}
-         * @param subPath the path within the external directory, including the destination filename
+         * @param dirType the directory type to pass to {@link Environment#getExternalStoragePublicDirectory(String)}
+         * @param subPath the path within the external directory, including the
+         *            destination filename
          * @return this object
+         * @throws IllegalStateException If the external storage directory
+         *             cannot be found or created.
          */
         public Request setDestinationInExternalPublicDir(String dirType, String subPath) {
             File file = Environment.getExternalStoragePublicDirectory(dirType);
-            if (file.exists()) {
+            if (file == null) {
+                throw new IllegalStateException("Failed to get external storage public directory");
+            } else if (file.exists()) {
                 if (!file.isDirectory()) {
                     throw new IllegalStateException(file.getAbsolutePath() +
                             " already exists and is not a directory");
diff --git a/core/java/android/app/NativeActivity.java b/core/java/android/app/NativeActivity.java
index 396f910..51867bc 100644
--- a/core/java/android/app/NativeActivity.java
+++ b/core/java/android/app/NativeActivity.java
@@ -353,8 +353,7 @@
     }
     
     void preDispatchKeyEvent(KeyEvent event, int seq) {
-        mIMM.dispatchKeyEvent(this, seq, event,
-                mInputMethodCallback);
+        mIMM.dispatchInputEvent(this, seq, event, mInputMethodCallback);
     }
 
     void setWindowFlags(int flags, int mask) {
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index 02cf3aa..4fbca73 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -666,7 +666,9 @@
     
     /**
      * Print the Service's state into the given stream.  This gets invoked if
-     * you run "adb shell dumpsys activity service &lt;yourservicename&gt;".
+     * you run "adb shell dumpsys activity service &lt;yourservicename&gt;"
+     * (note that for this command to work, the service must be running, and
+     * you must specify a fully-qualified service name).
      * This is distinct from "dumpsys &lt;servicename&gt;", which only works for
      * named system services and which invokes the {@link IBinder#dump} method
      * on the {@link IBinder} interface registered with ServiceManager.
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 4c0eba0..8284b2c 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1513,4 +1513,57 @@
             }
         }
     }
+
+    /**
+     * @hide
+     * Sets the given package as the device owner. The package must already be installed and there
+     * shouldn't be an existing device owner registered, for this call to succeed. Also, this
+     * method must be called before the device is provisioned.
+     * @param packageName the package name of the application to be registered as the device owner.
+     * @return whether the package was successfully registered as the device owner.
+     * @throws IllegalArgumentException if the package name is null or invalid
+     * @throws IllegalStateException if a device owner is already registered or the device has
+     *         already been provisioned.
+     */
+    public boolean setDeviceOwner(String packageName) throws IllegalArgumentException,
+            IllegalStateException {
+        if (mService != null) {
+            try {
+                return mService.setDeviceOwner(packageName);
+            } catch (RemoteException re) {
+                Log.w(TAG, "Failed to set device owner");
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Used to determine if a particular package has been registered as a Device Owner admin.
+     * Device Owner admins cannot be deactivated by the user unless the Device Owner itself allows
+     * it. And Device Owner packages cannot be uninstalled, once registered.
+     * @param packageName the package name to check against the registered device owner.
+     * @return whether or not the package is registered as the Device Owner.
+     */
+    public boolean isDeviceOwner(String packageName) {
+        if (mService != null) {
+            try {
+                return mService.isDeviceOwner(packageName);
+            } catch (RemoteException re) {
+                Log.w(TAG, "Failed to check device owner");
+            }
+        }
+        return false;
+    }
+
+    /** @hide */
+    public String getDeviceOwner() {
+        if (mService != null) {
+            try {
+                return mService.getDeviceOwner();
+            } catch (RemoteException re) {
+                Log.w(TAG, "Failed to get device owner");
+            }
+        }
+        return null;
+    }
 }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index e061ab3..b2a65bf 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -97,4 +97,8 @@
         int numbers, int symbols, int nonletter, int userHandle);
     void reportFailedPasswordAttempt(int userHandle);
     void reportSuccessfulPasswordAttempt(int userHandle);
+
+    boolean setDeviceOwner(String packageName);
+    boolean isDeviceOwner(String packageName);
+    String getDeviceOwner();
 }
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 3425765..37fddcb 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -477,21 +477,31 @@
             }
         } else {
             // Not a supported location
-            Log.i(TAG, "Data restored from non-app domain " + domain + ", ignoring");
+            Log.i(TAG, "Unrecognized domain " + domain);
         }
 
         // Now that we've figured out where the data goes, send it on its way
         if (basePath != null) {
+            // Canonicalize the nominal path and verify that it lies within the stated domain
             File outFile = new File(basePath, path);
-            if (DEBUG) Log.i(TAG, "[" + domain + " : " + path + "] mapped to " + outFile.getPath());
-            onRestoreFile(data, size, outFile, type, mode, mtime);
-        } else {
-            // Not a supported output location?  We need to consume the data
-            // anyway, so just use the default "copy the data out" implementation
-            // with a null destination.
-            if (DEBUG) Log.i(TAG, "[ skipping data from unsupported domain " + domain + "]");
-            FullBackup.restoreFile(data, size, type, mode, mtime, null);
+            String outPath = outFile.getCanonicalPath();
+            if (outPath.startsWith(basePath + File.separatorChar)) {
+                if (DEBUG) Log.i(TAG, "[" + domain + " : " + path + "] mapped to " + outPath);
+                onRestoreFile(data, size, outFile, type, mode, mtime);
+                return;
+            } else {
+                // Attempt to restore to a path outside the file's nominal domain.
+                if (DEBUG) {
+                    Log.e(TAG, "Cross-domain restore attempt: " + outPath);
+                }
+            }
         }
+
+        // Not a supported output location, or bad path:  we need to consume the data
+        // anyway, so just use the default "copy the data out" implementation
+        // with a null destination.
+        if (DEBUG) Log.i(TAG, "[ skipping file " + path + "]");
+        FullBackup.restoreFile(data, size, type, mode, mtime, null);
     }
 
     // ----- Core implementation -----
diff --git a/core/java/android/appwidget/AppWidgetHost.java b/core/java/android/appwidget/AppWidgetHost.java
index f7933d3..c62bf32 100644
--- a/core/java/android/appwidget/AppWidgetHost.java
+++ b/core/java/android/appwidget/AppWidgetHost.java
@@ -338,8 +338,8 @@
      */
     public final AppWidgetHostView createView(Context context, int appWidgetId,
             AppWidgetProviderInfo appWidget) {
-        final int userId = context.getUserId();
-        AppWidgetHostView view = onCreateView(context, appWidgetId, appWidget);
+        final int userId = mContext.getUserId();
+        AppWidgetHostView view = onCreateView(mContext, appWidgetId, appWidget);
         view.setUserId(userId);
         view.setOnClickHandler(mOnClickHandler);
         view.setAppWidget(appWidgetId, appWidget);
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index e68d23a..1166e4b 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -49,8 +49,8 @@
     static final String TAG = "AppWidgetManager";
 
     /**
-     * Send this from your {@link AppWidgetHost} activity when you want to pick an AppWidget to display.
-     * The AppWidget picker activity will be launched.
+     * Activity action to launch from your {@link AppWidgetHost} activity when you want to
+     * pick an AppWidget to display.  The AppWidget picker activity will be launched.
      * <p>
      * You must supply the following extras:
      * <table>
@@ -89,8 +89,8 @@
             ACTION_KEYGUARD_APPWIDGET_PICK = "android.appwidget.action.KEYGUARD_APPWIDGET_PICK";
 
     /**
-     * Send this from your {@link AppWidgetHost} activity when you want to bind an AppWidget to
-     * display and bindAppWidgetIdIfAllowed returns false.
+     * Activity action to launch from your {@link AppWidgetHost} activity when you want to bind
+     * an AppWidget to display and bindAppWidgetIdIfAllowed returns false.
      * <p>
      * You must supply the following extras:
      * <table>
@@ -269,6 +269,9 @@
     /**
      * Sent when the custom extras for an AppWidget change.
      *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
+     *
      * @see AppWidgetProvider#onAppWidgetOptionsChanged
      *      AppWidgetProvider.onAppWidgetOptionsChanged(Context context,
      *      AppWidgetManager appWidgetManager, int appWidgetId, Bundle newExtras)
@@ -278,6 +281,9 @@
     /**
      * Sent when an instance of an AppWidget is deleted from its host.
      *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
+     *
      * @see AppWidgetProvider#onDeleted AppWidgetProvider.onDeleted(Context context, int[] appWidgetIds)
      */
     public static final String ACTION_APPWIDGET_DELETED = "android.appwidget.action.APPWIDGET_DELETED";
@@ -285,6 +291,9 @@
     /**
      * Sent when an instance of an AppWidget is removed from the last host.
      *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
+     *
      * @see AppWidgetProvider#onEnabled AppWidgetProvider.onEnabled(Context context)
      */
     public static final String ACTION_APPWIDGET_DISABLED = "android.appwidget.action.APPWIDGET_DISABLED";
@@ -294,6 +303,9 @@
      * This broadcast is sent at boot time if there is a AppWidgetHost installed with
      * an instance for this provider.
      *
+     * <p class="note">This is a protected intent that can only be sent
+     * by the system.
+     *
      * @see AppWidgetProvider#onEnabled AppWidgetProvider.onEnabled(Context context)
      */
     public static final String ACTION_APPWIDGET_ENABLED = "android.appwidget.action.APPWIDGET_ENABLED";
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
old mode 100755
new mode 100644
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index b00bf09..2e9c9e3 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -22,7 +22,6 @@
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.Looper;
 import android.os.Message;
 import android.os.ParcelUuid;
 import android.os.RemoteException;
@@ -359,6 +358,8 @@
     private IBluetooth mService;
 
     private Handler mServiceRecordHandler;
+    private BluetoothAdapterCallback mCallback;
+    private int mClientIf;
 
     /**
      * Get a handle to the default local Bluetooth adapter.
@@ -1137,7 +1138,8 @@
      * Get the profile proxy object associated with the profile.
      *
      * <p>Profile can be one of {@link BluetoothProfile#HEALTH}, {@link BluetoothProfile#HEADSET},
-     * or {@link BluetoothProfile#A2DP}. Clients must implement
+     * {@link BluetoothProfile#A2DP}, {@link BluetoothProfile#GATT}, or
+     * {@link BluetoothProfile#GATT_SERVER}. Clients must implement
      * {@link BluetoothProfile.ServiceListener} to get notified of
      * the connection status and to get the proxy object.
      *
@@ -1166,12 +1168,6 @@
         } else if (profile == BluetoothProfile.HEALTH) {
             BluetoothHealth health = new BluetoothHealth(context, listener);
             return true;
-        } else if (profile == BluetoothProfile.GATT) {
-            BluetoothGatt gatt = new BluetoothGatt(context, listener);
-            return true;
-        } else if (profile == BluetoothProfile.GATT_SERVER) {
-            BluetoothGattServer gattServer = new BluetoothGattServer(context, listener);
-            return true;
         } else {
             return false;
         }
@@ -1411,4 +1407,230 @@
             mProxyServiceStateCallbacks.remove(cb);
         }
     }
+
+    /**
+     * Register an callback to receive async results, such as LE scan result.
+     *
+     * <p>This is an asynchronous call. The callback
+     * {@link BluetoothAdapterCallback#onCallbackRegistration}
+     * is used to notify success or failure if the function returns true.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+     *
+     * @param callback BluetootAdapter callback handler that will receive asynchronous callbacks.
+     * @return If true, the callback will be called to notify success or failure,
+     *         false on immediate error
+     */
+    public boolean registerCallback(BluetoothAdapterCallback callback) {
+        try {
+            IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt();
+            mCallback = callback;
+            UUID uuid = UUID.randomUUID();
+            if (DBG) Log.d(TAG, "registerCallback() - UUID=" + uuid);
+
+            iGatt.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback);
+            return true;
+        } catch (RemoteException e) {
+            Log.e(TAG,"",e);
+            return false;
+        }
+    }
+
+    /**
+     * Unregister the registered callback.
+     */
+    public boolean unRegisterCallback(BluetoothAdapterCallback callback) {
+        if (callback != mCallback) return false;
+        try {
+            IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt();
+
+            iGatt.unregisterClient(mClientIf);
+            return true;
+        } catch (RemoteException e) {
+            Log.e(TAG,"",e);
+            return false;
+        }
+    }
+
+    /**
+     * Starts a scan for Bluetooth LE devices.
+     *
+     * <p>Results of the scan are reported using the
+     * {@link BluetoothAdapterCallback#onLeScan} callback.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+     *
+     * @return true, if the scan was started successfully
+     */
+    public boolean startLeScan() {
+        if (DBG) Log.d(TAG, "startLeScan()");
+        if (mClientIf == 0) return false;
+
+        try {
+            IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt();
+            iGatt.startScan(mClientIf, false);
+        } catch (RemoteException e) {
+            Log.e(TAG,"",e);
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Starts a scan for Bluetooth LE devices, looking for devices that
+     * advertise given services.
+     *
+     * <p>Devices which advertise all specified services are reported using the
+     * {@link BluetoothAdapterCallback#onLeScan} callback.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+     *
+     * @param serviceUuids Array of services to look for
+     * @return true, if the scan was started successfully
+     */
+    public boolean startLeScan(UUID[] serviceUuids) {
+        if (DBG) Log.d(TAG, "startLeScan() - with UUIDs");
+        if (mClientIf == 0) return false;
+
+        try {
+            IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt();
+            ParcelUuid[] uuids = new ParcelUuid[serviceUuids.length];
+            for(int i = 0; i != uuids.length; ++i) {
+                uuids[i] = new ParcelUuid(serviceUuids[i]);
+            }
+            iGatt.startScanWithUuids(mClientIf, false, uuids);
+        } catch (RemoteException e) {
+            Log.e(TAG,"",e);
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Stops an ongoing Bluetooth LE device scan.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+     */
+    public void stopLeScan() {
+        if (DBG) Log.d(TAG, "stopScan()");
+        if (mClientIf == 0) return;
+
+        try {
+            IBluetoothGatt iGatt = (IBluetoothGatt) mManagerService.getBluetoothGatt();
+            iGatt.stopScan(mClientIf, false);
+        } catch (RemoteException e) {
+            Log.e(TAG,"",e);
+        }
+    }
+
+    /**
+     * Bluetooth GATT interface callbacks
+     */
+    private final IBluetoothGattCallback mBluetoothGattCallback =
+        new IBluetoothGattCallback.Stub() {
+            /**
+             * Application interface registered - app is ready to go
+             */
+            public void onClientRegistered(int status, int clientIf) {
+                if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status
+                    + " clientIf=" + clientIf);
+                mClientIf = clientIf;
+                mCallback.onCallbackRegistration(status == BluetoothGatt.GATT_SUCCESS ?
+                                  BluetoothAdapterCallback.CALLBACK_REGISTERED :
+                                  BluetoothAdapterCallback.CALLBACK_REGISTRATION_FAILURE);
+            }
+
+            public void onClientConnectionState(int status, int clientIf,
+                                                boolean connected, String address) {
+                // no op
+            }
+
+            /**
+             * Callback reporting an LE scan result.
+             * @hide
+             */
+            public void onScanResult(String address, int rssi, byte[] advData) {
+                if (DBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi);
+
+                try {
+                    mCallback.onLeScan(getRemoteDevice(address), rssi, advData);
+                } catch (Exception ex) {
+                    Log.w(TAG, "Unhandled exception: " + ex);
+                }
+            }
+
+            public void onGetService(String address, int srvcType,
+                                     int srvcInstId, ParcelUuid srvcUuid) {
+                // no op
+            }
+
+            public void onGetIncludedService(String address, int srvcType,
+                                             int srvcInstId, ParcelUuid srvcUuid,
+                                             int inclSrvcType, int inclSrvcInstId,
+                                             ParcelUuid inclSrvcUuid) {
+                // no op
+            }
+
+            public void onGetCharacteristic(String address, int srvcType,
+                             int srvcInstId, ParcelUuid srvcUuid,
+                             int charInstId, ParcelUuid charUuid,
+                             int charProps) {
+                // no op
+            }
+
+            public void onGetDescriptor(String address, int srvcType,
+                             int srvcInstId, ParcelUuid srvcUuid,
+                             int charInstId, ParcelUuid charUuid,
+                             ParcelUuid descUuid) {
+                // no op
+            }
+
+            public void onSearchComplete(String address, int status) {
+                // no op
+            }
+
+            public void onCharacteristicRead(String address, int status, int srvcType,
+                             int srvcInstId, ParcelUuid srvcUuid,
+                             int charInstId, ParcelUuid charUuid, byte[] value) {
+                // no op
+            }
+
+            public void onCharacteristicWrite(String address, int status, int srvcType,
+                             int srvcInstId, ParcelUuid srvcUuid,
+                             int charInstId, ParcelUuid charUuid) {
+                // no op
+            }
+
+            public void onNotify(String address, int srvcType,
+                             int srvcInstId, ParcelUuid srvcUuid,
+                             int charInstId, ParcelUuid charUuid,
+                             byte[] value) {
+                // no op
+            }
+
+            public void onDescriptorRead(String address, int status, int srvcType,
+                             int srvcInstId, ParcelUuid srvcUuid,
+                             int charInstId, ParcelUuid charUuid,
+                             ParcelUuid descrUuid, byte[] value) {
+                // no op
+            }
+
+            public void onDescriptorWrite(String address, int status, int srvcType,
+                             int srvcInstId, ParcelUuid srvcUuid,
+                             int charInstId, ParcelUuid charUuid,
+                             ParcelUuid descrUuid) {
+                // no op
+            }
+
+            public void onExecuteWrite(String address, int status) {
+                // no op
+            }
+
+            public void onReadRemoteRssi(String address, int rssi, int status) {
+                // no op
+            }
+        };
+
 }
diff --git a/core/java/android/bluetooth/BluetoothAdapterCallback.java b/core/java/android/bluetooth/BluetoothAdapterCallback.java
new file mode 100644
index 0000000..a726bc9
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothAdapterCallback.java
@@ -0,0 +1,57 @@
+/*
+ * 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.bluetooth;
+
+import android.bluetooth.BluetoothDevice;
+
+/**
+ * This abstract class is used to implement {@link BluetoothAdapter} callbacks.
+ */
+public abstract class BluetoothAdapterCallback {
+
+    /**
+     * Indicates the callback has been registered successfully
+     */
+    public static final int CALLBACK_REGISTERED = 0;
+
+    /**
+     * Indicates the callback registration has failed
+     */
+    public static final int CALLBACK_REGISTRATION_FAILURE = 1;
+
+    /**
+     * Callback to inform change in registration state of the  application.
+     *
+     * @param status Returns {@link #CALLBACK_REGISTERED} if the application
+     *               was successfully registered.
+     */
+    public void onCallbackRegistration(int status) {
+    }
+
+    /**
+     * Callback reporting an LE device found during a device scan initiated
+     * by the {@link BluetoothAdapter#startLeScan} function.
+     *
+     * @param device Identifies the remote device
+     * @param rssi The RSSI value for the remote device as reported by the
+     *             Bluetooth hardware. 0 if no RSSI value is available.
+     * @param scanRecord The content of the advertisement record offered by
+     *                   the remote device.
+     */
+    public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
+    }
+}
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
old mode 100755
new mode 100644
index 4cc22b4..83e95ca
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -18,6 +18,7 @@
 
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.content.Context;
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -1126,4 +1127,30 @@
         return pinBytes;
     }
 
+    /**
+     * Connect to GATT Server hosted by this device. Caller acts as GATT client.
+     * The callback is used to deliver results to Caller, such as connection status as well
+     * as any further GATT client operations.
+     * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct
+     * GATT client operations.
+     * @param callback GATT callback handler that will receive asynchronous callbacks.
+     * @param autoConnect Whether to directly connect to the remote device (false)
+     *                    or to automatically connect as soon as the remote
+     *                    device becomes available (true).
+     * @throws IllegalArgumentException if callback is null
+     */
+    public BluetoothGatt connectGattServer(Context context, boolean autoConnect,
+                                           BluetoothGattCallback callback) {
+        // TODO(Bluetooth) check whether platform support BLE
+        //     Do the check here or in GattServer?
+        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+        IBluetoothManager managerService = adapter.getBluetoothManager();
+        try {
+            IBluetoothGatt iGatt = managerService.getBluetoothGatt();
+            BluetoothGatt gatt = new BluetoothGatt(context, iGatt, this);
+            gatt.connect(autoConnect, callback);
+            return gatt;
+        } catch (RemoteException e) {Log.e(TAG, "", e);}
+        return null;
+    }
 }
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index 1e12025..f9ce6ea 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -16,8 +16,6 @@
 
 package android.bluetooth;
 
-
-import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
 import android.bluetooth.BluetoothProfile;
 import android.bluetooth.BluetoothProfile.ServiceListener;
@@ -39,42 +37,48 @@
 import java.util.UUID;
 
 /**
- * Public API for the Bluetooth Gatt Profile.
+ * Public API for the Bluetooth GATT Profile.
  *
- * <p>This class provides Bluetooth Gatt functionality to enable communication
+ * <p>This class provides Bluetooth GATT functionality to enable communication
  * with Bluetooth Smart or Smart Ready devices.
  *
- * <p>BluetoothGatt is a proxy object for controlling the Bluetooth Service
- * via IPC.  Use {@link BluetoothAdapter#getProfileProxy} to get the
- * BluetoothGatt proxy object.
- *
  * <p>To connect to a remote peripheral device, create a {@link BluetoothGattCallback}
- * and call {@link #registerApp} to register your application. Gatt capable
- * devices can be discovered using the {@link #startScan} function or the
- * regular Bluetooth device discovery process.
- * @hide
+ * and call {@link BluetoothDevice#connectGattServer} to get a instance of this class.
+ * GATT capable devices can be discovered using the Bluetooth device discovery or BLE
+ * scan process.
  */
 public final class BluetoothGatt implements BluetoothProfile {
     private static final String TAG = "BluetoothGatt";
     private static final boolean DBG = true;
+    private static final boolean VDBG = true;
 
-    private Context mContext;
-    private ServiceListener mServiceListener;
-    private BluetoothAdapter mAdapter;
+    private final Context mContext;
     private IBluetoothGatt mService;
     private BluetoothGattCallback mCallback;
     private int mClientIf;
     private boolean mAuthRetry = false;
+    private BluetoothDevice mDevice;
+    private boolean mAutoConnect;
+    private int mConnState;
+    private final Object mStateLock = new Object();
+
+    private static final int CONN_STATE_IDLE = 0;
+    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 List<BluetoothGattService> mServices;
 
-    /** A Gatt operation completed successfully */
+    /** A GATT operation failed */
+    public static final int GATT_FAILURE = 0;
+
+    /** A GATT operation completed successfully */
     public static final int GATT_SUCCESS = 0;
 
-    /** Gatt read operation is not permitted */
+    /** GATT read operation is not permitted */
     public static final int GATT_READ_NOT_PERMITTED = 0x2;
 
-    /** Gatt write operation is not permitted */
+    /** GATT write operation is not permitted */
     public static final int GATT_WRITE_NOT_PERMITTED = 0x3;
 
     /** Insufficient authentication for a given operation */
@@ -111,55 +115,6 @@
     /*package*/ static final int AUTHENTICATION_MITM = 2;
 
     /**
-     * Bluetooth state change handlers
-     */
-    private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
-        new IBluetoothStateChangeCallback.Stub() {
-            public void onBluetoothStateChange(boolean up) {
-                if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
-                if (!up) {
-                    if (DBG) Log.d(TAG,"Unbinding service...");
-                    synchronized (mConnection) {
-                        mService = null;
-                        mContext.unbindService(mConnection);
-                    }
-                } else {
-                    synchronized (mConnection) {
-                        if (mService == null) {
-                            if (DBG) Log.d(TAG,"Binding service...");
-                            if (!mContext.bindService(new Intent(IBluetoothGatt.class.getName()),
-                                                      mConnection, 0)) {
-                                Log.e(TAG, "Could not bind to Bluetooth GATT Service");
-                            }
-                        }
-                    }
-                }
-            }
-        };
-
-    /**
-     * Service binder handling
-     */
-    private ServiceConnection mConnection = new ServiceConnection() {
-            public void onServiceConnected(ComponentName className, IBinder service) {
-                if (DBG) Log.d(TAG, "Proxy object connected");
-                mService = IBluetoothGatt.Stub.asInterface(service);
-                ServiceListener serviceListener = mServiceListener;
-                if (serviceListener != null) {
-                    serviceListener.onServiceConnected(BluetoothProfile.GATT, BluetoothGatt.this);
-                }
-            }
-            public void onServiceDisconnected(ComponentName className) {
-                if (DBG) Log.d(TAG, "Proxy object disconnected");
-                mService = null;
-                ServiceListener serviceListener = mServiceListener;
-                if (serviceListener != null) {
-                    serviceListener.onServiceDisconnected(BluetoothProfile.GATT);
-                }
-            }
-        };
-
-    /**
      * Bluetooth GATT interface callbacks
      */
     private final IBluetoothGattCallback mBluetoothGattCallback =
@@ -171,11 +126,27 @@
             public void onClientRegistered(int status, int clientIf) {
                 if (DBG) Log.d(TAG, "onClientRegistered() - status=" + status
                     + " clientIf=" + clientIf);
+                if (VDBG) {
+                    synchronized(mStateLock) {
+                        if (mConnState != CONN_STATE_CONNECTING) {
+                            Log.e(TAG, "Bad connection state: " + mConnState);
+                        }
+                    }
+                }
                 mClientIf = clientIf;
+                if (status != GATT_SUCCESS) {
+                    mCallback.onConnectionStateChange(mDevice, GATT_FAILURE,
+                                                      BluetoothProfile.STATE_DISCONNECTED);
+                    synchronized(mStateLock) {
+                        mConnState = CONN_STATE_IDLE;
+                    }
+                    return;
+                }
                 try {
-                    mCallback.onAppRegistered(status);
-                } catch (Exception ex) {
-                    Log.w(TAG, "Unhandled exception: " + ex);
+                    mService.clientConnect(mClientIf, mDevice.getAddress(),
+                                           !mAutoConnect); // autoConnect is inverse of "isDirect"
+                } catch (RemoteException e) {
+                    Log.e(TAG,"",e);
                 }
             }
 
@@ -187,13 +158,24 @@
                                                 boolean connected, String address) {
                 if (DBG) Log.d(TAG, "onClientConnectionState() - status=" + status
                                  + " clientIf=" + clientIf + " device=" + address);
+                if (!address.equals(mDevice.getAddress())) {
+                    return;
+                }
+                int profileState = connected ? BluetoothProfile.STATE_CONNECTED :
+                                               BluetoothProfile.STATE_DISCONNECTED;
                 try {
-                    mCallback.onConnectionStateChange(mAdapter.getRemoteDevice(address), status,
-                                                      connected ? BluetoothProfile.STATE_CONNECTED
-                                                      : BluetoothProfile.STATE_DISCONNECTED);
+                    mCallback.onConnectionStateChange(mDevice, status, profileState);
                 } catch (Exception ex) {
                     Log.w(TAG, "Unhandled exception: " + ex);
                 }
+
+                synchronized(mStateLock) {
+                    if (connected) {
+                        mConnState = CONN_STATE_CONNECTED;
+                    } else {
+                        mConnState = CONN_STATE_IDLE;
+                    }
+                }
             }
 
             /**
@@ -201,13 +183,7 @@
              * @hide
              */
             public void onScanResult(String address, int rssi, byte[] advData) {
-                if (DBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi);
-
-                try {
-                    mCallback.onScanResult(mAdapter.getRemoteDevice(address), rssi, advData);
-                } catch (Exception ex) {
-                    Log.w(TAG, "Unhandled exception: " + ex);
-                }
+                // no op
             }
 
             /**
@@ -219,8 +195,10 @@
             public void onGetService(String address, int srvcType,
                                      int srvcInstId, ParcelUuid srvcUuid) {
                 if (DBG) Log.d(TAG, "onGetService() - Device=" + address + " UUID=" + srvcUuid);
-                BluetoothDevice device = mAdapter.getRemoteDevice(address);
-                mServices.add(new BluetoothGattService(device, srvcUuid.getUuid(),
+                if (!address.equals(mDevice.getAddress())) {
+                    return;
+                }
+                mServices.add(new BluetoothGattService(mDevice, srvcUuid.getUuid(),
                                                        srvcInstId, srvcType));
             }
 
@@ -236,10 +214,12 @@
                 if (DBG) Log.d(TAG, "onGetIncludedService() - Device=" + address
                     + " UUID=" + srvcUuid + " Included=" + inclSrvcUuid);
 
-                BluetoothDevice device = mAdapter.getRemoteDevice(address);
-                BluetoothGattService service = getService(device,
+                if (!address.equals(mDevice.getAddress())) {
+                    return;
+                }
+                BluetoothGattService service = getService(mDevice,
                         srvcUuid.getUuid(), srvcInstId, srvcType);
-                BluetoothGattService includedService = getService(device,
+                BluetoothGattService includedService = getService(mDevice,
                         inclSrvcUuid.getUuid(), inclSrvcInstId, inclSrvcType);
 
                 if (service != null && includedService != null) {
@@ -260,8 +240,10 @@
                 if (DBG) Log.d(TAG, "onGetCharacteristic() - Device=" + address + " UUID=" +
                                charUuid);
 
-                BluetoothDevice device = mAdapter.getRemoteDevice(address);
-                BluetoothGattService service = getService(device, srvcUuid.getUuid(),
+                if (!address.equals(mDevice.getAddress())) {
+                    return;
+                }
+                BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
                                                           srvcInstId, srvcType);
                 if (service != null) {
                     service.addCharacteristic(new BluetoothGattCharacteristic(
@@ -281,8 +263,10 @@
                              ParcelUuid descUuid) {
                 if (DBG) Log.d(TAG, "onGetDescriptor() - Device=" + address + " UUID=" + descUuid);
 
-                BluetoothDevice device = mAdapter.getRemoteDevice(address);
-                BluetoothGattService service = getService(device, srvcUuid.getUuid(),
+                if (!address.equals(mDevice.getAddress())) {
+                    return;
+                }
+                BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
                                                           srvcInstId, srvcType);
                 if (service == null) return;
 
@@ -303,9 +287,11 @@
              */
             public void onSearchComplete(String address, int status) {
                 if (DBG) Log.d(TAG, "onSearchComplete() = Device=" + address + " Status=" + status);
-                BluetoothDevice device = mAdapter.getRemoteDevice(address);
+                if (!address.equals(mDevice.getAddress())) {
+                    return;
+                }
                 try {
-                    mCallback.onServicesDiscovered(device, status);
+                    mCallback.onServicesDiscovered(mDevice, status);
                 } catch (Exception ex) {
                     Log.w(TAG, "Unhandled exception: " + ex);
                 }
@@ -322,6 +308,9 @@
                 if (DBG) Log.d(TAG, "onCharacteristicRead() - Device=" + address
                             + " UUID=" + charUuid + " Status=" + status);
 
+                if (!address.equals(mDevice.getAddress())) {
+                    return;
+                }
                 if ((status == GATT_INSUFFICIENT_AUTHENTICATION
                   || status == GATT_INSUFFICIENT_ENCRYPTION)
                   && mAuthRetry == false) {
@@ -338,8 +327,7 @@
 
                 mAuthRetry = false;
 
-                BluetoothDevice device = mAdapter.getRemoteDevice(address);
-                BluetoothGattService service = getService(device, srvcUuid.getUuid(),
+                BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
                                                           srvcInstId, srvcType);
                 if (service == null) return;
 
@@ -367,8 +355,10 @@
                 if (DBG) Log.d(TAG, "onCharacteristicWrite() - Device=" + address
                             + " UUID=" + charUuid + " Status=" + status);
 
-                BluetoothDevice device = mAdapter.getRemoteDevice(address);
-                BluetoothGattService service = getService(device, srvcUuid.getUuid(),
+                if (!address.equals(mDevice.getAddress())) {
+                    return;
+                }
+                BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
                                                           srvcInstId, srvcType);
                 if (service == null) return;
 
@@ -411,8 +401,10 @@
                              byte[] value) {
                 if (DBG) Log.d(TAG, "onNotify() - Device=" + address + " UUID=" + charUuid);
 
-                BluetoothDevice device = mAdapter.getRemoteDevice(address);
-                BluetoothGattService service = getService(device, srvcUuid.getUuid(),
+                if (!address.equals(mDevice.getAddress())) {
+                    return;
+                }
+                BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
                                                           srvcInstId, srvcType);
                 if (service == null) return;
 
@@ -439,8 +431,10 @@
                              ParcelUuid descrUuid, byte[] value) {
                 if (DBG) Log.d(TAG, "onDescriptorRead() - Device=" + address + " UUID=" + charUuid);
 
-                BluetoothDevice device = mAdapter.getRemoteDevice(address);
-                BluetoothGattService service = getService(device, srvcUuid.getUuid(),
+                if (!address.equals(mDevice.getAddress())) {
+                    return;
+                }
+                BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
                                                           srvcInstId, srvcType);
                 if (service == null) return;
 
@@ -486,8 +480,10 @@
                              ParcelUuid descrUuid) {
                 if (DBG) Log.d(TAG, "onDescriptorWrite() - Device=" + address + " UUID=" + charUuid);
 
-                BluetoothDevice device = mAdapter.getRemoteDevice(address);
-                BluetoothGattService service = getService(device, srvcUuid.getUuid(),
+                if (!address.equals(mDevice.getAddress())) {
+                    return;
+                }
+                BluetoothGattService service = getService(mDevice, srvcUuid.getUuid(),
                                                           srvcInstId, srvcType);
                 if (service == null) return;
 
@@ -529,9 +525,11 @@
             public void onExecuteWrite(String address, int status) {
                 if (DBG) Log.d(TAG, "onExecuteWrite() - Device=" + address
                     + " status=" + status);
-                BluetoothDevice device = mAdapter.getRemoteDevice(address);
+                if (!address.equals(mDevice.getAddress())) {
+                    return;
+                }
                 try {
-                    mCallback.onReliableWriteCompleted(device, status);
+                    mCallback.onReliableWriteCompleted(mDevice, status);
                 } catch (Exception ex) {
                     Log.w(TAG, "Unhandled exception: " + ex);
                 }
@@ -544,43 +542,24 @@
             public void onReadRemoteRssi(String address, int rssi, int status) {
                 if (DBG) Log.d(TAG, "onReadRemoteRssi() - Device=" + address +
                             " rssi=" + rssi + " status=" + status);
-                BluetoothDevice device = mAdapter.getRemoteDevice(address);
+                if (!address.equals(mDevice.getAddress())) {
+                    return;
+                }
                 try {
-                    mCallback.onReadRemoteRssi(device, rssi, status);
+                    mCallback.onReadRemoteRssi(mDevice, rssi, status);
                 } catch (Exception ex) {
                     Log.w(TAG, "Unhandled exception: " + ex);
                 }
             }
         };
 
-    /**
-     * Create a BluetoothGatt proxy object.
-     */
-    /*package*/ BluetoothGatt(Context context, ServiceListener l) {
+    /*package*/ BluetoothGatt(Context context, IBluetoothGatt iGatt, BluetoothDevice device) {
         mContext = context;
-        mServiceListener = l;
-        mAdapter = BluetoothAdapter.getDefaultAdapter();
+        mService = iGatt;
+        mDevice = device;
         mServices = new ArrayList<BluetoothGattService>();
 
-        IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE);
-        if (b != null) {
-            IBluetoothManager mgr = IBluetoothManager.Stub.asInterface(b);
-            try {
-                mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (RemoteException re) {
-                Log.e(TAG, "Unable to register BluetoothStateChangeCallback", re);
-            }
-        } else {
-            Log.e(TAG, "Unable to get BluetoothManager interface.");
-            throw new RuntimeException("BluetoothManager inactive");
-        }
-
-        //Bind to the service only if the Bluetooth is ON
-        if(mAdapter.isEnabled()){
-            if (!context.bindService(new Intent(IBluetoothGatt.class.getName()), mConnection, 0)) {
-                Log.e(TAG, "Could not bind to Bluetooth Gatt Service");
-            }
-        }
+        mConnState = CONN_STATE_IDLE;
     }
 
     /**
@@ -590,24 +569,6 @@
         if (DBG) Log.d(TAG, "close()");
 
         unregisterApp();
-        mServiceListener = null;
-
-        IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE);
-        if (b != null) {
-            IBluetoothManager mgr = IBluetoothManager.Stub.asInterface(b);
-            try {
-                mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (RemoteException re) {
-                Log.e(TAG, "Unable to unregister BluetoothStateChangeCallback", re);
-            }
-        }
-
-        synchronized (mConnection) {
-            if (mService != null) {
-                mService = null;
-                mContext.unbindService(mConnection);
-            }
-        }
     }
 
     /**
@@ -629,18 +590,18 @@
 
 
     /**
-     * Register an application callback to start using Gatt.
+     * Register an application callback to start using GATT.
      *
-     * <p>This is an asynchronous call. The callback is used to notify
-     * success or failure if the function returns true.
+     * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered}
+     * is used to notify success or failure if the function returns true.
      *
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
      *
-     * @param callback Gatt callback handler that will receive asynchronous
-     *          callbacks.
-     * @return true, if application was successfully registered.
+     * @param callback GATT callback handler that will receive asynchronous callbacks.
+     * @return If true, the callback will be called to notify success or failure,
+     *         false on immediate error
      */
-    public boolean registerApp(BluetoothGattCallback callback) {
+    private boolean registerApp(BluetoothGattCallback callback) {
         if (DBG) Log.d(TAG, "registerApp()");
         if (mService == null) return false;
 
@@ -661,7 +622,7 @@
     /**
      * Unregister the current application and callbacks.
      */
-    public void unregisterApp() {
+    private void unregisterApp() {
         if (DBG) Log.d(TAG, "unregisterApp() - mClientIf=" + mClientIf);
         if (mService == null || mClientIf == 0) return;
 
@@ -675,77 +636,7 @@
     }
 
     /**
-     * Starts a scan for Bluetooth LE devices.
-     *
-     * <p>Results of the scan are reported using the
-     * {@link BluetoothGattCallback#onScanResult} callback.
-     *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     *
-     * @return true, if the scan was started successfully
-     */
-    public boolean startScan() {
-        if (DBG) Log.d(TAG, "startScan()");
-        if (mService == null || mClientIf == 0) return false;
-
-        try {
-            mService.startScan(mClientIf, false);
-        } catch (RemoteException e) {
-            Log.e(TAG,"",e);
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Starts a scan for Bluetooth LE devices, looking for devices that
-     * advertise given services.
-     *
-     * <p>Devices which advertise all specified services are reported using the
-     * {@link BluetoothGattCallback#onScanResult} callback.
-     *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     *
-     * @param serviceUuids Array of services to look for
-     * @return true, if the scan was started successfully
-     */
-    public boolean startScan(UUID[] serviceUuids) {
-        if (DBG) Log.d(TAG, "startScan() - with UUIDs");
-        if (mService == null || mClientIf == 0) return false;
-
-        try {
-            ParcelUuid[] uuids = new ParcelUuid[serviceUuids.length];
-            for(int i = 0; i != uuids.length; ++i) {
-                uuids[i] = new ParcelUuid(serviceUuids[i]);
-            }
-            mService.startScanWithUuids(mClientIf, false, uuids);
-        } catch (RemoteException e) {
-            Log.e(TAG,"",e);
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Stops an ongoing Bluetooth LE device scan.
-     *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     */
-    public void stopScan() {
-        if (DBG) Log.d(TAG, "stopScan()");
-        if (mService == null || mClientIf == 0) return;
-
-        try {
-            mService.stopScan(mClientIf, false);
-        } catch (RemoteException e) {
-            Log.e(TAG,"",e);
-        }
-    }
-
-    /**
-     * Initiate a connection to a Bluetooth Gatt capable device.
+     * Initiate a connection to a Bluetooth GATT capable device.
      *
      * <p>The connection may not be established right away, but will be
      * completed when the remote device is available. A
@@ -757,7 +648,7 @@
      * when the remote device is in range/available. Generally, the first ever
      * connection to a device should be direct (autoConnect set to false) and
      * subsequent connections to known devices should be invoked with the
-     * autoConnect parameter set to false.
+     * autoConnect parameter set to true.
      *
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
      *
@@ -767,18 +658,24 @@
      *                    device becomes available (true).
      * @return true, if the connection attempt was initiated successfully
      */
-    public boolean connect(BluetoothDevice device, boolean autoConnect) {
-        if (DBG) Log.d(TAG, "connect() - device: " + device.getAddress() + ", auto: " + autoConnect);
-        if (mService == null || mClientIf == 0) return false;
-
-        try {
-            mService.clientConnect(mClientIf, device.getAddress(),
-                                   autoConnect ? false : true); // autoConnect is inverse of "isDirect"
-        } catch (RemoteException e) {
-            Log.e(TAG,"",e);
+    /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback) {
+        if (DBG) Log.d(TAG, "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect);
+        synchronized(mStateLock) {
+            if (mConnState != CONN_STATE_IDLE) {
+                throw new IllegalStateException("Not idle");
+            }
+            mConnState = CONN_STATE_CONNECTING;
+        }
+        if (!registerApp(callback)) {
+            synchronized(mStateLock) {
+                mConnState = CONN_STATE_IDLE;
+            }
+            Log.e(TAG, "Failed to register callback");
             return false;
         }
 
+        // the connection will continue after successful callback registration
+        mAutoConnect = autoConnect;
         return true;
     }
 
@@ -787,18 +684,17 @@
      * currently in progress.
      *
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     *
-     * @param device Remote device
      */
-    public void cancelConnection(BluetoothDevice device) {
-        if (DBG) Log.d(TAG, "cancelOpen() - device: " + device.getAddress());
+    public void disconnect() {
+        if (DBG) Log.d(TAG, "cancelOpen() - device: " + mDevice.getAddress());
         if (mService == null || mClientIf == 0) return;
 
         try {
-            mService.clientDisconnect(mClientIf, device.getAddress());
+            mService.clientDisconnect(mClientIf, mDevice.getAddress());
         } catch (RemoteException e) {
             Log.e(TAG,"",e);
         }
+        // TBD deregister after conneciton is torn down
     }
 
     /**
@@ -812,17 +708,16 @@
      *
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
      *
-     * @param device Remote device to explore
      * @return true, if the remote service discovery has been started
      */
-    public boolean discoverServices(BluetoothDevice device) {
-        if (DBG) Log.d(TAG, "discoverServices() - device: " + device.getAddress());
+    public boolean discoverServices() {
+        if (DBG) Log.d(TAG, "discoverServices() - device: " + mDevice.getAddress());
         if (mService == null || mClientIf == 0) return false;
 
         mServices.clear();
 
         try {
-            mService.discoverServices(mClientIf, device.getAddress());
+            mService.discoverServices(mClientIf, mDevice.getAddress());
         } catch (RemoteException e) {
             Log.e(TAG,"",e);
             return false;
@@ -839,16 +734,15 @@
      *
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
      *
-     * @param device Remote device
      * @return List of services on the remote device. Returns an empty list
      *         if service discovery has not yet been performed.
      */
-    public List<BluetoothGattService> getServices(BluetoothDevice device) {
+    public List<BluetoothGattService> getServices() {
         List<BluetoothGattService> result =
                 new ArrayList<BluetoothGattService>();
 
         for (BluetoothGattService service : mServices) {
-            if (service.getDevice().equals(device)) {
+            if (service.getDevice().equals(mDevice)) {
                 result.add(service);
             }
         }
@@ -868,14 +762,13 @@
      *
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
      *
-     * @param device Remote device
      * @param uuid UUID of the requested service
      * @return BluetoothGattService if supported, or null if the requested
      *         service is not offered by the remote device.
      */
-    public BluetoothGattService getService(BluetoothDevice device, UUID uuid) {
+    public BluetoothGattService getService(UUID uuid) {
         for (BluetoothGattService service : mServices) {
-            if (service.getDevice().equals(device) &&
+            if (service.getDevice().equals(mDevice) &&
                 service.getUuid().equals(uuid)) {
                 return service;
             }
@@ -923,8 +816,7 @@
     }
 
     /**
-     * Writes a given characteristic and it's values to the associated remote
-     * device.
+     * Writes a given characteristic and its values to the associated remote device.
      *
      * <p>Once the write operation has been completed, the
      * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
@@ -1061,15 +953,14 @@
      *
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
      *
-     * @param device Remote device
      * @return true, if the reliable write transaction has been initiated
      */
-    public boolean beginReliableWrite(BluetoothDevice device) {
-        if (DBG) Log.d(TAG, "beginReliableWrite() - device: " + device.getAddress());
+    public boolean beginReliableWrite() {
+        if (DBG) Log.d(TAG, "beginReliableWrite() - device: " + mDevice.getAddress());
         if (mService == null || mClientIf == 0) return false;
 
         try {
-            mService.beginReliableWrite(mClientIf, device.getAddress());
+            mService.beginReliableWrite(mClientIf, mDevice.getAddress());
         } catch (RemoteException e) {
             Log.e(TAG,"",e);
             return false;
@@ -1089,15 +980,14 @@
      *
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
      *
-     * @param device Remote device
      * @return true, if the request to execute the transaction has been sent
      */
-    public boolean executeReliableWrite(BluetoothDevice device) {
-        if (DBG) Log.d(TAG, "executeReliableWrite() - device: " + device.getAddress());
+    public boolean executeReliableWrite() {
+        if (DBG) Log.d(TAG, "executeReliableWrite() - device: " + mDevice.getAddress());
         if (mService == null || mClientIf == 0) return false;
 
         try {
-            mService.endReliableWrite(mClientIf, device.getAddress(), true);
+            mService.endReliableWrite(mClientIf, mDevice.getAddress(), true);
         } catch (RemoteException e) {
             Log.e(TAG,"",e);
             return false;
@@ -1113,15 +1003,13 @@
      * operations for a given remote device.
      *
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     *
-     * @param device Remote device
      */
-    public void abortReliableWrite(BluetoothDevice device) {
-        if (DBG) Log.d(TAG, "abortReliableWrite() - device: " + device.getAddress());
+    public void abortReliableWrite(BluetoothDevice mDevice) {
+        if (DBG) Log.d(TAG, "abortReliableWrite() - device: " + mDevice.getAddress());
         if (mService == null || mClientIf == 0) return;
 
         try {
-            mService.endReliableWrite(mClientIf, device.getAddress(), false);
+            mService.endReliableWrite(mClientIf, mDevice.getAddress(), false);
         } catch (RemoteException e) {
             Log.e(TAG,"",e);
         }
@@ -1172,12 +1060,12 @@
      * remote device.
      * @hide
      */
-    public boolean refresh(BluetoothDevice device) {
-        if (DBG) Log.d(TAG, "refresh() - device: " + device.getAddress());
+    public boolean refresh() {
+        if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress());
         if (mService == null || mClientIf == 0) return false;
 
         try {
-            mService.refreshDevice(mClientIf, device.getAddress());
+            mService.refreshDevice(mClientIf, mDevice.getAddress());
         } catch (RemoteException e) {
             Log.e(TAG,"",e);
             return false;
@@ -1194,15 +1082,14 @@
      *
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
      *
-     * @param device Remote device
      * @return true, if the RSSI value has been requested successfully
      */
-    public boolean readRemoteRssi(BluetoothDevice device) {
-        if (DBG) Log.d(TAG, "readRssi() - device: " + device.getAddress());
+    public boolean readRemoteRssi() {
+        if (DBG) Log.d(TAG, "readRssi() - device: " + mDevice.getAddress());
         if (mService == null || mClientIf == 0) return false;
 
         try {
-            mService.readRemoteRssi(mClientIf, device.getAddress());
+            mService.readRemoteRssi(mClientIf, mDevice.getAddress());
         } catch (RemoteException e) {
             Log.e(TAG,"",e);
             return false;
@@ -1212,98 +1099,38 @@
     }
 
     /**
-     * Get the current connection state of the profile.
+     * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
+     * with {@link BluetoothProfile#GATT} as argument
      *
-     * <p>This is not specific to any application configuration but represents
-     * the connection state of the local Bluetooth adapter for this profile.
-     * This can be used by applications like status bar which would just like
-     * to know the state of the local adapter.
-     *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     *
-     * @param device Remote bluetooth device.
-     * @return State of the profile connection. One of
-     *               {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING},
-     *               {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}
+     * @throws UnsupportedOperationException
      */
     @Override
     public int getConnectionState(BluetoothDevice device) {
-        if (DBG) Log.d(TAG,"getConnectionState()");
-        if (mService == null) return STATE_DISCONNECTED;
-
-        List<BluetoothDevice> connectedDevices = getConnectedDevices();
-        for(BluetoothDevice connectedDevice : connectedDevices) {
-            if (device.equals(connectedDevice)) {
-                return STATE_CONNECTED;
-            }
-        }
-
-        return STATE_DISCONNECTED;
+        throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
     }
 
     /**
-     * Get connected devices for the Gatt profile.
+     * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
+     * with {@link BluetoothProfile#GATT} as argument
      *
-     * <p> Return the set of devices which are in state {@link #STATE_CONNECTED}
-     *
-     * <p>This is not specific to any application configuration but represents
-     * the connection state of the local Bluetooth adapter for this profile.
-     * This can be used by applications like status bar which would just like
-     * to know the state of the local adapter.
-     *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     *
-     * @return List of devices. The list will be empty on error.
+     * @throws UnsupportedOperationException
      */
     @Override
     public List<BluetoothDevice> getConnectedDevices() {
-        if (DBG) Log.d(TAG,"getConnectedDevices");
-
-        List<BluetoothDevice> connectedDevices = new ArrayList<BluetoothDevice>();
-        if (mService == null) return connectedDevices;
-
-        try {
-            connectedDevices = mService.getDevicesMatchingConnectionStates(
-                new int[] { BluetoothProfile.STATE_CONNECTED });
-        } catch (RemoteException e) {
-            Log.e(TAG,"",e);
-        }
-
-        return connectedDevices;
+        throw new UnsupportedOperationException
+            ("Use BluetoothManager#getConnectedDevices instead.");
     }
 
     /**
-     * Get a list of devices that match any of the given connection
-     * states.
+     * Not supported - please use
+     * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
+     * with {@link BluetoothProfile#GATT} as first argument
      *
-     * <p> If none of the devices match any of the given states,
-     * an empty list will be returned.
-     *
-     * <p>This is not specific to any application configuration but represents
-     * the connection state of the local Bluetooth adapter for this profile.
-     * This can be used by applications like status bar which would just like
-     * to know the state of the local adapter.
-     *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     *
-     * @param states Array of states. States can be one of
-     *              {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING},
-     *              {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING},
-     * @return List of devices. The list will be empty on error.
+     * @throws UnsupportedOperationException
      */
     @Override
     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        if (DBG) Log.d(TAG,"getDevicesMatchingConnectionStates");
-
-        List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
-        if (mService == null) return devices;
-
-        try {
-            devices = mService.getDevicesMatchingConnectionStates(states);
-        } catch (RemoteException e) {
-            Log.e(TAG,"",e);
-        }
-
-        return devices;
+        throw new UnsupportedOperationException
+            ("Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
     }
 }
diff --git a/core/java/android/bluetooth/BluetoothGattCallback.java b/core/java/android/bluetooth/BluetoothGattCallback.java
index afa4539..c9e5fea 100644
--- a/core/java/android/bluetooth/BluetoothGattCallback.java
+++ b/core/java/android/bluetooth/BluetoothGattCallback.java
@@ -18,34 +18,10 @@
 
 import android.bluetooth.BluetoothDevice;
 
-import android.util.Log;
-
 /**
  * This abstract class is used to implement {@link BluetoothGatt} callbacks.
- * @hide
  */
 public abstract class BluetoothGattCallback {
-    /**
-     * Callback to inform change in registration state of the  application.
-     *
-     * @param status Returns {@link BluetoothGatt#GATT_SUCCESS} if the application
-     *               was successfully registered.
-     */
-    public void onAppRegistered(int status) {
-    }
-
-    /**
-     * Callback reporting an LE device found during a device scan initiated
-     * by the {@link BluetoothGatt#startScan} function.
-     *
-     * @param device Identifies the remote device
-     * @param rssi The RSSI value for the remote device as reported by the
-     *             Bluetooth hardware. 0 if no RSSI value is available.
-     * @param scanRecord The content of the advertisement record offered by
-     *                   the remote device.
-     */
-    public void onScanResult(BluetoothDevice device, int rssi, byte[] scanRecord) {
-    }
 
     /**
      * Callback indicating when a remote device has been connected or disconnected.
@@ -61,8 +37,8 @@
     }
 
     /**
-     * Callback invoked when the list of remote services, characteristics and
-     * descriptors for the remote device have been updated.
+     * 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 status {@link BluetoothGatt#GATT_SUCCESS} if the remote device
@@ -97,7 +73,7 @@
      * @param status The result of the write operation
      */
     public void onCharacteristicWrite(BluetoothGattCharacteristic characteristic,
-                               int status) {
+                                      int status) {
     }
 
     /**
@@ -113,23 +89,21 @@
      * Callback reporting the result of a descriptor read operation.
      *
      * @param descriptor Descriptor that was read from the associated
-     *                       remote device.
+     *                   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(BluetoothGattDescriptor descriptor, int status) {
     }
 
     /**
      * Callback indicating the result of a descriptor write operation.
      *
      * @param descriptor Descriptor that was writte to the associated
-     *                       remote device.
+     *                   remote device.
      * @param status The result of the write operation
      */
-    public void onDescriptorWrite(BluetoothGattDescriptor descriptor,
-                               int status) {
+    public void onDescriptorWrite(BluetoothGattDescriptor descriptor, int status) {
     }
 
     /**
@@ -150,7 +124,7 @@
      *
      * @param device Identifies the remote device
      * @param rssi The RSSI value for the remote device
-     * @param status 0 if the RSSI was read successfully
+     * @param status {@link BluetoothGatt#GATT_SUCCESS} if the RSSI was read successfully
      */
     public void onReadRemoteRssi(BluetoothDevice device, int rssi, int status) {
     }
diff --git a/core/java/android/bluetooth/BluetoothGattCharacteristic.java b/core/java/android/bluetooth/BluetoothGattCharacteristic.java
index f44dc5c..d63d97e 100644
--- a/core/java/android/bluetooth/BluetoothGattCharacteristic.java
+++ b/core/java/android/bluetooth/BluetoothGattCharacteristic.java
@@ -21,8 +21,7 @@
 import java.util.UUID;
 
 /**
- * Represents a Bluetooth Gatt Characteristic
- * @hide
+ * Represents a Bluetooth GATT Characteristic
  */
 public class BluetoothGattCharacteristic {
 
@@ -119,7 +118,7 @@
     public static final int WRITE_TYPE_NO_RESPONSE = 0x01;
 
     /**
-     * Write characteristic including and authenticated signature
+     * Write characteristic including authentication signature
      */
     public static final int WRITE_TYPE_SIGNED = 0x04;
 
@@ -219,12 +218,30 @@
     protected List<BluetoothGattDescriptor> mDescriptors;
 
     /**
+     * Create a new BluetoothGattCharacteristic.
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+     *
+     * @param uuid The UUID for this characteristic
+     * @param properties Properties of this characteristic
+     * @param permissions Permissions for this characteristic
+     */
+    public BluetoothGattCharacteristic(UUID uuid, int properties, int permissions) {
+        initCharacteristic(null, uuid, 0, properties, permissions);
+    }
+
+    /**
      * Create a new BluetoothGattCharacteristic
      * @hide
      */
     /*package*/ BluetoothGattCharacteristic(BluetoothGattService service,
                                             UUID uuid, int instanceId,
                                             int properties, int permissions) {
+        initCharacteristic(service, uuid, instanceId, properties, permissions);
+    }
+
+    private void initCharacteristic(BluetoothGattService service,
+                                    UUID uuid, int instanceId,
+                                    int properties, int permissions) {
         mUuid = uuid;
         mInstance = instanceId;
         mProperties = properties;
@@ -249,11 +266,16 @@
     }
 
     /**
-     * Add a descriptor to this characteristic
-     * @hide
+     * Adds a descriptor to this characteristic.
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+     *
+     * @param descriptor Descriptor to be added to this characteristic.
+     * @return true, if the descriptor was added to the characteristic
      */
-    /*package*/ void addDescriptor(BluetoothGattDescriptor descriptor) {
+    public boolean addDescriptor(BluetoothGattDescriptor descriptor) {
         mDescriptors.add(descriptor);
+        descriptor.setCharacteristic(this);
+        return true;
     }
 
     /**
@@ -265,8 +287,15 @@
     }
 
     /**
+     * Sets the service associated with this device.
+     * @hide
+     */
+    /*package*/ void setService(BluetoothGattService service) {
+        mService = service;
+    }
+
+    /**
      * Returns the UUID of this characteristic
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
      *
      * @return UUID of this characteristic
      */
@@ -280,8 +309,6 @@
      * <p>If a remote device offers multiple characteristics with the same UUID,
      * the instance ID is used to distuinguish between characteristics.
      *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     *
      * @return Instance ID of this characteristic
      */
     public int getInstanceId() {
@@ -294,8 +321,6 @@
      * <p>The properties contain a bit mask of property flags indicating
      * the features of this characteristic.
      *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     *
      * @return Properties of this characteristic
      */
     public int getProperties() {
@@ -304,7 +329,6 @@
 
     /**
      * Returns the permissions for this characteristic.
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
      *
      * @return Permissions of this characteristic
      */
@@ -314,7 +338,6 @@
 
     /**
      * Gets the write type for this characteristic.
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
      *
      * @return Write type for this characteristic
      */
@@ -329,11 +352,6 @@
      * {@link BluetoothGatt#writeCharacteristic} function write this
      * characteristic.
      *
-     * <p>The default write type for a characteristic is
-     * {@link #WRITE_TYPE_DEFAULT}.
-     *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     *
      * @param writeType The write type to for this characteristic. Can be one
      *                  of:
      *                  {@link #WRITE_TYPE_DEFAULT},
@@ -345,8 +363,15 @@
     }
 
     /**
+     * Set the desired key size.
+     * @hide
+     */
+    public void setKeySize(int keySize) {
+        mKeySize = keySize;
+    }
+
+    /**
      * Returns a list of descriptors for this characteristic.
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
      *
      * @return Descriptors for this characteristic
      */
@@ -358,9 +383,7 @@
      * Returns a descriptor with a given UUID out of the list of
      * descriptors for this characteristic.
      *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     *
-     * @return Gatt descriptor object or null if no descriptor with the
+     * @return GATT descriptor object or null if no descriptor with the
      *         given UUID was found.
      */
     public BluetoothGattDescriptor getDescriptor(UUID uuid) {
@@ -376,12 +399,10 @@
      * Get the stored value for this characteristic.
      *
      * <p>This function returns the stored value for this characteristic as
-     * retrieved by calling {@link BluetoothGatt#readCharacteristic}. To cached
+     * retrieved by calling {@link BluetoothGatt#readCharacteristic}. The cached
      * value of the characteristic is updated as a result of a read characteristic
      * operation or if a characteristic update notification has been received.
      *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     *
      * @return Cached value of the characteristic
      */
     public byte[] getValue() {
@@ -397,8 +418,6 @@
      * characteristic value at the given offset are interpreted to generate the
      * return value.
      *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     *
      * @param formatType The format type used to interpret the characteristic
      *                   value.
      * @param offset Offset at which the integer value can be found.
@@ -436,7 +455,6 @@
     /**
      * Return the stored value of this characteristic.
      * <p>See {@link #getValue} for details.
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
      *
      * @param formatType The format type used to interpret the characteristic
      *                   value.
@@ -462,7 +480,7 @@
     /**
      * Return the stored value of this characteristic.
      * <p>See {@link #getValue} for details.
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+     *
      * @param offset Offset at which the string value can be found.
      * @return Cached value of the characteristic
      */
@@ -481,8 +499,6 @@
      * {@link BluetoothGatt#writeCharacteristic} to send the value to the
      * remote device.
      *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     *
      * @param value New value for this characteristic
      * @return true if the locally stored value has been set, false if the
      *              requested value could not be stored locally.
@@ -495,7 +511,6 @@
     /**
      * Set the locally stored value of this characteristic.
      * <p>See {@link #setValue(byte[])} for details.
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
      *
      * @param value New value for this characteristic
      * @param formatType Integer format type used to transform the value parameter
@@ -542,7 +557,7 @@
     /**
      * Set the locally stored value of this characteristic.
      * <p>See {@link #setValue(byte[])} for details.
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+     *
      * @param mantissa Mantissa for this characteristic
      * @param exponent  exponent value for this characteristic
      * @param formatType Float format type used to transform the value parameter
@@ -582,7 +597,7 @@
     /**
      * Set the locally stored value of this characteristic.
      * <p>See {@link #setValue(byte[])} for details.
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+     *
      * @param value New value for this characteristic
      * @return true if the locally stored value has been set
      */
@@ -593,7 +608,6 @@
 
     /**
      * Returns the size of a give value type.
-     * @hide
      */
     private int getTypeLen(int formatType) {
         return formatType & 0xF;
@@ -601,7 +615,6 @@
 
     /**
      * Convert a signed byte to an unsigned int.
-     * @hide
      */
     private int unsignedByteToInt(byte b) {
         return b & 0xFF;
@@ -609,7 +622,6 @@
 
     /**
      * Convert signed bytes to a 16-bit unsigned int.
-     * @hide
      */
     private int unsignedBytesToInt(byte b0, byte b1) {
         return (unsignedByteToInt(b0) + (unsignedByteToInt(b1) << 8));
@@ -617,7 +629,6 @@
 
     /**
      * Convert signed bytes to a 32-bit unsigned int.
-     * @hide
      */
     private int unsignedBytesToInt(byte b0, byte b1, byte b2, byte b3) {
         return (unsignedByteToInt(b0) + (unsignedByteToInt(b1) << 8))
@@ -626,7 +637,6 @@
 
     /**
      * Convert signed bytes to a 16-bit short float value.
-     * @hide
      */
     private float bytesToFloat(byte b0, byte b1) {
         int mantissa = unsignedToSigned(unsignedByteToInt(b0)
@@ -637,7 +647,6 @@
 
     /**
      * Convert signed bytes to a 32-bit short float value.
-     * @hide
      */
     private float bytesToFloat(byte b0, byte b1, byte b2, byte b3) {
         int mantissa = unsignedToSigned(unsignedByteToInt(b0)
@@ -649,7 +658,6 @@
     /**
      * Convert an unsigned integer value to a two's-complement encoded
      * signed value.
-     * @hide
      */
     private int unsignedToSigned(int unsigned, int size) {
         if ((unsigned & (1 << size-1)) != 0) {
@@ -660,7 +668,6 @@
 
     /**
      * Convert an integer into the signed bits of a given length.
-     * @hide
      */
     private int intToSignedBits(int i, int size) {
         if (i < 0) {
diff --git a/core/java/android/bluetooth/BluetoothGattDescriptor.java b/core/java/android/bluetooth/BluetoothGattDescriptor.java
index ba1f28a..6ba2db7 100644
--- a/core/java/android/bluetooth/BluetoothGattDescriptor.java
+++ b/core/java/android/bluetooth/BluetoothGattDescriptor.java
@@ -19,8 +19,7 @@
 import java.util.UUID;
 
 /**
- * Represents a Bluetooth Gatt Descriptor
- * @hide
+ * Represents a Bluetooth GATT Descriptor
  */
 public class BluetoothGattDescriptor {
 
@@ -109,12 +108,28 @@
      * Create a new BluetoothGattDescriptor.
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
      *
+     * @param uuid The UUID for this descriptor
+     * @param permissions Permissions for this descriptor
+     */
+    public BluetoothGattDescriptor(UUID uuid, int permissions) {
+        initDescriptor(null, uuid, permissions);
+    }
+
+    /**
+     * Create a new BluetoothGattDescriptor.
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+     *
      * @param characteristic The characteristic this descriptor belongs to
      * @param uuid The UUID for this descriptor
      * @param permissions Permissions for this descriptor
      */
     /*package*/ BluetoothGattDescriptor(BluetoothGattCharacteristic characteristic, UUID uuid,
                                     int permissions) {
+        initDescriptor(characteristic, uuid, permissions);
+    }
+
+    private void initDescriptor(BluetoothGattCharacteristic characteristic, UUID uuid,
+                                int permissions) {
         mCharacteristic = characteristic;
         mUuid = uuid;
         mPermissions = permissions;
@@ -129,8 +144,15 @@
     }
 
     /**
+     * Set the back-reference to the associated characteristic
+     * @hide
+     */
+    /*package*/ void setCharacteristic(BluetoothGattCharacteristic characteristic) {
+        mCharacteristic = characteristic;
+    }
+
+    /**
      * Returns the UUID of this descriptor.
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
      *
      * @return UUID of this descriptor
      */
@@ -140,7 +162,6 @@
 
     /**
      * Returns the permissions for this descriptor.
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
      *
      * @return Permissions of this descriptor
      */
@@ -152,12 +173,10 @@
      * Returns the stored value for this descriptor
      *
      * <p>This function returns the stored value for this descriptor as
-     * retrieved by calling {@link BluetoothGatt#readDescriptor}. To cached
+     * retrieved by calling {@link BluetoothGatt#readDescriptor}. The cached
      * value of the descriptor is updated as a result of a descriptor read
      * operation.
      *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     *
      * @return Cached value of the descriptor
      */
     public byte[] getValue() {
@@ -172,8 +191,6 @@
      * {@link BluetoothGatt#writeDescriptor} to send the value to the
      * remote device.
      *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     *
      * @param value New value for this descriptor
      * @return true if the locally stored value has been set, false if the
      *              requested value could not be stored locally.
diff --git a/core/java/android/bluetooth/BluetoothGattServer.java b/core/java/android/bluetooth/BluetoothGattServer.java
index 6b69377..d1f4b82 100644
--- a/core/java/android/bluetooth/BluetoothGattServer.java
+++ b/core/java/android/bluetooth/BluetoothGattServer.java
@@ -38,88 +38,30 @@
 import java.util.UUID;
 
 /**
- * Public API for the Bluetooth Gatt Profile server role.
+ * Public API for the Bluetooth GATT Profile server role.
  *
- * <p>This class provides Bluetooth Gatt server role functionality,
+ * <p>This class provides Bluetooth GATT server role functionality,
  * allowing applications to create and advertise Bluetooth Smart services
  * and characteristics.
  *
  * <p>BluetoothGattServer is a proxy object for controlling the Bluetooth Service
  * via IPC.  Use {@link BluetoothAdapter#getProfileProxy} to get the
  * BluetoothGatt proxy object.
- * @hide
  */
 public final class BluetoothGattServer implements BluetoothProfile {
     private static final String TAG = "BluetoothGattServer";
     private static final boolean DBG = true;
 
-    private Context mContext;
-    private ServiceListener mServiceListener;
+    private final Context mContext;
     private BluetoothAdapter mAdapter;
     private IBluetoothGatt mService;
     private BluetoothGattServerCallback mCallback;
-    private int mServerIf;
 
+    private Object mServerIfLock = new Object();
+    private int mServerIf;
     private List<BluetoothGattService> mServices;
 
-    /**
-     * Bluetooth state change handlers
-     */
-    private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
-        new IBluetoothStateChangeCallback.Stub() {
-            public void onBluetoothStateChange(boolean up) {
-                if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
-                if (!up) {
-                    if (DBG) Log.d(TAG,"Unbinding service...");
-                    synchronized (mConnection) {
-                        try {
-                            mService = null;
-                            mContext.unbindService(mConnection);
-                        } catch (Exception re) {
-                            Log.e(TAG,"",re);
-                        }
-                    }
-                } else {
-                    synchronized (mConnection) {
-                        try {
-                            if (mService == null) {
-                                if (DBG) Log.d(TAG,"Binding service...");
-                                if (!mContext.bindService(new
-                                        Intent(IBluetoothGatt.class.getName()),
-                                        mConnection, 0)) {
-                                    Log.e(TAG, "Could not bind to Bluetooth GATT Service");
-                                }
-                            }
-                        } catch (Exception re) {
-                            Log.e(TAG,"",re);
-                        }
-                    }
-                }
-            }
-        };
-
-    /**
-     * Service binder handling
-     */
-    private ServiceConnection mConnection = new ServiceConnection() {
-            public void onServiceConnected(ComponentName className, IBinder service) {
-                if (DBG) Log.d(TAG, "Proxy object connected");
-                mService = IBluetoothGatt.Stub.asInterface(service);
-                ServiceListener serviceListner = mServiceListener;
-                if (serviceListner != null) {
-                    serviceListner.onServiceConnected(BluetoothProfile.GATT_SERVER,
-                                                      BluetoothGattServer.this);
-                }
-            }
-            public void onServiceDisconnected(ComponentName className) {
-                if (DBG) Log.d(TAG, "Proxy object disconnected");
-                mService = null;
-                ServiceListener serviceListner = mServiceListener;
-                if (serviceListner != null) {
-                    serviceListner.onServiceDisconnected(BluetoothProfile.GATT_SERVER);
-                }
-            }
-        };
+    private static final int CALLBACK_REG_TIMEOUT = 10000;
 
     /**
      * Bluetooth GATT interface callbacks
@@ -133,11 +75,14 @@
             public void onServerRegistered(int status, int serverIf) {
                 if (DBG) Log.d(TAG, "onServerRegistered() - status=" + status
                     + " serverIf=" + serverIf);
-                mServerIf = serverIf;
-                try {
-                    mCallback.onAppRegistered(status);
-                } catch (Exception ex) {
-                    Log.w(TAG, "Unhandled exception: " + ex);
+                synchronized(mServerIfLock) {
+                    if (mCallback != null) {
+                        mServerIf = serverIf;
+                        mServerIfLock.notify();
+                    } else {
+                        // registration timeout
+                        Log.e(TAG, "onServerRegistered: mCallback is null");
+                    }
                 }
             }
 
@@ -147,13 +92,7 @@
              */
             public void onScanResult(String address, int rssi, byte[] advData) {
                 if (DBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi);
-
-                try {
-                    mCallback.onScanResult(mAdapter.getRemoteDevice(address),
-                                           rssi, advData);
-                } catch (Exception ex) {
-                    Log.w(TAG, "Unhandled exception: " + ex);
-                }
+                // no op
             }
 
             /**
@@ -209,8 +148,7 @@
                 BluetoothGattService service = getService(srvcUuid, srvcInstId, srvcType);
                 if (service == null) return;
 
-                BluetoothGattCharacteristic characteristic = service.getCharacteristic(
-                    charUuid);
+                BluetoothGattCharacteristic characteristic = service.getCharacteristic(charUuid);
                 if (characteristic == null) return;
 
                 try {
@@ -340,31 +278,13 @@
     /**
      * Create a BluetoothGattServer proxy object.
      */
-    /*package*/ BluetoothGattServer(Context context, ServiceListener l) {
+    /*package*/ BluetoothGattServer(Context context, IBluetoothGatt iGatt) {
         mContext = context;
-        mServiceListener = l;
+        mService = iGatt;
         mAdapter = BluetoothAdapter.getDefaultAdapter();
+        mCallback = null;
+        mServerIf = 0;
         mServices = new ArrayList<BluetoothGattService>();
-
-        IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE);
-        if (b != null) {
-            IBluetoothManager mgr = IBluetoothManager.Stub.asInterface(b);
-            try {
-                mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (RemoteException re) {
-                Log.e(TAG, "Unable to register BluetoothStateChangeCallback", re);
-            }
-        } else {
-            Log.e(TAG, "Unable to get BluetoothManager interface.");
-            throw new RuntimeException("BluetoothManager inactive");
-        }
-
-        //Bind to the service only if the Bluetooth is ON
-        if(mAdapter.isEnabled()){
-            if (!context.bindService(new Intent(IBluetoothGatt.class.getName()), mConnection, 0)) {
-                Log.e(TAG, "Could not bind to Bluetooth Gatt Service");
-            }
-        }
     }
 
     /**
@@ -372,29 +292,75 @@
      */
     /*package*/ void close() {
         if (DBG) Log.d(TAG, "close()");
+        unregisterCallback();
+    }
 
-        unregisterApp();
-        mServiceListener = null;
+    /**
+     * Register an application callback to start using GattServer.
+     *
+     * <p>This is an asynchronous call. The callback is used to notify
+     * success or failure if the function returns true.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+     *
+     * @param callback GATT callback handler that will receive asynchronous
+     *                 callbacks.
+     * @return true, the callback will be called to notify success or failure,
+     *         false on immediate error
+     */
+    /*package*/ boolean registerCallback(BluetoothGattServerCallback callback) {
+        if (DBG) Log.d(TAG, "registerCallback()");
+        if (mService == null) {
+            Log.e(TAG, "GATT service not available");
+            return false;
+        }
+        UUID uuid = UUID.randomUUID();
+        if (DBG) Log.d(TAG, "registerCallback() - UUID=" + uuid);
 
-        IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_MANAGER_SERVICE);
-        if (b != null) {
-            IBluetoothManager mgr = IBluetoothManager.Stub.asInterface(b);
+        synchronized(mServerIfLock) {
+            if (mCallback != null) {
+                Log.e(TAG, "App can register callback only once");
+                return false;
+            }
+
+            mCallback = callback;
             try {
-                mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
-            } catch (RemoteException re) {
-                Log.e(TAG, "Unable to unregister BluetoothStateChangeCallback", re);
+                mService.registerServer(new ParcelUuid(uuid), mBluetoothGattServerCallback);
+            } catch (RemoteException e) {
+                Log.e(TAG,"",e);
+                mCallback = null;
+                return false;
+            }
+
+            try {
+                mServerIfLock.wait(CALLBACK_REG_TIMEOUT);
+            } catch (InterruptedException e) {
+                Log.e(TAG, "" + e);
+                mCallback = null;
+            }
+
+            if (mServerIf == 0) {
+                mCallback = null;
+                return false;
+            } else {
+                return true;
             }
         }
+    }
 
-        synchronized (mConnection) {
-            if (mService != null) {
-                try {
-                    mService = null;
-                    mContext.unbindService(mConnection);
-                } catch (Exception re) {
-                    Log.e(TAG,"",re);
-                }
-            }
+    /**
+     * Unregister the current application and callbacks.
+     */
+    private void unregisterCallback() {
+        if (DBG) Log.d(TAG, "unregisterCallback() - mServerIf=" + mServerIf);
+        if (mService == null || mServerIf == 0) return;
+
+        try {
+            mCallback = null;
+            mService.unregisterServer(mServerIf);
+            mServerIf = 0;
+        } catch (RemoteException e) {
+            Log.e(TAG,"",e);
         }
     }
 
@@ -414,123 +380,7 @@
     }
 
     /**
-     * Register an application callback to start using Gatt.
-     *
-     * <p>This is an asynchronous call. The callback is used to notify
-     * success or failure if the function returns true.
-     *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     *
-     * @param callback Gatt callback handler that will receive asynchronous
-     *          callbacks.
-     * @return true, if application was successfully registered.
-     */
-    public boolean registerApp(BluetoothGattServerCallback callback) {
-        if (DBG) Log.d(TAG, "registerApp()");
-        if (mService == null) return false;
-
-        mCallback = callback;
-        UUID uuid = UUID.randomUUID();
-        if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);
-
-        try {
-            mService.registerServer(new ParcelUuid(uuid), mBluetoothGattServerCallback);
-        } catch (RemoteException e) {
-            Log.e(TAG,"",e);
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Unregister the current application and callbacks.
-     */
-    public void unregisterApp() {
-        if (DBG) Log.d(TAG, "unregisterApp() - mServerIf=" + mServerIf);
-        if (mService == null || mServerIf == 0) return;
-
-        try {
-            mCallback = null;
-            mService.unregisterServer(mServerIf);
-            mServerIf = 0;
-        } catch (RemoteException e) {
-            Log.e(TAG,"",e);
-        }
-    }
-
-    /**
-     * Starts a scan for Bluetooth LE devices.
-     *
-     * <p>Results of the scan are reported using the
-     * {@link BluetoothGattServerCallback#onScanResult} callback.
-     *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     *
-     * @return true, if the scan was started successfully
-     */
-    public boolean startScan() {
-        if (DBG) Log.d(TAG, "startScan()");
-        if (mService == null || mServerIf == 0) return false;
-
-        try {
-            mService.startScan(mServerIf, true);
-        } catch (RemoteException e) {
-            Log.e(TAG,"",e);
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Starts a scan for Bluetooth LE devices, looking for devices that
-     * advertise given services.
-     *
-     * <p>Devices which advertise all specified services are reported using the
-     * {@link BluetoothGattServerCallback#onScanResult} callback.
-     *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     *
-     * @param serviceUuids Array of services to look for
-     * @return true, if the scan was started successfully
-     */
-    public boolean startScan(UUID[] serviceUuids) {
-        if (DBG) Log.d(TAG, "startScan() - with UUIDs");
-        if (mService == null || mServerIf == 0) return false;
-
-        try {
-            ParcelUuid[] uuids = new ParcelUuid[serviceUuids.length];
-            for(int i = 0; i != uuids.length; ++i) {
-                uuids[i] = new ParcelUuid(serviceUuids[i]);
-            }
-            mService.startScanWithUuids(mServerIf, true, uuids);
-        } catch (RemoteException e) {
-            Log.e(TAG,"",e);
-            return false;
-        }
-
-        return true;
-    }
-
-    /**
-     * Stops an ongoing Bluetooth LE device scan.
-     *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     */
-    public void stopScan() {
-        if (DBG) Log.d(TAG, "stopScan()");
-        if (mService == null || mServerIf == 0) return;
-
-        try {
-            mService.stopScan(mServerIf, true);
-        } catch (RemoteException e) {
-            Log.e(TAG,"",e);
-        }
-    }
-
-    /**
-     * Initiate a connection to a Bluetooth Gatt capable device.
+     * Initiate a connection to a Bluetooth GATT capable device.
      *
      * <p>The connection may not be established right away, but will be
      * completed when the remote device is available. A
@@ -542,11 +392,10 @@
      * when the remote device is in range/available. Generally, the first ever
      * connection to a device should be direct (autoConnect set to false) and
      * subsequent connections to known devices should be invoked with the
-     * autoConnect parameter set to false.
+     * autoConnect parameter set to true.
      *
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
      *
-     * @param device Remote device to connect to
      * @param autoConnect Whether to directly connect to the remote device (false)
      *                    or to automatically connect as soon as the remote
      *                    device becomes available (true).
@@ -590,7 +439,7 @@
      * Send a response to a read or write request to a remote device.
      *
      * <p>This function must be invoked in when a remote read/write request
-     * is received by one of these callback methots:
+     * is received by one of these callback methods:
      *
      * <ul>
      *      <li>{@link BluetoothGattServerCallback#onCharacteristicReadRequest}
@@ -662,17 +511,17 @@
     }
 
     /**
-     * Add a service to the list of services to be advertised.
+     * Add a service to the list of services to be hosted.
      *
      * <p>Once a service has been addded to the the list, the service and it's
-     * included characteristics will be advertised by the local device.
+     * included characteristics will be provided by the local device.
      *
-     * <p>If the local device is already advertising services when this function
+     * <p>If the local device has already exposed services when this function
      * is called, a service update notification will be sent to all clients.
      *
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
      *
-     * @param service Service to be added to the list of services advertised
+     * @param service Service to be added to the list of services provided
      *                by this device.
      * @return true, if the service has been added successfully
      */
@@ -721,11 +570,11 @@
     }
 
     /**
-     * Removes a service from the list of services to be advertised.
+     * Removes a service from the list of services to be provided.
      *
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
      *
-     * @param service Service to beremoved.
+     * @param service Service to be removed.
      * @return true, if the service has been removed
      */
     public boolean removeService(BluetoothGattService service) {
@@ -749,7 +598,7 @@
     }
 
     /**
-     * Remove all services from the list of advertised services.
+     * Remove all services from the list of provided services.
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
      */
     public void clearServices() {
@@ -765,7 +614,7 @@
     }
 
     /**
-     * Returns a list of GATT services offered bu this device.
+     * Returns a list of GATT services offered by this device.
      *
      * <p>An application must call {@link #addService} to add a serice to the
      * list of services offered by this device.
@@ -802,99 +651,40 @@
         return null;
     }
 
+
     /**
-     * Get the current connection state of the profile.
+     * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
+     * with {@link BluetoothProfile#GATT} as argument
      *
-     * <p>This is not specific to any application configuration but represents
-     * the connection state of the local Bluetooth adapter for this profile.
-     * This can be used by applications like status bar which would just like
-     * to know the state of the local adapter.
-     *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     *
-     * @param device Remote bluetooth device.
-     * @return State of the profile connection. One of
-     *               {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING},
-     *               {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING}
+     * @throws UnsupportedOperationException
      */
     @Override
     public int getConnectionState(BluetoothDevice device) {
-        if (DBG) Log.d(TAG,"getConnectionState()");
-        if (mService == null) return STATE_DISCONNECTED;
-
-        List<BluetoothDevice> connectedDevices = getConnectedDevices();
-        for(BluetoothDevice connectedDevice : connectedDevices) {
-            if (device.equals(connectedDevice)) {
-                return STATE_CONNECTED;
-            }
-        }
-
-        return STATE_DISCONNECTED;
+        throw new UnsupportedOperationException("Use BluetoothManager#getConnectionState instead.");
     }
 
     /**
-     * Get connected devices for the Gatt profile.
+     * Not supported - please use {@link BluetoothManager#getConnectedDevices(int)}
+     * with {@link BluetoothProfile#GATT} as argument
      *
-     * <p> Return the set of devices which are in state {@link #STATE_CONNECTED}
-     *
-     * <p>This is not specific to any application configuration but represents
-     * the connection state of the local Bluetooth adapter for this profile.
-     * This can be used by applications like status bar which would just like
-     * to know the state of the local adapter.
-     *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     *
-     * @return List of devices. The list will be empty on error.
+     * @throws UnsupportedOperationException
      */
     @Override
     public List<BluetoothDevice> getConnectedDevices() {
-        if (DBG) Log.d(TAG,"getConnectedDevices");
-
-        List<BluetoothDevice> connectedDevices = new ArrayList<BluetoothDevice>();
-        if (mService == null) return connectedDevices;
-
-        try {
-            connectedDevices = mService.getDevicesMatchingConnectionStates(
-                new int[] { BluetoothProfile.STATE_CONNECTED });
-        } catch (RemoteException e) {
-            Log.e(TAG,"",e);
-        }
-
-        return connectedDevices;
+        throw new UnsupportedOperationException
+            ("Use BluetoothManager#getConnectedDevices instead.");
     }
 
     /**
-     * Get a list of devices that match any of the given connection
-     * states.
+     * Not supported - please use
+     * {@link BluetoothManager#getDevicesMatchingConnectionStates(int, int[])}
+     * with {@link BluetoothProfile#GATT} as first argument
      *
-     * <p> If none of the devices match any of the given states,
-     * an empty list will be returned.
-     *
-     * <p>This is not specific to any application configuration but represents
-     * the connection state of the local Bluetooth adapter for this profile.
-     * This can be used by applications like status bar which would just like
-     * to know the state of the local adapter.
-     *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     *
-     * @param states Array of states. States can be one of
-     *              {@link #STATE_CONNECTED}, {@link #STATE_CONNECTING},
-     *              {@link #STATE_DISCONNECTED}, {@link #STATE_DISCONNECTING},
-     * @return List of devices. The list will be empty on error.
+     * @throws UnsupportedOperationException
      */
     @Override
     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
-        if (DBG) Log.d(TAG,"getDevicesMatchingConnectionStates");
-
-        List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
-        if (mService == null) return devices;
-
-        try {
-            devices = mService.getDevicesMatchingConnectionStates(states);
-        } catch (RemoteException e) {
-            Log.e(TAG,"",e);
-        }
-
-        return devices;
+        throw new UnsupportedOperationException
+            ("Use BluetoothManager#getDevicesMatchingConnectionStates instead.");
     }
 }
diff --git a/core/java/android/bluetooth/BluetoothGattServerCallback.java b/core/java/android/bluetooth/BluetoothGattServerCallback.java
index 4f608ff..f9f1d97 100644
--- a/core/java/android/bluetooth/BluetoothGattServerCallback.java
+++ b/core/java/android/bluetooth/BluetoothGattServerCallback.java
@@ -22,30 +22,8 @@
 
 /**
  * This abstract class is used to implement {@link BluetoothGattServer} callbacks.
- * @hide
  */
 public abstract class BluetoothGattServerCallback {
-    /**
-     * Callback to inform change in registration state of the  application.
-     *
-     * @param status Returns {@link BluetoothGatt#GATT_SUCCESS} if the application
-     *               was successfully registered.
-     */
-    public void onAppRegistered(int status) {
-    }
-
-    /**
-     * Callback reporting an LE device found during a device scan initiated
-     * by the {@link BluetoothGattServer#startScan} function.
-     *
-     * @param device Identifies the remote device
-     * @param rssi The RSSI value for the remote device as reported by the
-     *             Bluetooth hardware. 0 if no RSSI value is available.
-     * @param scanRecord The content of the advertisement record offered by
-     *                   the remote device.
-     */
-    public void onScanResult(BluetoothDevice device, int rssi, byte[] scanRecord) {
-    }
 
     /**
      * Callback indicating when a remote device has been connected or disconnected.
@@ -101,9 +79,9 @@
      * @param value The value the client wants to assign to the characteristic
      */
     public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId,
-                            BluetoothGattCharacteristic characteristic,
-                            boolean preparedWrite, boolean responseNeeded,
-                            int offset, byte[] value) {
+                                             BluetoothGattCharacteristic characteristic,
+                                             boolean preparedWrite, boolean responseNeeded,
+                                             int offset, byte[] value) {
     }
 
     /**
@@ -118,7 +96,7 @@
      * @param descriptor Descriptor to be read
      */
     public void onDescriptorReadRequest(BluetoothDevice device, int requestId,
-                            int offset, BluetoothGattDescriptor descriptor) {
+                                        int offset, BluetoothGattDescriptor descriptor) {
     }
 
     /**
@@ -137,9 +115,9 @@
      * @param value The value the client wants to assign to the descriptor
      */
     public void onDescriptorWriteRequest(BluetoothDevice device, int requestId,
-                            BluetoothGattDescriptor descriptor,
-                            boolean preparedWrite, boolean responseNeeded,
-                            int offset,  byte[] value) {
+                                         BluetoothGattDescriptor descriptor,
+                                         boolean preparedWrite, boolean responseNeeded,
+                                         int offset,  byte[] value) {
     }
 
     /**
diff --git a/core/java/android/bluetooth/BluetoothGattService.java b/core/java/android/bluetooth/BluetoothGattService.java
index 6a3ce66..c3b3cfe 100644
--- a/core/java/android/bluetooth/BluetoothGattService.java
+++ b/core/java/android/bluetooth/BluetoothGattService.java
@@ -22,8 +22,7 @@
 import java.util.UUID;
 
 /**
- * Represents a Bluetooth Gatt Service
- * @hide
+ * Represents a Bluetooth GATT Service
  */
 public class BluetoothGattService {
 
@@ -81,9 +80,14 @@
 
     /**
      * Create a new BluetoothGattService.
-     * @hide
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+     *
+     * @param uuid The UUID for this service
+     * @param serviceType The type of this service,
+     *        {@link BluetoothGattService#SERVICE_TYPE_PRIMARY} or
+     *        {@link BluetoothGattService#SERVICE_TYPE_SECONDARY}
      */
-    /*package*/ BluetoothGattService(UUID uuid, int serviceType) {
+    public BluetoothGattService(UUID uuid, int serviceType) {
         mDevice = null;
         mUuid = uuid;
         mInstanceId = 0;
@@ -115,11 +119,28 @@
     }
 
     /**
-     * Add a characteristic to this service.
-     * @hide
+     * Add an included service to this service.
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+     *
+     * @param service The service to be added
+     * @return true, if the included service was added to the service
      */
-    /*package*/ void addCharacteristic(BluetoothGattCharacteristic characteristic) {
+    public boolean addService(BluetoothGattService service) {
+        mIncludedServices.add(service);
+        return true;
+    }
+
+    /**
+     * Add a characteristic to this service.
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+     *
+     * @param characteristic The characteristics to be added
+     * @return true, if the characteristic was added to the service
+     */
+    public boolean addCharacteristic(BluetoothGattCharacteristic characteristic) {
         mCharacteristics.add(characteristic);
+        characteristic.setService(this);
+        return true;
     }
 
     /**
@@ -136,6 +157,15 @@
     }
 
     /**
+     * Force the instance ID.
+     * This is needed for conformance testing only.
+     * @hide
+     */
+    public void setInstanceId(int instanceId) {
+        mInstanceId = instanceId;
+    }
+
+    /**
      * Get the handle count override (conformance testing.
      * @hide
      */
@@ -144,6 +174,15 @@
     }
 
     /**
+     * Force the number of handles to reserve for this service.
+     * This is needed for conformance testing only.
+     * @hide
+     */
+    public void setHandles(int handles) {
+        mHandles = handles;
+    }
+
+    /**
      * Add an included service to the internal map.
      * @hide
      */
@@ -153,7 +192,6 @@
 
     /**
      * Returns the UUID of this service
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
      *
      * @return UUID of this service
      */
@@ -168,8 +206,6 @@
      * (ex. multiple battery services for different batteries), the instance
      * ID is used to distuinguish services.
      *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     *
      * @return Instance ID of this service
      */
     public int getInstanceId() {
@@ -178,15 +214,13 @@
 
     /**
      * Get the type of this service (primary/secondary)
-     * @hide
      */
     public int getType() {
         return mServiceType;
     }
 
     /**
-     * Get the list of included Gatt services for this service.
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+     * Get the list of included GATT services for this service.
      *
      * @return List of included services or empty list if no included services
      *         were discovered.
@@ -197,7 +231,6 @@
 
     /**
      * Returns a list of characteristics included in this service.
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
      *
      * @return Characteristics included in this service
      */
@@ -217,9 +250,7 @@
      * UUID, the first instance of a characteristic with the given UUID
      * is returned.
      *
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     *
-     * @return Gatt characteristic object or null if no characteristic with the
+     * @return GATT characteristic object or null if no characteristic with the
      *         given UUID was found.
      */
     public BluetoothGattCharacteristic getCharacteristic(UUID uuid) {
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
old mode 100755
new mode 100644
diff --git a/core/java/android/bluetooth/BluetoothInputDevice.java b/core/java/android/bluetooth/BluetoothInputDevice.java
old mode 100755
new mode 100644
diff --git a/core/java/android/bluetooth/BluetoothManager.java b/core/java/android/bluetooth/BluetoothManager.java
new file mode 100644
index 0000000..19083b5
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothManager.java
@@ -0,0 +1,219 @@
+/*
+ * 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.bluetooth;
+
+import android.content.Context;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * High level manager used to obtain an instance of an {@link BluetoothAdapter}
+ * and to conduct overall Bluetooth Management.
+ * <p>
+ * Use {@link android.content.Context#getSystemService(java.lang.String)}
+ * with {@link Context#BLUETOOTH_SERVICE} to create an {@link BluetoothManager},
+ * then call {@link #getAdapter} to obtain the {@link BluetoothAdapter}.
+ * <p>
+ * Alternately, you can just call the static helper
+ * {@link BluetoothAdapter#getDefaultAdapter()}.
+ *
+ * <div class="special reference">
+ * <h3>Developer Guides</h3>
+ * <p>For more information about using BLUETOOTH, read the
+ * <a href="{@docRoot}guide/topics/connectivity/bluetooth.html">Bluetooth</a> developer guide.</p>
+ * </div>
+ *
+ * @see Context#getSystemService
+ * @see BluetoothAdapter#getDefaultAdapter()
+ */
+public final class BluetoothManager {
+    private static final String TAG = "BluetoothManager";
+    private static final boolean DBG = true;
+    private static final boolean VDBG = true;
+
+    private final BluetoothAdapter mAdapter;
+
+    /**
+     * @hide
+     */
+    public BluetoothManager(Context context) {
+        context = context.getApplicationContext();
+        if (context == null) {
+            throw new IllegalArgumentException(
+                    "context not associated with any application (using a mock context?)");
+        }
+        // Legacy api - getDefaultAdapter does not take in the context
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
+    }
+
+    /**
+     * Get the default BLUETOOTH Adapter for this device.
+     *
+     * @return the default BLUETOOTH Adapter
+     */
+    public BluetoothAdapter getAdapter() {
+        return mAdapter;
+    }
+
+    /**
+     * Get the current connection state of the profile to the remote device.
+     *
+     * <p>This is not specific to any application configuration but represents
+     * the connection state of the local Bluetooth adapter for certain profile.
+     * This can be used by applications like status bar which would just like
+     * to know the state of Bluetooth.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+     *
+     * @param device Remote bluetooth device.
+     * @param profile GATT or GATT_SERVER
+     * @return State of the profile connection. One of
+     *         {@link BluetoothProfile#STATE_CONNECTED}, {@link BluetoothProfile#STATE_CONNECTING},
+     *         {@link BluetoothProfile#STATE_DISCONNECTED},
+     *         {@link BluetoothProfile#STATE_DISCONNECTING}
+     */
+    public int getConnectionState(BluetoothDevice device, int profile) {
+        if (DBG) Log.d(TAG,"getConnectionState()");
+
+        List<BluetoothDevice> connectedDevices = getConnectedDevices(profile);
+        for(BluetoothDevice connectedDevice : connectedDevices) {
+            if (device.equals(connectedDevice)) {
+                return BluetoothProfile.STATE_CONNECTED;
+            }
+        }
+
+        return BluetoothProfile.STATE_DISCONNECTED;
+    }
+
+    /**
+     * Get connected devices for the specified profile.
+     *
+     * <p> Return the set of devices which are in state {@link BluetoothProfile#STATE_CONNECTED}
+     *
+     * <p>This is not specific to any application configuration but represents
+     * the connection state of Bluetooth for this profile.
+     * This can be used by applications like status bar which would just like
+     * to know the state of Bluetooth.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+     *
+     * @param profile GATT or GATT_SERVER
+     * @return List of devices. The list will be empty on error.
+     */
+    public List<BluetoothDevice> getConnectedDevices(int profile) {
+        if (DBG) Log.d(TAG,"getConnectedDevices");
+        if (profile != BluetoothProfile.GATT && profile != BluetoothProfile.GATT_SERVER) {
+            throw new IllegalArgumentException("Profile not supported: " + profile);
+        }
+
+        List<BluetoothDevice> connectedDevices = new ArrayList<BluetoothDevice>();
+
+        try {
+            IBluetoothManager managerService = mAdapter.getBluetoothManager();
+            IBluetoothGatt iGatt = (IBluetoothGatt) managerService.getBluetoothGatt();
+            if (iGatt == null) return connectedDevices;
+
+            connectedDevices = iGatt.getDevicesMatchingConnectionStates(
+                new int[] { BluetoothProfile.STATE_CONNECTED });
+        } catch (RemoteException e) {
+            Log.e(TAG,"",e);
+        }
+
+        return connectedDevices;
+    }
+
+    /**
+     * 
+     * Get a list of devices that match any of the given connection
+     * states.
+     *
+     * <p> If none of the devices match any of the given states,
+     * an empty list will be returned.
+     *
+     * <p>This is not specific to any application configuration but represents
+     * the connection state of the local Bluetooth adapter for this profile.
+     * This can be used by applications like status bar which would just like
+     * to know the state of the local adapter.
+     *
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
+     *
+     * @param profile GATT or GATT_SERVER
+     * @param states Array of states. States can be one of
+     *        {@link BluetoothProfile#STATE_CONNECTED}, {@link BluetoothProfile#STATE_CONNECTING},
+     *        {@link BluetoothProfile#STATE_DISCONNECTED},
+     *        {@link BluetoothProfile#STATE_DISCONNECTING},
+     * @return List of devices. The list will be empty on error.
+     */
+    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int profile, int[] states) {
+        if (DBG) Log.d(TAG,"getDevicesMatchingConnectionStates");
+
+        if (profile != BluetoothProfile.GATT && profile != BluetoothProfile.GATT_SERVER) {
+            throw new IllegalArgumentException("Profile not supported: " + profile);
+        }
+
+        List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
+
+        try {
+            IBluetoothManager managerService = mAdapter.getBluetoothManager();
+            IBluetoothGatt iGatt = (IBluetoothGatt) managerService.getBluetoothGatt();
+            if (iGatt == null) return devices;
+            devices = iGatt.getDevicesMatchingConnectionStates(states);
+        } catch (RemoteException e) {
+            Log.e(TAG,"",e);
+        }
+
+        return devices;
+    }
+
+    /**
+     * Open a GATT Server
+     * The callback is used to deliver results to Caller, such as connection status as well
+     * as the results of any other GATT server operations.
+     * The method returns a BluetoothGattServer instance. You can use BluetoothGattServer
+     * to conduct GATT server operations.
+     * @param context App context
+     * @param callback GATT server callback handler that will receive asynchronous callbacks.
+     * @return BluetoothGattServer instance
+     */
+    public BluetoothGattServer openGattServer(Context context,
+                                              BluetoothGattServerCallback callback) {
+        if (context == null || callback == null) {
+            throw new IllegalArgumentException("null parameter: " + context + " " + callback);
+        }
+
+        // TODO(Bluetooth) check whether platform support BLE
+        //     Do the check here or in GattServer?
+
+        try {
+            IBluetoothManager managerService = mAdapter.getBluetoothManager();
+            IBluetoothGatt iGatt = (IBluetoothGatt) managerService.getBluetoothGatt();
+            if (iGatt == null) {
+                Log.e(TAG, "Fail to get GATT Server connection");
+                return null;
+            }
+            BluetoothGattServer mGattServer = new BluetoothGattServer(context, iGatt);
+            Boolean regStatus = mGattServer.registerCallback(callback);
+            return regStatus? mGattServer : null;
+        } catch (RemoteException e) {
+            Log.e(TAG,"",e);
+            return null;
+        }
+    }
+}
diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java
old mode 100755
new mode 100644
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index 9ee202a..43079f4 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -89,13 +89,11 @@
 
     /**
      * GATT
-     * @hide
      */
     static public final int GATT = 7;
 
     /**
      * GATT_SERVER
-     * @hide
      */
     static public final int GATT_SERVER = 8;
 
diff --git a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java
index 43c2392..81c0a6a 100644
--- a/core/java/android/bluetooth/BluetoothTetheringDataTracker.java
+++ b/core/java/android/bluetooth/BluetoothTetheringDataTracker.java
@@ -21,6 +21,7 @@
 import android.os.INetworkManagementService;
 import android.content.Context;
 import android.net.ConnectivityManager;
+import android.net.DhcpResults;
 import android.net.LinkCapabilities;
 import android.net.LinkProperties;
 import android.net.NetworkInfo;
@@ -28,7 +29,10 @@
 import android.net.NetworkStateTracker;
 import android.net.NetworkUtils;
 import android.os.Handler;
+import android.os.Looper;
 import android.os.Message;
+import android.os.Messenger;
+import android.text.TextUtils;
 import android.util.Log;
 import java.net.InterfaceAddress;
 import android.net.LinkAddress;
@@ -36,8 +40,11 @@
 import java.net.Inet4Address;
 import android.os.SystemProperties;
 
+import com.android.internal.util.AsyncChannel;
+
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
 
 /**
  * This class tracks the data connection associated with Bluetooth
@@ -51,24 +58,29 @@
     private static final String NETWORKTYPE = "BLUETOOTH_TETHER";
     private static final String TAG = "BluetoothTethering";
     private static final boolean DBG = true;
-    private static final boolean VDBG = false;
+    private static final boolean VDBG = true;
 
     private AtomicBoolean mTeardownRequested = new AtomicBoolean(false);
     private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false);
     private AtomicInteger mDefaultGatewayAddr = new AtomicInteger(0);
     private AtomicBoolean mDefaultRouteSet = new AtomicBoolean(false);
 
+    private final Object mLinkPropertiesLock = new Object();
     private LinkProperties mLinkProperties;
+
     private LinkCapabilities mLinkCapabilities;
+
+    private final Object mNetworkInfoLock = new Object();
     private NetworkInfo mNetworkInfo;
 
     private BluetoothPan mBluetoothPan;
-    private static String mIface;
-    private Thread mDhcpThread;
+    private static String mRevTetheredIface;
     /* For sending events to connectivity service handler */
     private Handler mCsHandler;
-    private Context mContext;
-    public static BluetoothTetheringDataTracker sInstance;
+    protected Context mContext;
+    private static BluetoothTetheringDataTracker sInstance;
+    private BtdtHandler mBtdtHandler;
+    private AtomicReference<AsyncChannel> mAsyncChannel = new AtomicReference<AsyncChannel>(null);
 
     private BluetoothTetheringDataTracker() {
         mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_BLUETOOTH, 0, NETWORKTYPE, "");
@@ -108,6 +120,7 @@
         if (adapter != null) {
             adapter.getProfileProxy(mContext, mProfileServiceListener, BluetoothProfile.PAN);
         }
+        mBtdtHandler = new BtdtHandler(target.getLooper(), this);
     }
 
     private BluetoothProfile.ServiceListener mProfileServiceListener =
@@ -224,15 +237,19 @@
     /**
      * Fetch NetworkInfo for the network
      */
-    public synchronized NetworkInfo getNetworkInfo() {
-        return mNetworkInfo;
+    public NetworkInfo getNetworkInfo() {
+        synchronized (mNetworkInfoLock) {
+            return new NetworkInfo(mNetworkInfo);
+        }
     }
 
     /**
      * Fetch LinkProperties for the network
      */
-    public synchronized LinkProperties getLinkProperties() {
-        return new LinkProperties(mLinkProperties);
+    public LinkProperties getLinkProperties() {
+        synchronized (mLinkPropertiesLock) {
+            return new LinkProperties(mLinkProperties);
+        }
     }
 
    /**
@@ -286,88 +303,68 @@
         return count;
     }
 
-
-    private boolean readLinkProperty(String iface) {
-        String DhcpPrefix = "dhcp." + iface + ".";
-        String ip = SystemProperties.get(DhcpPrefix + "ipaddress");
-        String dns1 = SystemProperties.get(DhcpPrefix + "dns1");
-        String dns2 = SystemProperties.get(DhcpPrefix + "dns2");
-        String gateway = SystemProperties.get(DhcpPrefix + "gateway");
-        String mask = SystemProperties.get(DhcpPrefix + "mask");
-        if(ip.isEmpty() || gateway.isEmpty()) {
-            Log.e(TAG, "readLinkProperty, ip: " +  ip + ", gateway: " + gateway + ", can not be empty");
-            return false;
+    void startReverseTether(final LinkProperties linkProperties) {
+        if (linkProperties == null || TextUtils.isEmpty(linkProperties.getInterfaceName())) {
+            Log.e(TAG, "attempted to reverse tether with empty interface");
+            return;
         }
-        int PrefixLen = countPrefixLength(NetworkUtils.numericToInetAddress(mask).getAddress());
-        mLinkProperties.addLinkAddress(new LinkAddress(NetworkUtils.numericToInetAddress(ip), PrefixLen));
-        RouteInfo ri = new RouteInfo(NetworkUtils.numericToInetAddress(gateway));
-        mLinkProperties.addRoute(ri);
-        if(!dns1.isEmpty())
-            mLinkProperties.addDns(NetworkUtils.numericToInetAddress(dns1));
-        if(!dns2.isEmpty())
-            mLinkProperties.addDns(NetworkUtils.numericToInetAddress(dns2));
-        mLinkProperties.setInterfaceName(iface);
-        return true;
-    }
-    public synchronized void startReverseTether(String iface) {
-        mIface = iface;
-        if (DBG) Log.d(TAG, "startReverseTether mCsHandler: " + mCsHandler);
-         mDhcpThread = new Thread(new Runnable() {
+        synchronized (mLinkPropertiesLock) {
+            if (mLinkProperties.getInterfaceName() != null) {
+                Log.e(TAG, "attempted to reverse tether while already in process");
+                return;
+            }
+            mLinkProperties = linkProperties;
+        }
+        Thread dhcpThread = new Thread(new Runnable() {
             public void run() {
-                //TODO(): Add callbacks for failure and success case.
                 //Currently this thread runs independently.
-                if (DBG) Log.d(TAG, "startReverseTether mCsHandler: " + mCsHandler);
-                String DhcpResultName = "dhcp." + mIface + ".result";;
-                String result = "";
-                if (VDBG) Log.d(TAG, "waiting for change of sys prop dhcp result: " + DhcpResultName);
-                for(int i = 0; i < 30*5; i++) {
-                    try { Thread.sleep(200); } catch (InterruptedException ie) { return;}
-                    result = SystemProperties.get(DhcpResultName);
-                    if (VDBG) Log.d(TAG, "read " + DhcpResultName + ": " + result);
-                    if(result.equals("failed")) {
-                        Log.e(TAG, "startReverseTether, failed to start dhcp service");
+                DhcpResults dhcpResults = new DhcpResults();
+                boolean success = NetworkUtils.runDhcp(linkProperties.getInterfaceName(),
+                        dhcpResults);
+                synchronized (mLinkPropertiesLock) {
+                    if (linkProperties.getInterfaceName() != mLinkProperties.getInterfaceName()) {
+                        Log.e(TAG, "obsolete DHCP run aborted");
                         return;
                     }
-                    if(result.equals("ok")) {
-                        if (VDBG) Log.d(TAG, "startReverseTether, dhcp resut: " + result);
-                        if(readLinkProperty(mIface)) {
-
-                            mNetworkInfo.setIsAvailable(true);
-                            mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
-
-                            if (VDBG) Log.d(TAG, "startReverseTether mCsHandler: " + mCsHandler);
-                            if(mCsHandler != null) {
-                                Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo);
-                                msg.sendToTarget();
-
-                                msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo);
-                                msg.sendToTarget();
-                            }
-                        }
+                    if (!success) {
+                        Log.e(TAG, "DHCP request error:" + NetworkUtils.getDhcpError());
                         return;
                     }
+                    mLinkProperties = dhcpResults.linkProperties;
+                    synchronized (mNetworkInfoLock) {
+                        mNetworkInfo.setIsAvailable(true);
+                        mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
+                        if (mCsHandler != null) {
+                            Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED,
+                                    new NetworkInfo(mNetworkInfo));
+                            msg.sendToTarget();
+                       }
+                    }
+                    return;
                 }
-                Log.e(TAG, "startReverseTether, dhcp failed, resut: " + result);
             }
         });
-        mDhcpThread.start();
+        dhcpThread.start();
     }
 
-    public synchronized void stopReverseTether() {
-        //NetworkUtils.stopDhcp(iface);
-        if(mDhcpThread != null && mDhcpThread.isAlive()) {
-            mDhcpThread.interrupt();
-            try { mDhcpThread.join(); } catch (InterruptedException ie) { return; }
+    void stopReverseTether() {
+        synchronized (mLinkPropertiesLock) {
+            if (TextUtils.isEmpty(mLinkProperties.getInterfaceName())) {
+                Log.e(TAG, "attempted to stop reverse tether with nothing tethered");
+                return;
+            }
+            NetworkUtils.stopDhcp(mLinkProperties.getInterfaceName());
+            mLinkProperties.clear();
+            synchronized (mNetworkInfoLock) {
+                mNetworkInfo.setIsAvailable(false);
+                mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
+
+                if (mCsHandler != null) {
+                    mCsHandler.obtainMessage(EVENT_STATE_CHANGED, new NetworkInfo(mNetworkInfo)).
+                            sendToTarget();
+                }
+            }
         }
-        mLinkProperties.clear();
-        mNetworkInfo.setIsAvailable(false);
-        mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
-
-        Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo);
-        msg.sendToTarget();
-
-        msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo);
-        msg.sendToTarget();
     }
 
     public void setDependencyMet(boolean met) {
@@ -383,4 +380,54 @@
     public void removeStackedLink(LinkProperties link) {
         mLinkProperties.removeStackedLink(link);
     }
+
+    static class BtdtHandler extends Handler {
+        private AsyncChannel mStackChannel;
+        private final BluetoothTetheringDataTracker mBtdt;
+
+        BtdtHandler(Looper looper, BluetoothTetheringDataTracker parent) {
+            super(looper);
+            mBtdt = parent;
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
+                    if (VDBG) Log.d(TAG, "got CMD_CHANNEL_HALF_CONNECTED");
+                    if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
+                        AsyncChannel ac = (AsyncChannel)msg.obj;
+                        if (mBtdt.mAsyncChannel.compareAndSet(null, ac) == false) {
+                            Log.e(TAG, "Trying to set mAsyncChannel twice!");
+                        } else {
+                            ac.sendMessage(
+                                    AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
+                        }
+                    }
+                    break;
+                case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
+                    if (VDBG) Log.d(TAG, "got CMD_CHANNEL_DISCONNECTED");
+                    mBtdt.stopReverseTether();
+                    mBtdt.mAsyncChannel.set(null);
+                    break;
+                case NetworkStateTracker.EVENT_NETWORK_CONNECTED:
+                    LinkProperties linkProperties = (LinkProperties)(msg.obj);
+                    if (VDBG) Log.d(TAG, "got EVENT_NETWORK_CONNECTED, " + linkProperties);
+                    mBtdt.startReverseTether(linkProperties);
+                    break;
+                case NetworkStateTracker.EVENT_NETWORK_DISCONNECTED:
+                    linkProperties = (LinkProperties)(msg.obj);
+                    if (VDBG) Log.d(TAG, "got EVENT_NETWORK_DISCONNECTED, " + linkProperties);
+                    mBtdt.stopReverseTether();
+                    break;
+            }
+        }
+    }
+
+    @Override
+    public void supplyMessenger(Messenger messenger) {
+        if (messenger != null) {
+            new AsyncChannel().connect(mContext, mBtdtHandler, messenger);
+        }
+    }
 }
diff --git a/core/java/android/bluetooth/IBluetoothInputDevice.aidl b/core/java/android/bluetooth/IBluetoothInputDevice.aidl
old mode 100755
new mode 100644
diff --git a/core/java/android/bluetooth/IBluetoothManager.aidl b/core/java/android/bluetooth/IBluetoothManager.aidl
old mode 100755
new mode 100644
index ed8777c..493d2f8
--- a/core/java/android/bluetooth/IBluetoothManager.aidl
+++ b/core/java/android/bluetooth/IBluetoothManager.aidl
@@ -17,6 +17,7 @@
 package android.bluetooth;
 
 import android.bluetooth.IBluetooth;
+import android.bluetooth.IBluetoothGatt;
 import android.bluetooth.IBluetoothManagerCallback;
 import android.bluetooth.IBluetoothStateChangeCallback;
 
@@ -35,6 +36,7 @@
     boolean enable();
     boolean enableNoAutoConnect();
     boolean disable(boolean persist);
+    IBluetoothGatt getBluetoothGatt();
 
     String getAddress();
     String getName();
diff --git a/core/java/android/bluetooth/MutableBluetoothGattCharacteristic.java b/core/java/android/bluetooth/MutableBluetoothGattCharacteristic.java
deleted file mode 100644
index c05abb2..0000000
--- a/core/java/android/bluetooth/MutableBluetoothGattCharacteristic.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * 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.bluetooth;
-
-import java.util.ArrayList;
-import java.util.IllegalFormatConversionException;
-import java.util.List;
-import java.util.UUID;
-
-/**
- * Mutable variant of a Bluetooth Gatt Characteristic
- * @hide
- */
-public class MutableBluetoothGattCharacteristic extends BluetoothGattCharacteristic {
-
-    /**
-     * Create a new MutableBluetoothGattCharacteristic.
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     *
-     * @param uuid The UUID for this characteristic
-     * @param properties Properties of this characteristic
-     * @param permissions Permissions for this characteristic
-     */
-    public MutableBluetoothGattCharacteristic(UUID uuid, int properties, int permissions) {
-        super(null, uuid, 0, properties, permissions);
-    }
-
-    /**
-     * Adds a descriptor to this characteristic.
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     *
-     * @param descriptor Descriptor to be added to this characteristic.
-     */
-    public void addDescriptor(MutableBluetoothGattDescriptor descriptor) {
-        mDescriptors.add(descriptor);
-        descriptor.setCharacteristic(this);
-    }
-
-    /**
-     * Set the desired key size.
-     * @hide
-     */
-    public void setKeySize(int keySize) {
-        mKeySize = keySize;
-    }
-
-    /**
-     * Sets the service associated with this device.
-     * @hide
-     */
-    /*package*/ void setService(BluetoothGattService service) {
-        mService = service;
-    }
-}
diff --git a/core/java/android/bluetooth/MutableBluetoothGattDescriptor.java b/core/java/android/bluetooth/MutableBluetoothGattDescriptor.java
deleted file mode 100644
index e455392..0000000
--- a/core/java/android/bluetooth/MutableBluetoothGattDescriptor.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * 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.bluetooth;
-
-import java.util.UUID;
-
-/**
- * Mutable variant of a Bluetooth Gatt Descriptor
- * @hide
- */
-public class MutableBluetoothGattDescriptor extends BluetoothGattDescriptor {
-
-    /**
-     * Create a new BluetoothGattDescriptor.
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     *
-     * @param uuid The UUID for this descriptor
-     * @param permissions Permissions for this descriptor
-     */
-    public MutableBluetoothGattDescriptor(UUID uuid, int permissions) {
-        super(null, uuid, permissions);
-    }
-
-    /**
-     * Set the back-reference to the associated characteristic
-     * @hide
-     */
-    /*package*/ void setCharacteristic(BluetoothGattCharacteristic characteristic) {
-        mCharacteristic = characteristic;
-    }
-}
diff --git a/core/java/android/bluetooth/MutableBluetoothGattService.java b/core/java/android/bluetooth/MutableBluetoothGattService.java
deleted file mode 100644
index 927f5ab..0000000
--- a/core/java/android/bluetooth/MutableBluetoothGattService.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * 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.bluetooth;
-
-import android.bluetooth.BluetoothDevice;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.UUID;
-
-/**
- * Represents a Bluetooth Gatt Service
- * @hide
- */
-public class MutableBluetoothGattService extends BluetoothGattService {
-
-    /**
-     * Create a new MutableBluetoothGattService.
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     *
-     * @param uuid The UUID for this service
-     * @param serviceType The type of this service (primary/secondary)
-     */
-    public MutableBluetoothGattService(UUID uuid, int serviceType) {
-        super(uuid, serviceType);
-    }
-
-    /**
-     * Add an included service to this service.
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     *
-     * @param service The service to be added
-     * @return true, if the included service was added to the service
-     */
-    public boolean addService(BluetoothGattService service) {
-        mIncludedServices.add(service);
-        return true;
-    }
-
-    /**
-     * Add a characteristic to this service.
-     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
-     *
-     * @param characteristic The characteristics to be added
-     * @return true, if the characteristic was added to the service
-     */
-    public boolean addCharacteristic(MutableBluetoothGattCharacteristic characteristic) {
-        mCharacteristics.add(characteristic);
-        characteristic.setService(this);
-        return true;
-    }
-
-    /**
-     * Force the instance ID.
-     * This is needed for conformance testing only.
-     * @hide
-     */
-    public void setInstanceId(int instanceId) {
-        mInstanceId = instanceId;
-    }
-
-    /**
-     * Force the number of handles to reserve for this service.
-     * This is needed for conformance testing only.
-     * @hide
-     */
-    public void setHandles(int handles) {
-        mHandles = handles;
-    }
-}
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 4968268..cf627d7 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -609,7 +609,7 @@
         // selection statement with a dummy one that will always be false.
         // This way we will get a cursor back that has the correct structure
         // but contains no rows.
-        if (selection == null) {
+        if (selection == null || selection.isEmpty()) {
             selection = "'A' = 'B'";
         } else {
             selection = "'A' = 'B' AND (" + selection + ")";
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 7dd76cd..ef9b0bf 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2203,7 +2203,6 @@
      * {@link android.bluetooth.BluetoothAdapter} for using Bluetooth.
      *
      * @see #getSystemService
-     * @hide
      */
     public static final String BLUETOOTH_SERVICE = "bluetooth";
 
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index e1461e3..0efd6b0 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -2416,8 +2416,11 @@
 
     /**
      * Broadcast to a specific application to query any supported restrictions to impose
-     * on restricted users. The response should contain an extra {@link #EXTRA_RESTRICTIONS}
-     * which is of type <code>ArrayList&lt;RestrictionEntry&gt;</code>.
+     * on restricted users. The response should contain an extra {@link #EXTRA_RESTRICTIONS},
+     * which is of type <code>ArrayList&lt;RestrictionEntry&gt;</code>. It can also
+     * contain an extra {@link #EXTRA_RESTRICTIONS_INTENT}, which is of type <code>Intent</code>.
+     * The activity specified by that intent will be launched for a result which must contain
+     * the extra {@link #EXTRA_RESTRICTIONS}. The returned restrictions will be persisted.
      * @see RestrictionEntry
      */
     public static final String ACTION_GET_RESTRICTION_ENTRIES =
@@ -3161,6 +3164,13 @@
      */
     public static final String EXTRA_RESTRICTIONS = "android.intent.extra.restrictions";
 
+    /**
+     * Extra used in the response from a BroadcastReceiver that handles
+     * {@link #ACTION_GET_RESTRICTION_ENTRIES}.
+     */
+    public static final String EXTRA_RESTRICTIONS_INTENT =
+            "android.intent.extra.restrictions_intent";
+
     // ---------------------------------------------------------------------
     // ---------------------------------------------------------------------
     // Intent flags (see mFlags variable).
diff --git a/core/java/android/content/RestrictionEntry.java b/core/java/android/content/RestrictionEntry.java
index 97c4cb8..af90385 100644
--- a/core/java/android/content/RestrictionEntry.java
+++ b/core/java/android/content/RestrictionEntry.java
@@ -34,141 +34,278 @@
 public class RestrictionEntry implements Parcelable {
 
     /**
-     * A type of restriction. Use this one for information that needs to be transferred across
-     * but shouldn't be presented to the user in the UI.
+     * A type of restriction. Use this type for information that needs to be transferred across
+     * but shouldn't be presented to the user in the UI. Stores a single String value.
      */
     public static final int TYPE_NULL         = 0;
+
     /**
-     * A type of restriction. Use this for storing true/false values, typically presented as
+     * A type of restriction. Use this for storing a boolean value, typically presented as
      * a checkbox in the UI.
      */
     public static final int TYPE_BOOLEAN      = 1;
+
     /**
      * A type of restriction. Use this for storing a string value, typically presented as
-     * a single-select list. The {@link #values} and {@link #choices} need to have the list of
-     * possible values and the corresponding localized strings, respectively, to present in the UI.
+     * a single-select list. Call {@link #setChoiceEntries(String[])} and
+     * {@link #setChoiceValues(String[])} to set the localized list entries to present to the user
+     * and the corresponding values, respectively.
      */
     public static final int TYPE_CHOICE       = 2;
+
     /**
      * A type of restriction. Use this for storing a string value, typically presented as
-     * a single-select list. The {@link #values} and {@link #choices} need to have the list of
-     * possible values and the corresponding localized strings, respectively, to present in the UI.
+     * a single-select list. Call {@link #setChoiceEntries(String[])} and
+     * {@link #setChoiceValues(String[])} to set the localized list entries to present to the user
+     * and the corresponding values, respectively.
      * The presentation could imply that values in lower array indices are included when a
      * particular value is chosen.
      */
     public static final int TYPE_CHOICE_LEVEL = 3;
+
     /**
      * A type of restriction. Use this for presenting a multi-select list where more than one
      * entry can be selected, such as for choosing specific titles to white-list.
-     * The {@link #values} and {@link #choices} need to have the list of
-     * possible values and the corresponding localized strings, respectively, to present in the UI.
-     * Use {@link #getMultipleValues()} and {@link #setMultipleValues(String[])} to manipulate
-     * the selections.
+     * Call {@link #setChoiceEntries(String[])} and
+     * {@link #setChoiceValues(String[])} to set the localized list entries to present to the user
+     * and the corresponding values, respectively.
+     * Use {@link #getAllSelectedStrings()} and {@link #setAllSelectedStrings(String[])} to
+     * manipulate the selections.
      */
     public static final int TYPE_MULTI_SELECT = 4;
 
     /** The type of restriction. */
-    public int type;
+    private int type;
 
     /** The unique key that identifies the restriction. */
-    public String key;
+    private String key;
 
     /** The user-visible title of the restriction. */
-    public String title;
+    private String title;
 
     /** The user-visible secondary description of the restriction. */
-    public String description;
+    private String description;
 
     /** The user-visible set of choices used for single-select and multi-select lists. */
-    public String [] choices;
+    private String [] choices;
 
     /** The values corresponding to the user-visible choices. The value(s) of this entry will
-     * one or more of these, returned by {@link #getMultipleValues()} and
-     * {@link #getStringValue()}.
+     * one or more of these, returned by {@link #getAllSelectedStrings()} and
+     * {@link #getSelectedString()}.
      */
-    public String [] values;
+    private String [] values;
 
     /* The chosen value, whose content depends on the type of the restriction. */
     private String currentValue;
+
     /* List of selected choices in the multi-select case. */
     private String[] currentValues;
 
     /**
      * Constructor for {@link #TYPE_CHOICE} and {@link #TYPE_CHOICE_LEVEL} types.
      * @param key the unique key for this restriction
-     * @param value the current value
+     * @param selectedString the current value
      */
-    public RestrictionEntry(String key, String value) {
+    public RestrictionEntry(String key, String selectedString) {
         this.key = key;
-        this.currentValue = value;
+        this.currentValue = selectedString;
     }
 
     /**
      * Constructor for {@link #TYPE_BOOLEAN} type.
      * @param key the unique key for this restriction
-     * @param value the current value
+     * @param selectedState whether this restriction is selected or not
      */
-    public RestrictionEntry(String key, boolean value) {
+    public RestrictionEntry(String key, boolean selectedState) {
         this.key = key;
-        setValue(value);
+        setSelectedState(selectedState);
     }
 
     /**
      * Constructor for {@link #TYPE_MULTI_SELECT} type.
      * @param key the unique key for this restriction
-     * @param multipleValues the list of values that are currently selected
+     * @param selectedStrings the list of values that are currently selected
      */
-    public RestrictionEntry(String key, String[] multipleValues) {
+    public RestrictionEntry(String key, String[] selectedStrings) {
         this.key = key;
-        this.currentValues = multipleValues;
+        this.currentValues = selectedStrings;
     }
 
     /**
-     * Returns the current value. Null for {@link #TYPE_MULTI_SELECT} type.
-     * @return the current value
+     * Sets the type for this restriction.
+     * @param type the type for this restriction.
      */
-    public String getStringValue() {
+    public void setType(int type) {
+        this.type = type;
+    }
+
+    /**
+     * Returns the type for this restriction.
+     * @return the type for this restriction
+     */
+    public int getType() {
+        return type;
+    }
+
+    /**
+     * Returns the currently selected string value.
+     * @return the currently selected value, which can be null for types that aren't for holding
+     * single string values.
+     */
+    public String getSelectedString() {
         return currentValue;
     }
 
     /**
-     * Returns the list of current selections. Null if the type is not {@link #TYPE_MULTI_SELECT}.
-     * @return the list of current selections.
+     * Returns the list of currently selected values.
+     * @return the list of current selections, if type is {@link #TYPE_MULTI_SELECT},
+     *  null otherwise.
      */
-    public String[] getMultipleValues() {
+    public String[] getAllSelectedStrings() {
         return currentValues;
     }
 
     /**
-     * Returns the current boolean value for entries of type {@link #TYPE_BOOLEAN}.
-     * @return the current value
+     * Returns the current selected state for an entry of type {@link #TYPE_BOOLEAN}.
+     * @return the current selected state of the entry.
      */
-    public boolean getBooleanValue() {
+    public boolean getSelectedState() {
         return Boolean.parseBoolean(currentValue);
     }
 
     /**
-     * Set the current string value.
-     * @param s the current value
+     * Sets the string value to use as the selected value for this restriction. This value will
+     * be persisted by the system for later use by the application.
+     * @param selectedString the string value to select.
      */
-    public void setValue(String s) {
-        currentValue = s;
+    public void setSelectedString(String selectedString) {
+        currentValue = selectedString;
     }
 
     /**
-     * Sets the current boolean value.
-     * @param b the current value
+     * Sets the current selected state for an entry of type {@link #TYPE_BOOLEAN}. This value will
+     * be persisted by the system for later use by the application.
+     * @param state the current selected state
      */
-    public void setValue(boolean b) {
-        currentValue = Boolean.toString(b);
+    public void setSelectedState(boolean state) {
+        currentValue = Boolean.toString(state);
     }
 
     /**
-     * Sets the current list of selected values.
-     * @param values the current list of selected values
+     * Sets the current list of selected values for an entry of type {@link #TYPE_MULTI_SELECT}.
+     * These values will be persisted by the system for later use by the application.
+     * @param allSelectedStrings the current list of selected values.
      */
-    public void setMultipleValues(String[] values) {
-        currentValues = values;
+    public void setAllSelectedStrings(String[] allSelectedStrings) {
+        currentValues = allSelectedStrings;
+    }
+
+    /**
+     * Sets a list of string values that can be selected by the user. If no user-visible entries
+     * are set by a call to {@link #setChoiceEntries(String[])}, these values will be the ones
+     * shown to the user. Values will be chosen from this list as the user's selection and the
+     * selected values can be retrieved by a call to {@link #getAllSelectedStrings()}, or
+     * {@link #getSelectedString()}, depending on whether it is a multi-select type or choice type.
+     * This method is not relevant for types other than {@link #TYPE_CHOICE_LEVEL},
+     * {@link #TYPE_CHOICE}, and {@link #TYPE_MULTI_SELECT}.
+     * @param choiceValues an array of Strings which will be the selected values for the user's
+     * selections.
+     * @see #getChoiceValues()
+     * @see #getAllSelectedStrings()
+     */
+    public void setChoiceValues(String[] choiceValues) {
+        values = choiceValues;
+    }
+
+    /**
+     * Sets a list of string values that can be selected by the user, similar to
+     * {@link #setChoiceValues(String[])}.
+     * @param context the application context for retrieving the resources.
+     * @param stringArrayResId the resource id for a string array containing the possible values.
+     * @see #setChoiceValues(String[])
+     */
+    public void setChoiceValues(Context context, int stringArrayResId) {
+        values = context.getResources().getStringArray(stringArrayResId);
+    }
+
+    /**
+     * Returns the list of possible string values set earlier.
+     * @return the list of possible values.
+     */
+    public String[] getChoiceValues() {
+        return values;
+    }
+
+    /**
+     * Sets a list of strings that will be presented as choices to the user. When the
+     * user selects one or more of these choices, the corresponding value from the possible values
+     * are stored as the selected strings. The size of this array must match the size of the array
+     * set in {@link #setChoiceValues(String[])}. This method is not relevant for types other
+     * than {@link #TYPE_CHOICE_LEVEL}, {@link #TYPE_CHOICE}, and {@link #TYPE_MULTI_SELECT}.
+     * @param choiceEntries the list of user-visible choices.
+     * @see #setChoiceValues(String[])
+     */
+    public void setChoiceEntries(String[] choiceEntries) {
+        choices = choiceEntries;
+    }
+
+    /** Sets a list of strings that will be presented as choices to the user. This is similar to
+     * {@link #setChoiceEntries(String[])}.
+     * @param context the application context, used for retrieving the resources.
+     * @param stringArrayResId the resource id of a string array containing the possible entries.
+     */
+    public void setChoiceEntries(Context context, int stringArrayResId) {
+        choices = context.getResources().getStringArray(stringArrayResId);
+    }
+
+    /**
+     * Returns the list of strings, set earlier, that will be presented as choices to the user.
+     * @return the list of choices presented to the user.
+     */
+    public String[] getChoiceEntries() {
+        return choices;
+    }
+
+    /**
+     * Returns the provided user-visible description of the entry, if any.
+     * @return the user-visible description, null if none was set earlier.
+     */
+    public String getDescription() {
+        return description;
+    }
+
+    /**
+     * Sets the user-visible description of the entry, as a possible sub-text for the title.
+     * You can use this to describe the entry in more detail or to display the current state of
+     * the restriction.
+     * @param description the user-visible description string.
+     */
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    /**
+     * This is the unique key for the restriction entry.
+     * @return the key for the restriction.
+     */
+    public String getKey() {
+        return key;
+    }
+
+    /**
+     * Returns the user-visible title for the entry, if any.
+     * @return the user-visible title for the entry, null if none was set earlier.
+     */
+    public String getTitle() {
+        return title;
+    }
+
+    /**
+     * Sets the user-visible title for the entry.
+     * @param title the user-visible title for the entry.
+     */
+    public void setTitle(String title) {
+        this.title = title;
     }
 
     private boolean equalArrays(String[] one, String[] other) {
diff --git a/core/java/android/content/UriMatcher.java b/core/java/android/content/UriMatcher.java
index 841c8f4..1a8ea47 100644
--- a/core/java/android/content/UriMatcher.java
+++ b/core/java/android/content/UriMatcher.java
@@ -69,6 +69,11 @@
         sURIMatcher.addURI("call_log", "calls/#", CALLS_ID);
     }
 </pre>
+<p>Starting from API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}, paths can start
+ with a leading slash.  For example:
+<pre>
+        sURIMatcher.addURI("contacts", "/people", PEOPLE);
+</pre>
 <p>Then when you need to match against a URI, call {@link #match}, providing
 the URL that you have been given.  You can use the result to build a query,
 return a type, insert or delete a row, or whatever you need, without duplicating
@@ -143,6 +148,9 @@
      * matched. URI nodes may be exact match string, the token "*"
      * that matches any text, or the token "#" that matches only
      * numbers.
+     * <p>
+     * Starting from API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2},
+     * this method will accept leading slash in the path.
      *
      * @param authority the authority to match
      * @param path the path to match. * may be used as a wild card for
@@ -155,7 +163,17 @@
         if (code < 0) {
             throw new IllegalArgumentException("code " + code + " is invalid: it must be positive");
         }
-        String[] tokens = path != null ? PATH_SPLIT_PATTERN.split(path) : null;
+
+        String[] tokens = null;
+        if (path != null) {
+            String newPath = path;
+            // Strip leading slash if present.
+            if (path.length() > 0 && path.charAt(0) == '/') {
+                newPath = path.substring(1);
+            }
+            tokens = PATH_SPLIT_PATTERN.split(newPath);
+        }
+
         int numTokens = tokens != null ? tokens.length : 0;
         UriMatcher node = this;
         for (int i = -1; i < numTokens; i++) {
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 32cc7fd..02401dc 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -398,6 +398,15 @@
     public String[] resourceDirs;
 
     /**
+     * String retrieved from the seinfo tag found in selinux policy. This value
+     * is useful in setting an SELinux security context on the process as well
+     * as its data directory.
+     *
+     * {@hide}
+     */
+    public String seinfo;
+
+    /**
      * Paths to all shared libraries this application is linked against.  This
      * field is only set if the {@link PackageManager#GET_SHARED_LIBRARY_FILES
      * PackageManager.GET_SHARED_LIBRARY_FILES} flag was used when retrieving
@@ -477,6 +486,9 @@
         if (resourceDirs != null) {
             pw.println(prefix + "resourceDirs=" + resourceDirs);
         }
+        if (seinfo != null) {
+            pw.println(prefix + "seinfo=" + seinfo);
+        }
         pw.println(prefix + "dataDir=" + dataDir);
         if (sharedLibraryFiles != null) {
             pw.println(prefix + "sharedLibraryFiles=" + sharedLibraryFiles);
@@ -544,6 +556,7 @@
         publicSourceDir = orig.publicSourceDir;
         nativeLibraryDir = orig.nativeLibraryDir;
         resourceDirs = orig.resourceDirs;
+        seinfo = orig.seinfo;
         sharedLibraryFiles = orig.sharedLibraryFiles;
         dataDir = orig.dataDir;
         uid = orig.uid;
@@ -583,6 +596,7 @@
         dest.writeString(publicSourceDir);
         dest.writeString(nativeLibraryDir);
         dest.writeStringArray(resourceDirs);
+        dest.writeString(seinfo);
         dest.writeStringArray(sharedLibraryFiles);
         dest.writeString(dataDir);
         dest.writeInt(uid);
@@ -621,6 +635,7 @@
         publicSourceDir = source.readString();
         nativeLibraryDir = source.readString();
         resourceDirs = source.readStringArray();
+        seinfo = source.readString();
         sharedLibraryFiles = source.readStringArray();
         dataDir = source.readString();
         uid = source.readInt();
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index a32a201..a0e1555 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -239,7 +239,8 @@
     /**
      * As per {@link android.content.pm.PackageManager#setApplicationEnabledSetting}.
      */
-    void setApplicationEnabledSetting(in String packageName, in int newState, int flags, int userId);
+    void setApplicationEnabledSetting(in String packageName, in int newState, int flags,
+            int userId, String callingPackage);
     
     /**
      * As per {@link android.content.pm.PackageManager#getApplicationEnabledSetting}.
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index 77ca7f6..fb539c5 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -154,7 +154,7 @@
     /**
      * Flag for {@link #requestedPermissionsFlags}: the requested permission
      * is required for the application to run; the user can not optionally
-     * disable it.  Currently all permissions are required.
+     * disable it.
      */
     public static final int REQUESTED_PERMISSION_REQUIRED = 1<<0;
 
@@ -221,6 +221,9 @@
     /** @hide */
     public boolean requiredForAllUsers;
 
+    /** @hide */
+    public String restrictedAccountType;
+
     public PackageInfo() {
     }
 
@@ -262,6 +265,7 @@
         dest.writeTypedArray(reqFeatures, parcelableFlags);
         dest.writeInt(installLocation);
         dest.writeInt(requiredForAllUsers ? 1 : 0);
+        dest.writeString(restrictedAccountType);
     }
 
     public static final Parcelable.Creator<PackageInfo> CREATOR
@@ -301,5 +305,6 @@
         reqFeatures = source.createTypedArray(FeatureInfo.CREATOR);
         installLocation = source.readInt();
         requiredForAllUsers = source.readInt() != 0;
+        restrictedAccountType = source.readString();
     }
 }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 0d463ee..da15e3b 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -45,7 +45,7 @@
 
     /**
      * This exception is thrown when a given package, application, or component
-     * name can not be found.
+     * name cannot be found.
      */
     public static class NameNotFoundException extends AndroidException {
         public NameNotFoundException() {
@@ -267,7 +267,7 @@
      * user has explicitly disabled the application, regardless of what it has
      * specified in its manifest.  Because this is due to the user's request,
      * they may re-enable it if desired through the appropriate system UI.  This
-     * option currently <strong>can not</strong> be used with
+     * option currently <strong>cannot</strong> be used with
      * {@link #setComponentEnabledSetting(ComponentName, int, int)}.
      */
     public static final int COMPONENT_ENABLED_STATE_DISABLED_USER = 3;
@@ -1145,6 +1145,29 @@
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports app widgets.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_APP_WIDGETS = "android.software.app_widgets";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports a home screen that is replaceable
+     * by third party applications.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_HOME_SCREEN = "android.software.home_screen";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports adding new input methods implemented
+     * with the {@link android.inputmethodservice.InputMethodService} API.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_INPUT_METHODS = "android.software.input_methods";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device supports WiFi (802.11) networking.
      */
     @SdkConstant(SdkConstantType.FEATURE)
@@ -1241,6 +1264,23 @@
             = "android.content.pm.extra.VERIFICATION_VERSION_CODE";
 
     /**
+     * The action used to request that the user approve a permission request
+     * from the application.
+     *
+     * @hide
+     */
+    public static final String ACTION_REQUEST_PERMISSION
+            = "android.content.pm.action.REQUEST_PERMISSION";
+
+    /**
+     * Extra field name for the list of permissions, which the user must approve.
+     *
+     * @hide
+     */
+    public static final String EXTRA_REQUEST_PERMISSION_PERMISSION_LIST
+            = "android.content.pm.extra.PERMISSION_LIST";
+
+    /**
      * Retrieve overall information about an application package that is
      * installed on the system.
      * <p>
@@ -1260,9 +1300,9 @@
      *         package. If flag GET_UNINSTALLED_PACKAGES is set and if the
      *         package is not found in the list of installed applications, the
      *         package information is retrieved from the list of uninstalled
-     *         applications(which includes installed applications as well as
-     *         applications with data directory ie applications which had been
-     *         deleted with DONT_DELTE_DATA flag set).
+     *         applications (which includes installed applications as well as
+     *         applications with data directory i.e. applications which had been
+     *         deleted with {@code DONT_DELETE_DATA} flag set).
      * @see #GET_ACTIVITIES
      * @see #GET_GIDS
      * @see #GET_CONFIGURATIONS
@@ -1303,7 +1343,7 @@
      * null if neither are found.
      *
      * <p>Throws {@link NameNotFoundException} if a package with the given
-     * name can not be found on the system.
+     * name cannot be found on the system.
      *
      * @param packageName The name of the package to inspect.
      *
@@ -1318,7 +1358,7 @@
      * assigned to a package.
      *
      * <p>Throws {@link NameNotFoundException} if a package with the given
-     * name can not be found on the system.
+     * name cannot be found on the system.
      *
      * @param packageName The full name (i.e. com.google.apps.contacts) of the
      *                    desired package.
@@ -1349,7 +1389,7 @@
      * Retrieve all of the information we know about a particular permission.
      *
      * <p>Throws {@link NameNotFoundException} if a permission with the given
-     * name can not be found on the system.
+     * name cannot be found on the system.
      *
      * @param name The fully qualified name (i.e. com.google.permission.LOGIN)
      *             of the permission you are interested in.
@@ -1385,7 +1425,7 @@
      * permissions.
      *
      * <p>Throws {@link NameNotFoundException} if a permission group with the given
-     * name can not be found on the system.
+     * name cannot be found on the system.
      *
      * @param name The fully qualified name (i.e. com.google.permission_group.APPS)
      *             of the permission you are interested in.
@@ -1414,7 +1454,7 @@
      * package/application.
      *
      * <p>Throws {@link NameNotFoundException} if an application with the given
-     * package name can not be found on the system.
+     * package name cannot be found on the system.
      *
      * @param packageName The full name (i.e. com.google.apps.contacts) of an
      *                    application.
@@ -1430,7 +1470,7 @@
      *         list of uninstalled applications(which includes
      *         installed applications as well as applications
      *         with data directory ie applications which had been
-     *         deleted with DONT_DELTE_DATA flag set).
+     *         deleted with {@code DONT_DELETE_DATA} flag set).
      *
      * @see #GET_META_DATA
      * @see #GET_SHARED_LIBRARY_FILES
@@ -1444,7 +1484,7 @@
      * class.
      *
      * <p>Throws {@link NameNotFoundException} if an activity with the given
-     * class name can not be found on the system.
+     * class name cannot be found on the system.
      *
      * @param component The full component name (i.e.
      * com.google.apps.contacts/com.google.apps.contacts.ContactsList) of an Activity
@@ -1467,7 +1507,7 @@
      * class.
      *
      * <p>Throws {@link NameNotFoundException} if a receiver with the given
-     * class name can not be found on the system.
+     * class name cannot be found on the system.
      *
      * @param component The full component name (i.e.
      * com.google.apps.calendar/com.google.apps.calendar.CalendarAlarm) of a Receiver
@@ -1490,7 +1530,7 @@
      * class.
      *
      * <p>Throws {@link NameNotFoundException} if a service with the given
-     * class name can not be found on the system.
+     * class name cannot be found on the system.
      *
      * @param component The full component name (i.e.
      * com.google.apps.media/com.google.apps.media.BackgroundPlayback) of a Service
@@ -1512,7 +1552,7 @@
      * provider class.
      *
      * <p>Throws {@link NameNotFoundException} if a provider with the given
-     * class name can not be found on the system.
+     * class name cannot be found on the system.
      *
      * @param component The full component name (i.e.
      * com.google.providers.media/com.google.providers.media.MediaProvider) of a
@@ -1549,7 +1589,7 @@
      *         installed on the device.  In the unlikely case of there being no
      *         installed packages, an empty list is returned.
      *         If flag GET_UNINSTALLED_PACKAGES is set, a list of all
-     *         applications including those deleted with DONT_DELETE_DATA
+     *         applications including those deleted with {@code DONT_DELETE_DATA}
      *         (partially installed apps with data directory) will be returned.
      *
      * @see #GET_ACTIVITIES
@@ -1619,7 +1659,7 @@
      *         installed on the device.  In the unlikely case of there being no
      *         installed packages, an empty list is returned.
      *         If flag GET_UNINSTALLED_PACKAGES is set, a list of all
-     *         applications including those deleted with DONT_DELETE_DATA
+     *         applications including those deleted with {@code DONT_DELETE_DATA}
      *         (partially installed apps with data directory) will be returned.
      *
      * @see #GET_ACTIVITIES
@@ -1712,6 +1752,29 @@
     public abstract void removePermission(String name);
 
     /**
+     * Returns an {@link Intent} suitable for passing to {@code startActivityForResult}
+     * which prompts the user to grant {@code permissions} to this application.
+     *
+     * @throws NullPointerException if {@code permissions} is {@code null}.
+     * @throws IllegalArgumentException if {@code permissions} contains {@code null}.
+     */
+    public Intent buildPermissionRequestIntent(String... permissions) {
+        if (permissions == null) {
+            throw new NullPointerException("permissions cannot be null");
+        }
+        for (String permission : permissions) {
+            if (permission == null) {
+                throw new IllegalArgumentException("permissions cannot contain null");
+            }
+        }
+
+        Intent i = new Intent(ACTION_REQUEST_PERMISSION);
+        i.putExtra(EXTRA_REQUEST_PERMISSION_PERMISSION_LIST, permissions);
+        i.setPackage("com.android.packageinstaller");
+        return i;
+    }
+
+    /**
      * Grant a permission to an application which the application does not
      * already have.  The permission must have been requested by the application,
      * but as an optional permission.  If the application is not allowed to
@@ -1824,7 +1887,7 @@
     /**
      * Return a List of all application packages that are installed on the
      * device. If flag GET_UNINSTALLED_PACKAGES has been set, a list of all
-     * applications including those deleted with DONT_DELETE_DATA (partially
+     * applications including those deleted with {@code DONT_DELETE_DATA} (partially
      * installed apps with data directory) will be returned.
      *
      * @param flags Additional option flags. Use any combination of
@@ -1835,7 +1898,7 @@
      *         is installed on the device.  In the unlikely case of there being
      *         no installed applications, an empty list is returned.
      *         If flag GET_UNINSTALLED_PACKAGES is set, a list of all
-     *         applications including those deleted with DONT_DELETE_DATA
+     *         applications including those deleted with {@code DONT_DELETE_DATA}
      *         (partially installed apps with data directory) will be returned.
      *
      * @see #GET_META_DATA
@@ -2141,7 +2204,7 @@
      * instrumentation class.
      *
      * <p>Throws {@link NameNotFoundException} if instrumentation with the
-     * given class name can not be found on the system.
+     * given class name cannot be found on the system.
      *
      * @param className The full name (i.e.
      *                  com.google.apps.contacts.InstrumentList) of an
@@ -2178,8 +2241,8 @@
      * icon.
      *
      * @param packageName The name of the package that this icon is coming from.
-     * Can not be null.
-     * @param resid The resource identifier of the desired image.  Can not be 0.
+     * Cannot be null.
+     * @param resid The resource identifier of the desired image.  Cannot be 0.
      * @param appInfo Overall information about <var>packageName</var>.  This
      * may be null, in which case the application information will be retrieved
      * for you if needed; if you already have this information around, it can
@@ -2195,7 +2258,7 @@
      * Retrieve the icon associated with an activity.  Given the full name of
      * an activity, retrieves the information about it and calls
      * {@link ComponentInfo#loadIcon ComponentInfo.loadIcon()} to return its icon.
-     * If the activity can not be found, NameNotFoundException is thrown.
+     * If the activity cannot be found, NameNotFoundException is thrown.
      *
      * @param activityName Name of the activity whose icon is to be retrieved.
      *
@@ -2214,7 +2277,7 @@
      * set, this simply returns the result of
      * getActivityIcon(intent.getClassName()).  Otherwise it resolves the intent's
      * component and returns the icon associated with the resolved component.
-     * If intent.getClassName() can not be found or the Intent can not be resolved
+     * If intent.getClassName() cannot be found or the Intent cannot be resolved
      * to a component, NameNotFoundException is thrown.
      *
      * @param intent The intent for which you would like to retrieve an icon.
@@ -2253,7 +2316,7 @@
     /**
      * Retrieve the icon associated with an application.  Given the name of the
      * application's package, retrieves the information about it and calls
-     * getApplicationIcon() to return its icon. If the application can not be
+     * getApplicationIcon() to return its icon. If the application cannot be
      * found, NameNotFoundException is thrown.
      *
      * @param packageName Name of the package whose application icon is to be
@@ -2273,7 +2336,7 @@
      * Retrieve the logo associated with an activity.  Given the full name of
      * an activity, retrieves the information about it and calls
      * {@link ComponentInfo#loadLogo ComponentInfo.loadLogo()} to return its logo.
-     * If the activity can not be found, NameNotFoundException is thrown.
+     * If the activity cannot be found, NameNotFoundException is thrown.
      *
      * @param activityName Name of the activity whose logo is to be retrieved.
      *
@@ -2293,7 +2356,7 @@
      * set, this simply returns the result of
      * getActivityLogo(intent.getClassName()).  Otherwise it resolves the intent's
      * component and returns the logo associated with the resolved component.
-     * If intent.getClassName() can not be found or the Intent can not be resolved
+     * If intent.getClassName() cannot be found or the Intent cannot be resolved
      * to a component, NameNotFoundException is thrown.
      *
      * @param intent The intent for which you would like to retrieve a logo.
@@ -2325,7 +2388,7 @@
     /**
      * Retrieve the logo associated with an application.  Given the name of the
      * application's package, retrieves the information about it and calls
-     * getApplicationLogo() to return its logo. If the application can not be
+     * getApplicationLogo() to return its logo. If the application cannot be
      * found, NameNotFoundException is thrown.
      *
      * @param packageName Name of the package whose application logo is to be
@@ -2349,8 +2412,8 @@
      * labels and other text.
      *
      * @param packageName The name of the package that this text is coming from.
-     * Can not be null.
-     * @param resid The resource identifier of the desired text.  Can not be 0.
+     * Cannot be null.
+     * @param resid The resource identifier of the desired text.  Cannot be 0.
      * @param appInfo Overall information about <var>packageName</var>.  This
      * may be null, in which case the application information will be retrieved
      * for you if needed; if you already have this information around, it can
@@ -2367,8 +2430,8 @@
      * retrieve XML meta data.
      *
      * @param packageName The name of the package that this xml is coming from.
-     * Can not be null.
-     * @param resid The resource identifier of the desired xml.  Can not be 0.
+     * Cannot be null.
+     * @param resid The resource identifier of the desired xml.  Cannot be 0.
      * @param appInfo Overall information about <var>packageName</var>.  This
      * may be null, in which case the application information will be retrieved
      * for you if needed; if you already have this information around, it can
@@ -2386,7 +2449,7 @@
      *
      * @return Returns the label associated with this application, or null if
      * it could not be found for any reason.
-     * @param info The application to get the label of
+     * @param info The application to get the label of.
      */
     public abstract CharSequence getApplicationLabel(ApplicationInfo info);
 
@@ -2394,7 +2457,7 @@
      * Retrieve the resources associated with an activity.  Given the full
      * name of an activity, retrieves the information about it and calls
      * getResources() to return its application's resources.  If the activity
-     * can not be found, NameNotFoundException is thrown.
+     * cannot be found, NameNotFoundException is thrown.
      *
      * @param activityName Name of the activity whose resources are to be
      *                     retrieved.
@@ -2425,7 +2488,7 @@
      * Retrieve the resources associated with an application.  Given the full
      * package name of an application, retrieves the information about it and
      * calls getResources() to return its application's resources.  If the
-     * appPackageName can not be found, NameNotFoundException is thrown.
+     * appPackageName cannot be found, NameNotFoundException is thrown.
      *
      * @param appPackageName Package name of the application whose resources
      *                       are to be retrieved.
@@ -2594,7 +2657,7 @@
      * {@link PackageManager#VERIFICATION_REJECT}.
      *
      * @param id pending package identifier as passed via the
-     *            {@link PackageManager#EXTRA_VERIFICATION_ID} Intent extra
+     *            {@link PackageManager#EXTRA_VERIFICATION_ID} Intent extra.
      * @param verificationCode either {@link PackageManager#VERIFICATION_ALLOW}
      *            or {@link PackageManager#VERIFICATION_REJECT}.
      * @throws SecurityException if the caller does not have the
@@ -2615,7 +2678,7 @@
      * will have no effect.
      *
      * @param id pending package identifier as passed via the
-     *            {@link PackageManager#EXTRA_VERIFICATION_ID} Intent extra
+     *            {@link PackageManager#EXTRA_VERIFICATION_ID} Intent extra.
      * @param verificationCodeAtTimeout either
      *            {@link PackageManager#VERIFICATION_ALLOW} or
      *            {@link PackageManager#VERIFICATION_REJECT}. If
@@ -2799,16 +2862,16 @@
 
     /**
      * @deprecated This function no longer does anything; it was an old
-     * approach to managing preferred activities, which has been superceeded
-     * (and conflicts with) the modern activity-based preferences.
+     * approach to managing preferred activities, which has been superseded
+     * by (and conflicts with) the modern activity-based preferences.
      */
     @Deprecated
     public abstract void addPackageToPreferred(String packageName);
 
     /**
      * @deprecated This function no longer does anything; it was an old
-     * approach to managing preferred activities, which has been superceeded
-     * (and conflicts with) the modern activity-based preferences.
+     * approach to managing preferred activities, which has been superseded
+     * by (and conflicts with) the modern activity-based preferences.
      */
     @Deprecated
     public abstract void removePackageFromPreferred(String packageName);
@@ -2847,7 +2910,7 @@
     /**
      * @deprecated This is a protected API that should not have been available
      * to third party applications.  It is the platform's responsibility for
-     * assigning preferred activities and this can not be directly modified.
+     * assigning preferred activities and this cannot be directly modified.
      *
      * Add a new preferred activity mapping to the system.  This will be used
      * to automatically select the given activity component when
@@ -2881,7 +2944,7 @@
     /**
      * @deprecated This is a protected API that should not have been available
      * to third party applications.  It is the platform's responsibility for
-     * assigning preferred activities and this can not be directly modified.
+     * assigning preferred activities and this cannot be directly modified.
      *
      * Replaces an existing preferred activity mapping to the system, and if that were not present
      * adds a new preferred activity.  This will be used
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 149b8e5..11f9be9 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -290,6 +290,7 @@
         pi.applicationInfo = generateApplicationInfo(p, flags, state, userId);
         pi.installLocation = p.installLocation;
         pi.requiredForAllUsers = p.mRequiredForAllUsers;
+        pi.restrictedAccountType = p.mRestrictedAccountType;
         pi.firstInstallTime = firstInstallTime;
         pi.lastUpdateTime = lastUpdateTime;
         if ((flags&PackageManager.GET_GIDS) != 0) {
@@ -1023,16 +1024,14 @@
                 // that may change.
                 String name = sa.getNonResourceString(
                         com.android.internal.R.styleable.AndroidManifestUsesPermission_name);
-                /* Not supporting optional permissions yet.
                 boolean required = sa.getBoolean(
                         com.android.internal.R.styleable.AndroidManifestUsesPermission_required, true);
-                */
 
                 sa.recycle();
 
                 if (name != null && !pkg.requestedPermissions.contains(name)) {
                     pkg.requestedPermissions.add(name.intern());
-                    pkg.requestedPermissionsRequired.add(Boolean.TRUE);
+                    pkg.requestedPermissionsRequired.add(required ? Boolean.TRUE : Boolean.FALSE);
                 }
 
                 XmlUtils.skipCurrentTag(parser);
@@ -1766,6 +1765,11 @@
                     false)) {
                 owner.mRequiredForAllUsers = true;
             }
+            String accountType = sa.getString(com.android.internal.R.styleable
+                    .AndroidManifestApplication_restrictedAccountType);
+            if (accountType != null && accountType.length() > 0) {
+                owner.mRestrictedAccountType = accountType;
+            }
         }
 
         if (sa.getBoolean(
@@ -3193,6 +3197,7 @@
     }
 
     public final static class Package {
+
         public String packageName;
 
         // For now we only support one application per package.
@@ -3280,6 +3285,9 @@
         /* An app that's required for all users and cannot be uninstalled for a user */
         public boolean mRequiredForAllUsers;
 
+        /* The restricted account authenticator type that is used by this application */
+        public String mRestrictedAccountType;
+
         /**
          * Digest suitable for comparing whether this package's manifest is the
          * same as another.
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index 3579977..dcd54fc 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -30,6 +30,8 @@
     public boolean installed;
     public int enabled;
 
+    public String lastDisableAppCaller;
+
     public HashSet<String> disabledComponents;
     public HashSet<String> enabledComponents;
 
@@ -43,6 +45,7 @@
         stopped = o.stopped;
         notLaunched = o.notLaunched;
         enabled = o.enabled;
+        lastDisableAppCaller = o.lastDisableAppCaller;
         disabledComponents = o.disabledComponents != null
                 ? new HashSet<String>(o.disabledComponents) : null;
         enabledComponents = o.enabledComponents != null
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index c0d2fae..7f94794 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -572,7 +572,10 @@
      *        are received faster. The value must be one of
      *        {@link #SENSOR_DELAY_NORMAL}, {@link #SENSOR_DELAY_UI},
      *        {@link #SENSOR_DELAY_GAME}, or {@link #SENSOR_DELAY_FASTEST}
-     *        or, the desired delay between events in microsecond.
+     *        or, the desired delay between events in microseconds.
+     *        Specifying the delay in microseconds only works from Android
+     *        2.3 (API level 9) onwards. For earlier releases, you must use
+     *        one of the {@code SENSOR_DELAY_*} constants.
      *
      * @return <code>true</code> if the sensor is supported and successfully
      *         enabled.
@@ -604,7 +607,10 @@
      *        are received faster. The value must be one of
      *        {@link #SENSOR_DELAY_NORMAL}, {@link #SENSOR_DELAY_UI},
      *        {@link #SENSOR_DELAY_GAME}, or {@link #SENSOR_DELAY_FASTEST}.
-     *        or, the desired delay between events in microsecond.
+     *        or, the desired delay between events in microseconds.
+     *        Specifying the delay in microseconds only works from Android
+     *        2.3 (API level 9) onwards. For earlier releases, you must use
+     *        one of the {@code SENSOR_DELAY_*} constants.
      *
      * @param handler
      *        The {@link android.os.Handler Handler} the
diff --git a/core/java/android/hardware/usb/UsbDeviceConnection.java b/core/java/android/hardware/usb/UsbDeviceConnection.java
index b536490..0856e27 100644
--- a/core/java/android/hardware/usb/UsbDeviceConnection.java
+++ b/core/java/android/hardware/usb/UsbDeviceConnection.java
@@ -17,7 +17,6 @@
 package android.hardware.usb;
 
 import android.os.ParcelFileDescriptor;
-import android.util.Log;
 
 import java.io.FileDescriptor;
 
@@ -119,10 +118,41 @@
      * @param timeout in milliseconds
      * @return length of data transferred (or zero) for success,
      * or negative value for failure
+     *
+     * @deprecated Use {@link #controlTransfer(int, int, int, int, byte[], int, int, int)}
+     * which accepts a buffer start index.
      */
+    @Deprecated
     public int controlTransfer(int requestType, int request, int value,
             int index, byte[] buffer, int length, int timeout) {
-        return native_control_request(requestType, request, value, index, buffer, length, timeout);
+        return controlTransfer(requestType, request, value, index, buffer, 0, length, timeout);
+    }
+
+    /**
+     * Performs a control transaction on endpoint zero for this device.
+     * The direction of the transfer is determined by the request type.
+     * If requestType & {@link UsbConstants#USB_ENDPOINT_DIR_MASK} is
+     * {@link UsbConstants#USB_DIR_OUT}, then the transfer is a write,
+     * and if it is {@link UsbConstants#USB_DIR_IN}, then the transfer
+     * is a read.
+     *
+     * @param requestType request type for this transaction
+     * @param request request ID for this transaction
+     * @param value value field for this transaction
+     * @param index index field for this transaction
+     * @param buffer buffer for data portion of transaction,
+     * or null if no data needs to be sent or received
+     * @param start the index of the first byte in the buffer to send or receive
+     * @param length the length of the data to send or receive
+     * @param timeout in milliseconds
+     * @return length of data transferred (or zero) for success,
+     * or negative value for failure
+     */
+    public int controlTransfer(int requestType, int request, int value, int index,
+            byte[] buffer, int start, int length, int timeout) {
+        checkBounds(buffer, start, length);
+        return native_control_request(requestType, request, value, index,
+                buffer, start, length, timeout);
     }
 
     /**
@@ -130,14 +160,37 @@
      * The direction of the transfer is determined by the direction of the endpoint
      *
      * @param endpoint the endpoint for this transaction
-     * @param buffer buffer for data to send or receive,
+     * @param buffer buffer for data to send or receive
+     * @param length the length of the data to send or receive
+     * @param timeout in milliseconds
+     * @return length of data transferred (or zero) for success,
+     * or negative value for failure
+     *
+     * @deprecated Use {@link #bulkTransfer(UsbEndpoint, byte[], int, int, int)}
+     * which accepts a buffer start index.
+     */
+    @Deprecated
+    public int bulkTransfer(UsbEndpoint endpoint,
+            byte[] buffer, int length, int timeout) {
+        return bulkTransfer(endpoint, buffer, 0, length, timeout);
+    }
+
+    /**
+     * Performs a bulk transaction on the given endpoint.
+     * The direction of the transfer is determined by the direction of the endpoint
+     *
+     * @param endpoint the endpoint for this transaction
+     * @param buffer buffer for data to send or receive
+     * @param start the index of the first byte in the buffer to send or receive
      * @param length the length of the data to send or receive
      * @param timeout in milliseconds
      * @return length of data transferred (or zero) for success,
      * or negative value for failure
      */
-    public int bulkTransfer(UsbEndpoint endpoint, byte[] buffer, int length, int timeout) {
-        return native_bulk_request(endpoint.getAddress(), buffer, length, timeout);
+    public int bulkTransfer(UsbEndpoint endpoint,
+            byte[] buffer, int start, int length, int timeout) {
+        checkBounds(buffer, start, length);
+        return native_bulk_request(endpoint.getAddress(), buffer, start, length, timeout);
     }
 
     /**
@@ -168,6 +221,13 @@
         return native_get_serial();
     }
 
+    private static void checkBounds(byte[] buffer, int start, int length) {
+        final int bufferLength = (buffer != null ? buffer.length : 0);
+        if (start < 0 || start + length > bufferLength) {
+            throw new IllegalArgumentException("Buffer start or length out of bounds.");
+        }
+    }
+
     private native boolean native_open(String deviceName, FileDescriptor pfd);
     private native void native_close();
     private native int native_get_fd();
@@ -175,8 +235,9 @@
     private native boolean native_claim_interface(int interfaceID, boolean force);
     private native boolean native_release_interface(int interfaceID);
     private native int native_control_request(int requestType, int request, int value,
-            int index, byte[] buffer, int length, int timeout);
-    private native int native_bulk_request(int endpoint, byte[] buffer, int length, int timeout);
+            int index, byte[] buffer, int start, int length, int timeout);
+    private native int native_bulk_request(int endpoint, byte[] buffer,
+            int start, int length, int timeout);
     private native UsbRequest native_request_wait();
     private native String native_get_serial();
 }
diff --git a/core/java/android/inputmethodservice/AbstractInputMethodService.java b/core/java/android/inputmethodservice/AbstractInputMethodService.java
index 3c3182a..3531926 100644
--- a/core/java/android/inputmethodservice/AbstractInputMethodService.java
+++ b/core/java/android/inputmethodservice/AbstractInputMethodService.java
@@ -126,11 +126,12 @@
             mRevoked = true;
             mEnabled = false;
         }
-        
+
         /**
          * Take care of dispatching incoming key events to the appropriate
          * callbacks on the service, and tell the client when this is done.
          */
+        @Override
         public void dispatchKeyEvent(int seq, KeyEvent event, EventCallback callback) {
             boolean handled = event.dispatch(AbstractInputMethodService.this,
                     mDispatcherState, this);
@@ -143,6 +144,7 @@
          * Take care of dispatching incoming trackball events to the appropriate
          * callbacks on the service, and tell the client when this is done.
          */
+        @Override
         public void dispatchTrackballEvent(int seq, MotionEvent event, EventCallback callback) {
             boolean handled = onTrackballEvent(event);
             if (callback != null) {
@@ -154,6 +156,7 @@
          * Take care of dispatching incoming generic motion events to the appropriate
          * callbacks on the service, and tell the client when this is done.
          */
+        @Override
         public void dispatchGenericMotionEvent(int seq, MotionEvent event, EventCallback callback) {
             boolean handled = onGenericMotionEvent(event);
             if (callback != null) {
diff --git a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
index d78262b..726dcec 100644
--- a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
@@ -18,15 +18,20 @@
 
 import com.android.internal.os.HandlerCaller;
 import com.android.internal.os.SomeArgs;
-import com.android.internal.view.IInputMethodCallback;
 import com.android.internal.view.IInputMethodSession;
 
 import android.content.Context;
 import android.graphics.Rect;
 import android.os.Bundle;
+import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
 import android.util.Log;
+import android.util.SparseArray;
+import android.view.InputChannel;
+import android.view.InputDevice;
+import android.view.InputEvent;
+import android.view.InputEventReceiver;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.inputmethod.CompletionInfo;
@@ -36,14 +41,10 @@
 class IInputMethodSessionWrapper extends IInputMethodSession.Stub
         implements HandlerCaller.Callback {
     private static final String TAG = "InputMethodWrapper";
-    private static final boolean DEBUG = false;
     
     private static final int DO_FINISH_INPUT = 60;
     private static final int DO_DISPLAY_COMPLETIONS = 65;
     private static final int DO_UPDATE_EXTRACTED_TEXT = 67;
-    private static final int DO_DISPATCH_KEY_EVENT = 70;
-    private static final int DO_DISPATCH_TRACKBALL_EVENT = 80;
-    private static final int DO_DISPATCH_GENERIC_MOTION_EVENT = 85;
     private static final int DO_UPDATE_SELECTION = 90;
     private static final int DO_UPDATE_CURSOR = 95;
     private static final int DO_APP_PRIVATE_COMMAND = 100;
@@ -53,34 +54,30 @@
 
     HandlerCaller mCaller;
     InputMethodSession mInputMethodSession;
-    
-    // NOTE: we should have a cache of these.
-    static class InputMethodEventCallbackWrapper implements InputMethodSession.EventCallback {
-        final IInputMethodCallback mCb;
-        InputMethodEventCallbackWrapper(IInputMethodCallback cb) {
-            mCb = cb;
-        }
-        public void finishedEvent(int seq, boolean handled) {
-            try {
-                mCb.finishedEvent(seq, handled);
-            } catch (RemoteException e) {
-            }
-        }
-    }
-    
+    InputChannel mChannel;
+    ImeInputEventReceiver mReceiver;
+
     public IInputMethodSessionWrapper(Context context,
-            InputMethodSession inputMethodSession) {
+            InputMethodSession inputMethodSession, InputChannel channel) {
         mCaller = new HandlerCaller(context, null,
                 this, true /*asyncHandler*/);
         mInputMethodSession = inputMethodSession;
+        mChannel = channel;
+        if (channel != null) {
+            mReceiver = new ImeInputEventReceiver(channel, context.getMainLooper());
+        }
     }
 
     public InputMethodSession getInternalInputMethodSession() {
         return mInputMethodSession;
     }
 
+    @Override
     public void executeMessage(Message msg) {
-        if (mInputMethodSession == null) return;
+        if (mInputMethodSession == null) {
+            // The session has been finished.
+            return;
+        }
 
         switch (msg.what) {
             case DO_FINISH_INPUT:
@@ -93,33 +90,6 @@
                 mInputMethodSession.updateExtractedText(msg.arg1,
                         (ExtractedText)msg.obj);
                 return;
-            case DO_DISPATCH_KEY_EVENT: {
-                SomeArgs args = (SomeArgs)msg.obj;
-                mInputMethodSession.dispatchKeyEvent(msg.arg1,
-                        (KeyEvent)args.arg1,
-                        new InputMethodEventCallbackWrapper(
-                                (IInputMethodCallback)args.arg2));
-                args.recycle();
-                return;
-            }
-            case DO_DISPATCH_TRACKBALL_EVENT: {
-                SomeArgs args = (SomeArgs)msg.obj;
-                mInputMethodSession.dispatchTrackballEvent(msg.arg1,
-                        (MotionEvent)args.arg1,
-                        new InputMethodEventCallbackWrapper(
-                                (IInputMethodCallback)args.arg2));
-                args.recycle();
-                return;
-            }
-            case DO_DISPATCH_GENERIC_MOTION_EVENT: {
-                SomeArgs args = (SomeArgs)msg.obj;
-                mInputMethodSession.dispatchGenericMotionEvent(msg.arg1,
-                        (MotionEvent)args.arg1,
-                        new InputMethodEventCallbackWrapper(
-                                (IInputMethodCallback)args.arg2));
-                args.recycle();
-                return;
-            }
             case DO_UPDATE_SELECTION: {
                 SomeArgs args = (SomeArgs)msg.obj;
                 mInputMethodSession.updateSelection(args.argi1, args.argi2,
@@ -143,7 +113,7 @@
                 return;
             }
             case DO_FINISH_SESSION: {
-                mInputMethodSession = null;
+                doFinishSession();
                 return;
             }
             case DO_VIEW_CLICKED: {
@@ -153,37 +123,37 @@
         }
         Log.w(TAG, "Unhandled message code: " + msg.what);
     }
-    
+
+    private void doFinishSession() {
+        mInputMethodSession = null;
+        if (mReceiver != null) {
+            mReceiver.dispose();
+            mReceiver = null;
+        }
+        if (mChannel != null) {
+            mChannel.dispose();
+            mChannel = null;
+        }
+    }
+
+    @Override
     public void finishInput() {
         mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_FINISH_INPUT));
     }
 
+    @Override
     public void displayCompletions(CompletionInfo[] completions) {
         mCaller.executeOrSendMessage(mCaller.obtainMessageO(
                 DO_DISPLAY_COMPLETIONS, completions));
     }
-    
+
+    @Override
     public void updateExtractedText(int token, ExtractedText text) {
         mCaller.executeOrSendMessage(mCaller.obtainMessageIO(
                 DO_UPDATE_EXTRACTED_TEXT, token, text));
     }
-    
-    public void dispatchKeyEvent(int seq, KeyEvent event, IInputMethodCallback callback) {
-        mCaller.executeOrSendMessage(mCaller.obtainMessageIOO(DO_DISPATCH_KEY_EVENT, seq,
-                event, callback));
-    }
 
-    public void dispatchTrackballEvent(int seq, MotionEvent event, IInputMethodCallback callback) {
-        mCaller.executeOrSendMessage(mCaller.obtainMessageIOO(DO_DISPATCH_TRACKBALL_EVENT, seq,
-                event, callback));
-    }
-
-    public void dispatchGenericMotionEvent(int seq, MotionEvent event,
-            IInputMethodCallback callback) {
-        mCaller.executeOrSendMessage(mCaller.obtainMessageIOO(DO_DISPATCH_GENERIC_MOTION_EVENT, seq,
-                event, callback));
-    }
-
+    @Override
     public void updateSelection(int oldSelStart, int oldSelEnd,
             int newSelStart, int newSelEnd, int candidatesStart, int candidatesEnd) {
         mCaller.executeOrSendMessage(mCaller.obtainMessageIIIIII(DO_UPDATE_SELECTION,
@@ -191,24 +161,74 @@
                 candidatesStart, candidatesEnd));
     }
 
+    @Override
     public void viewClicked(boolean focusChanged) {
-        mCaller.executeOrSendMessage(mCaller.obtainMessageI(DO_VIEW_CLICKED, focusChanged ? 1 : 0));
+        mCaller.executeOrSendMessage(
+                mCaller.obtainMessageI(DO_VIEW_CLICKED, focusChanged ? 1 : 0));
     }
 
+    @Override
     public void updateCursor(Rect newCursor) {
-        mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_UPDATE_CURSOR,
-                newCursor));
-    }
-    
-    public void appPrivateCommand(String action, Bundle data) {
-        mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_APP_PRIVATE_COMMAND, action, data));
-    }
-    
-    public void toggleSoftInput(int showFlags, int hideFlags) {
-        mCaller.executeOrSendMessage(mCaller.obtainMessageII(DO_TOGGLE_SOFT_INPUT, showFlags, hideFlags));
+        mCaller.executeOrSendMessage(
+                mCaller.obtainMessageO(DO_UPDATE_CURSOR, newCursor));
     }
 
+    @Override
+    public void appPrivateCommand(String action, Bundle data) {
+        mCaller.executeOrSendMessage(
+                mCaller.obtainMessageOO(DO_APP_PRIVATE_COMMAND, action, data));
+    }
+
+    @Override
+    public void toggleSoftInput(int showFlags, int hideFlags) {
+        mCaller.executeOrSendMessage(
+                mCaller.obtainMessageII(DO_TOGGLE_SOFT_INPUT, showFlags, hideFlags));
+    }
+
+    @Override
     public void finishSession() {
         mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_FINISH_SESSION));
     }
+
+    private final class ImeInputEventReceiver extends InputEventReceiver
+            implements InputMethodSession.EventCallback {
+        private final SparseArray<InputEvent> mPendingEvents = new SparseArray<InputEvent>();
+
+        public ImeInputEventReceiver(InputChannel inputChannel, Looper looper) {
+            super(inputChannel, looper);
+        }
+
+        @Override
+        public void onInputEvent(InputEvent event) {
+            if (mInputMethodSession == null) {
+                // The session has been finished.
+                finishInputEvent(event, false);
+                return;
+            }
+
+            final int seq = event.getSequenceNumber();
+            mPendingEvents.put(seq, event);
+            if (event instanceof KeyEvent) {
+                KeyEvent keyEvent = (KeyEvent)event;
+                mInputMethodSession.dispatchKeyEvent(seq, keyEvent, this);
+            } else {
+                MotionEvent motionEvent = (MotionEvent)event;
+                if (motionEvent.isFromSource(InputDevice.SOURCE_CLASS_TRACKBALL)) {
+                    mInputMethodSession.dispatchTrackballEvent(seq, motionEvent, this);
+                } else {
+                    mInputMethodSession.dispatchGenericMotionEvent(seq, motionEvent, this);
+                }
+            }
+        }
+
+        @Override
+        public void finishedEvent(int seq, boolean handled) {
+            int index = mPendingEvents.indexOfKey(seq);
+            if (index >= 0) {
+                InputEvent event = mPendingEvents.valueAt(index);
+                mPendingEvents.removeAt(index);
+                finishInputEvent(event, handled);
+            }
+        }
+    }
 }
diff --git a/core/java/android/inputmethodservice/IInputMethodWrapper.java b/core/java/android/inputmethodservice/IInputMethodWrapper.java
index 0d981be..9306373 100644
--- a/core/java/android/inputmethodservice/IInputMethodWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodWrapper.java
@@ -32,6 +32,7 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.util.Log;
+import android.view.InputChannel;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputBinding;
 import android.view.inputmethod.InputConnection;
@@ -53,7 +54,6 @@
 class IInputMethodWrapper extends IInputMethod.Stub
         implements HandlerCaller.Callback {
     private static final String TAG = "InputMethodWrapper";
-    private static final boolean DEBUG = false;
 
     private static final int DO_DUMP = 1;
     private static final int DO_ATTACH_TOKEN = 10;
@@ -78,20 +78,29 @@
     }
     
     // NOTE: we should have a cache of these.
-    static class InputMethodSessionCallbackWrapper implements InputMethod.SessionCallback {
+    static final class InputMethodSessionCallbackWrapper implements InputMethod.SessionCallback {
         final Context mContext;
+        final InputChannel mChannel;
         final IInputSessionCallback mCb;
-        InputMethodSessionCallbackWrapper(Context context, IInputSessionCallback cb) {
+
+        InputMethodSessionCallbackWrapper(Context context, InputChannel channel,
+                IInputSessionCallback cb) {
             mContext = context;
+            mChannel = channel;
             mCb = cb;
         }
+
+        @Override
         public void sessionCreated(InputMethodSession session) {
             try {
                 if (session != null) {
                     IInputMethodSessionWrapper wrap =
-                            new IInputMethodSessionWrapper(mContext, session);
+                            new IInputMethodSessionWrapper(mContext, session, mChannel);
                     mCb.sessionCreated(wrap);
                 } else {
+                    if (mChannel != null) {
+                        mChannel.dispose();
+                    }
                     mCb.sessionCreated(null);
                 }
             } catch (RemoteException e) {
@@ -112,6 +121,7 @@
         return mInputMethod.get();
     }
 
+    @Override
     public void executeMessage(Message msg) {
         InputMethod inputMethod = mInputMethod.get();
         // Need a valid reference to the inputMethod for everything except a dump.
@@ -174,8 +184,11 @@
                 return;
             }
             case DO_CREATE_SESSION: {
+                SomeArgs args = (SomeArgs)msg.obj;
                 inputMethod.createSession(new InputMethodSessionCallbackWrapper(
-                        mCaller.mContext, (IInputSessionCallback)msg.obj));
+                        mCaller.mContext, (InputChannel)args.arg1,
+                        (IInputSessionCallback)args.arg2));
+                args.recycle();
                 return;
             }
             case DO_SET_SESSION_ENABLED:
@@ -197,8 +210,9 @@
         }
         Log.w(TAG, "Unhandled message code: " + msg.what);
     }
-    
-    @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+
+    @Override
+    protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
         AbstractInputMethodService target = mTarget.get();
         if (target == null) {
             return;
@@ -224,10 +238,12 @@
         }
     }
 
+    @Override
     public void attachToken(IBinder token) {
         mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_ATTACH_TOKEN, token));
     }
-    
+
+    @Override
     public void bindInput(InputBinding binding) {
         InputConnection ic = new InputConnectionWrapper(
                 IInputContext.Stub.asInterface(binding.getConnectionToken()));
@@ -235,24 +251,30 @@
         mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_SET_INPUT_CONTEXT, nu));
     }
 
+    @Override
     public void unbindInput() {
         mCaller.executeOrSendMessage(mCaller.obtainMessage(DO_UNSET_INPUT_CONTEXT));
     }
 
+    @Override
     public void startInput(IInputContext inputContext, EditorInfo attribute) {
         mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_START_INPUT,
                 inputContext, attribute));
     }
 
+    @Override
     public void restartInput(IInputContext inputContext, EditorInfo attribute) {
         mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_RESTART_INPUT,
                 inputContext, attribute));
     }
 
-    public void createSession(IInputSessionCallback callback) {
-        mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_CREATE_SESSION, callback));
+    @Override
+    public void createSession(InputChannel channel, IInputSessionCallback callback) {
+        mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_CREATE_SESSION,
+                channel, callback));
     }
 
+    @Override
     public void setSessionEnabled(IInputMethodSession session, boolean enabled) {
         try {
             InputMethodSession ls = ((IInputMethodSessionWrapper)
@@ -263,7 +285,8 @@
             Log.w(TAG, "Incoming session not of correct type: " + session, e);
         }
     }
-    
+
+    @Override
     public void revokeSession(IInputMethodSession session) {
         try {
             InputMethodSession ls = ((IInputMethodSessionWrapper)
@@ -273,17 +296,20 @@
             Log.w(TAG, "Incoming session not of correct type: " + session, e);
         }
     }
-    
+
+    @Override
     public void showSoftInput(int flags, ResultReceiver resultReceiver) {
         mCaller.executeOrSendMessage(mCaller.obtainMessageIO(DO_SHOW_SOFT_INPUT,
                 flags, resultReceiver));
     }
-    
+
+    @Override
     public void hideSoftInput(int flags, ResultReceiver resultReceiver) {
         mCaller.executeOrSendMessage(mCaller.obtainMessageIO(DO_HIDE_SOFT_INPUT,
                 flags, resultReceiver));
     }
 
+    @Override
     public void changeInputMethodSubtype(InputMethodSubtype subtype) {
         mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_CHANGE_INPUTMETHOD_SUBTYPE,
                 subtype));
diff --git a/core/java/android/net/BaseNetworkStateTracker.java b/core/java/android/net/BaseNetworkStateTracker.java
index a554611..1165281 100644
--- a/core/java/android/net/BaseNetworkStateTracker.java
+++ b/core/java/android/net/BaseNetworkStateTracker.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.os.Handler;
+import android.os.Messenger;
 
 import com.android.internal.util.Preconditions;
 
@@ -165,4 +166,9 @@
     public void removeStackedLink(LinkProperties link) {
         mLinkProperties.removeStackedLink(link);
     }
+
+    @Override
+    public void supplyMessenger(Messenger messenger) {
+        // not supported on this network
+    }
 }
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 3a04c27..4e4980d 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -23,6 +23,7 @@
 import android.content.Context;
 import android.os.Binder;
 import android.os.Build.VERSION_CODES;
+import android.os.Messenger;
 import android.os.RemoteException;
 import android.provider.Settings;
 
@@ -1280,4 +1281,17 @@
         }
     }
 
+    /**
+     * Supply the backend messenger for a network tracker
+     *
+     * @param type NetworkType to set
+     * @param messenger {@link Messenger}
+     * {@hide}
+     */
+    public void supplyMessenger(int networkType, Messenger messenger) {
+        try {
+            mService.supplyMessenger(networkType, messenger);
+        } catch (RemoteException e) {
+        }
+    }
 }
diff --git a/core/java/android/net/DummyDataStateTracker.java b/core/java/android/net/DummyDataStateTracker.java
index db8f0bc..15a81f3 100644
--- a/core/java/android/net/DummyDataStateTracker.java
+++ b/core/java/android/net/DummyDataStateTracker.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.os.Handler;
 import android.os.Message;
+import android.os.Messenger;
 import android.util.Slog;
 
 /**
@@ -213,6 +214,11 @@
         mLinkProperties.removeStackedLink(link);
     }
 
+    @Override
+    public void supplyMessenger(Messenger messenger) {
+        // not supported on this network
+    }
+
     static private void log(String s) {
         Slog.d(TAG, s);
     }
diff --git a/core/java/android/net/EthernetDataTracker.java b/core/java/android/net/EthernetDataTracker.java
index b744a47..27d5a58 100644
--- a/core/java/android/net/EthernetDataTracker.java
+++ b/core/java/android/net/EthernetDataTracker.java
@@ -22,6 +22,7 @@
 import android.os.IBinder;
 import android.os.INetworkManagementService;
 import android.os.Message;
+import android.os.Messenger;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.util.Log;
@@ -417,4 +418,9 @@
     public void removeStackedLink(LinkProperties link) {
         mLinkProperties.removeStackedLink(link);
     }
+
+    @Override
+    public void supplyMessenger(Messenger messenger) {
+        // not supported on this network
+    }
 }
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 056fa03..9e9b43d 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -22,6 +22,7 @@
 import android.net.NetworkState;
 import android.net.ProxyProperties;
 import android.os.IBinder;
+import android.os.Messenger;
 import android.os.ParcelFileDescriptor;
 
 import com.android.internal.net.LegacyVpnInfo;
@@ -126,4 +127,6 @@
     boolean updateLockdownVpn();
 
     void captivePortalCheckComplete(in NetworkInfo info);
+
+    void supplyMessenger(int networkType, in Messenger messenger);
 }
diff --git a/core/java/android/net/LinkCapabilities.java b/core/java/android/net/LinkCapabilities.java
index eb9166f..fb444ea 100644
--- a/core/java/android/net/LinkCapabilities.java
+++ b/core/java/android/net/LinkCapabilities.java
@@ -314,8 +314,8 @@
             sb.append(":\"");
             sb.append(entry.getValue());
             sb.append("\"");
-            return mCapabilities.toString();
         }
+        sb.append("}");
         return sb.toString();
     }
 
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index e522754..52b238f 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -121,7 +121,7 @@
 
     public Collection<String> getAllInterfaceNames() {
         Collection interfaceNames = new ArrayList<String>(mStackedLinks.size() + 1);
-        interfaceNames.add(new String(mIfaceName));
+        if (mIfaceName != null) interfaceNames.add(new String(mIfaceName));
         for (LinkProperties stacked: mStackedLinks.values()) {
             interfaceNames.addAll(stacked.getAllInterfaceNames());
         }
@@ -290,7 +290,7 @@
             }
             stacked += "] ";
         }
-        return ifaceName + linkAddresses + routes + dns + domainName + proxy + stacked;
+        return "{" + ifaceName + linkAddresses + routes + dns + domainName + proxy + stacked + "}";
     }
 
     /**
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index faf739b..e85dbcd 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -74,7 +74,6 @@
 
     private Handler mHandler;
     private AsyncChannel mDataConnectionTrackerAc;
-    private Messenger mMessenger;
 
     /**
      * Create a new MobileDataStateTracker
@@ -103,7 +102,6 @@
         IntentFilter filter = new IntentFilter();
         filter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
         filter.addAction(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED);
-        filter.addAction(DctConstants.ACTION_DATA_CONNECTION_TRACKER_MESSENGER);
 
         mContext.registerReceiver(new MobileDataStateReceiver(), filter);
         mMobileDataState = PhoneConstants.DataState.DISCONNECTED;
@@ -285,13 +283,6 @@
                                 " broadcast" + reason == null ? "" : "(" + reason + ")");
                 }
                 setDetailedState(DetailedState.FAILED, reason, apnName);
-            } else if (intent.getAction().equals(DctConstants
-                    .ACTION_DATA_CONNECTION_TRACKER_MESSENGER)) {
-                if (VDBG) log(mApnType + " got ACTION_DATA_CONNECTION_TRACKER_MESSENGER");
-                mMessenger =
-                    intent.getParcelableExtra(DctConstants.EXTRA_MESSENGER);
-                AsyncChannel ac = new AsyncChannel();
-                ac.connect(mContext, MobileDataStateTracker.this.mHandler, mMessenger);
             } else {
                 if (DBG) log("Broadcast received: ignore " + intent.getAction());
             }
@@ -613,6 +604,12 @@
         return new LinkCapabilities(mLinkCapabilities);
     }
 
+    public void supplyMessenger(Messenger messenger) {
+        if (VDBG) log(mApnType + " got supplyMessenger");
+        AsyncChannel ac = new AsyncChannel();
+        ac.connect(mContext, MobileDataStateTracker.this.mHandler, messenger);
+    }
+
     private void log(String s) {
         Slog.d(TAG, mApnType + ": " + s);
     }
diff --git a/core/java/android/net/NetworkStateTracker.java b/core/java/android/net/NetworkStateTracker.java
index b22159c..cf77a1c 100644
--- a/core/java/android/net/NetworkStateTracker.java
+++ b/core/java/android/net/NetworkStateTracker.java
@@ -18,6 +18,9 @@
 
 import android.content.Context;
 import android.os.Handler;
+import android.os.Messenger;
+
+import static com.android.internal.util.Protocol.BASE_NETWORK_STATE_TRACKER;
 
 /**
  * Interface provides the {@link com.android.server.ConnectivityService}
@@ -48,25 +51,38 @@
      * msg.what = EVENT_STATE_CHANGED
      * msg.obj = NetworkInfo object
      */
-    public static final int EVENT_STATE_CHANGED = 1;
+    public static final int EVENT_STATE_CHANGED = BASE_NETWORK_STATE_TRACKER;
 
     /**
      * msg.what = EVENT_CONFIGURATION_CHANGED
      * msg.obj = NetworkInfo object
      */
-    public static final int EVENT_CONFIGURATION_CHANGED = 3;
+    public static final int EVENT_CONFIGURATION_CHANGED = BASE_NETWORK_STATE_TRACKER + 1;
 
     /**
      * msg.what = EVENT_RESTORE_DEFAULT_NETWORK
      * msg.obj = FeatureUser object
      */
-    public static final int EVENT_RESTORE_DEFAULT_NETWORK = 6;
+    public static final int EVENT_RESTORE_DEFAULT_NETWORK = BASE_NETWORK_STATE_TRACKER + 2;
 
     /**
      * msg.what = EVENT_NETWORK_SUBTYPE_CHANGED
      * msg.obj = NetworkInfo object
      */
-    public static final int EVENT_NETWORK_SUBTYPE_CHANGED = 7;
+    public static final int EVENT_NETWORK_SUBTYPE_CHANGED = BASE_NETWORK_STATE_TRACKER + 3;
+
+    /**
+     * msg.what = EVENT_NETWORK_CONNECTED
+     * msg.obj = LinkProperties object
+     */
+    public static final int EVENT_NETWORK_CONNECTED = BASE_NETWORK_STATE_TRACKER + 4;
+
+    /**
+     * msg.what = EVENT_NETWORK_CONNECTION_DISCONNECTED
+     * msg.obj = LinkProperties object, same iface name
+     */
+    public static final int EVENT_NETWORK_DISCONNECTED = BASE_NETWORK_STATE_TRACKER + 5;
+
 
     /**
      * -------------------------------------------------------------
@@ -207,4 +223,10 @@
      * Informs the state tracker that a stacked interface has been removed.
      **/
     public void removeStackedLink(LinkProperties link);
+
+    /*
+     * Called once to setup async channel between this and
+     * the underlying network specific code.
+     */
+    public void supplyMessenger(Messenger messenger);
 }
diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java
index 3a7abc0..cc3c5f7 100644
--- a/core/java/android/net/RouteInfo.java
+++ b/core/java/android/net/RouteInfo.java
@@ -132,7 +132,10 @@
     }
 
     private boolean isHost() {
-        return (mGateway.equals(Inet4Address.ANY) || mGateway.equals(Inet6Address.ANY));
+        return (mDestination.getAddress() instanceof Inet4Address &&
+                mDestination.getNetworkPrefixLength() == 32) ||
+               (mDestination.getAddress() instanceof Inet6Address &&
+                mDestination.getNetworkPrefixLength() == 128);
     }
 
     private boolean isDefault() {
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index ce1276f..786439e 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -119,6 +119,8 @@
      * Tags between {@code 0xFFFFFF00} and {@code 0xFFFFFFFF} are reserved and
      * used internally by system services like {@link DownloadManager} when
      * performing traffic on behalf of an application.
+     *
+     * @see #clearThreadStatsTag()
      */
     public static void setThreadStatsTag(int tag) {
         NetworkManagementSocketTagger.setThreadSocketStatsTag(tag);
@@ -128,11 +130,19 @@
      * Get the active tag used when accounting {@link Socket} traffic originating
      * from the current thread. Only one active tag per thread is supported.
      * {@link #tagSocket(Socket)}.
+     *
+     * @see #setThreadStatsTag(int)
      */
     public static int getThreadStatsTag() {
         return NetworkManagementSocketTagger.getThreadSocketStatsTag();
     }
 
+    /**
+     * Clear any active tag set to account {@link Socket} traffic originating
+     * from the current thread.
+     *
+     * @see #setThreadStatsTag(int)
+     */
     public static void clearThreadStatsTag() {
         NetworkManagementSocketTagger.setThreadSocketStatsTag(-1);
     }
@@ -148,7 +158,7 @@
      * To take effect, caller must hold
      * {@link android.Manifest.permission#UPDATE_DEVICE_STATS} permission.
      *
-     * {@hide}
+     * @hide
      */
     public static void setThreadStatsUid(int uid) {
         NetworkManagementSocketTagger.setThreadSocketStatsUid(uid);
@@ -260,10 +270,13 @@
     }
 
     /**
-     * Get the total number of packets transmitted through the mobile interface.
-     *
-     * @return number of packets.  If the statistics are not supported by this device,
-     * {@link #UNSUPPORTED} will be returned.
+     * Return number of packets transmitted across mobile networks since device
+     * boot. Counts packets across all mobile network interfaces, and always
+     * increases monotonically since device boot. Statistics are measured at the
+     * network layer, so they include both TCP and UDP usage.
+     * <p>
+     * Before {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}, this may
+     * return {@link #UNSUPPORTED} on devices where statistics aren't available.
      */
     public static long getMobileTxPackets() {
         long total = 0;
@@ -274,10 +287,13 @@
     }
 
     /**
-     * Get the total number of packets received through the mobile interface.
-     *
-     * @return number of packets.  If the statistics are not supported by this device,
-     * {@link #UNSUPPORTED} will be returned.
+     * Return number of packets received across mobile networks since device
+     * boot. Counts packets across all mobile network interfaces, and always
+     * increases monotonically since device boot. Statistics are measured at the
+     * network layer, so they include both TCP and UDP usage.
+     * <p>
+     * Before {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}, this may
+     * return {@link #UNSUPPORTED} on devices where statistics aren't available.
      */
     public static long getMobileRxPackets() {
         long total = 0;
@@ -288,10 +304,13 @@
     }
 
     /**
-     * Get the total number of bytes transmitted through the mobile interface.
-     *
-     * @return number of bytes.  If the statistics are not supported by this device,
-     * {@link #UNSUPPORTED} will be returned.
+     * Return number of bytes transmitted across mobile networks since device
+     * boot. Counts packets across all mobile network interfaces, and always
+     * increases monotonically since device boot. Statistics are measured at the
+     * network layer, so they include both TCP and UDP usage.
+     * <p>
+     * Before {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}, this may
+     * return {@link #UNSUPPORTED} on devices where statistics aren't available.
      */
     public static long getMobileTxBytes() {
         long total = 0;
@@ -302,10 +321,13 @@
     }
 
     /**
-     * Get the total number of bytes received through the mobile interface.
-     *
-     * @return number of bytes.  If the statistics are not supported by this device,
-     * {@link #UNSUPPORTED} will be returned.
+     * Return number of bytes received across mobile networks since device boot.
+     * Counts packets across all mobile network interfaces, and always increases
+     * monotonically since device boot. Statistics are measured at the network
+     * layer, so they include both TCP and UDP usage.
+     * <p>
+     * Before {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}, this may
+     * return {@link #UNSUPPORTED} on devices where statistics aren't available.
      */
     public static long getMobileRxBytes() {
         long total = 0;
@@ -339,85 +361,73 @@
         return total;
     }
 
-    /**
-     * Get the total number of packets transmitted through the specified interface.
-     *
-     * @return number of packets.  If the statistics are not supported by this interface,
-     * {@link #UNSUPPORTED} will be returned.
-     * @hide
-     */
+    /** {@hide} */
     public static long getTxPackets(String iface) {
         return nativeGetIfaceStat(iface, TYPE_TX_PACKETS);
     }
 
-    /**
-     * Get the total number of packets received through the specified interface.
-     *
-     * @return number of packets.  If the statistics are not supported by this interface,
-     * {@link #UNSUPPORTED} will be returned.
-     * @hide
-     */
+    /** {@hide} */
     public static long getRxPackets(String iface) {
         return nativeGetIfaceStat(iface, TYPE_RX_PACKETS);
     }
 
-    /**
-     * Get the total number of bytes transmitted through the specified interface.
-     *
-     * @return number of bytes.  If the statistics are not supported by this interface,
-     * {@link #UNSUPPORTED} will be returned.
-     * @hide
-     */
+    /** {@hide} */
     public static long getTxBytes(String iface) {
         return nativeGetIfaceStat(iface, TYPE_TX_BYTES);
     }
 
-    /**
-     * Get the total number of bytes received through the specified interface.
-     *
-     * @return number of bytes.  If the statistics are not supported by this interface,
-     * {@link #UNSUPPORTED} will be returned.
-     * @hide
-     */
+    /** {@hide} */
     public static long getRxBytes(String iface) {
         return nativeGetIfaceStat(iface, TYPE_RX_BYTES);
     }
 
     /**
-     * Get the total number of packets sent through all network interfaces.
-     *
-     * @return the number of packets.  If the statistics are not supported by this device,
-     * {@link #UNSUPPORTED} will be returned.
+     * Return number of packets transmitted since device boot. Counts packets
+     * across all network interfaces, and always increases monotonically since
+     * device boot. Statistics are measured at the network layer, so they
+     * include both TCP and UDP usage.
+     * <p>
+     * Before {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}, this may
+     * return {@link #UNSUPPORTED} on devices where statistics aren't available.
      */
     public static long getTotalTxPackets() {
         return nativeGetTotalStat(TYPE_TX_PACKETS);
     }
 
     /**
-     * Get the total number of packets received through all network interfaces.
-     *
-     * @return number of packets.  If the statistics are not supported by this device,
-     * {@link #UNSUPPORTED} will be returned.
+     * Return number of packets received since device boot. Counts packets
+     * across all network interfaces, and always increases monotonically since
+     * device boot. Statistics are measured at the network layer, so they
+     * include both TCP and UDP usage.
+     * <p>
+     * Before {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}, this may
+     * return {@link #UNSUPPORTED} on devices where statistics aren't available.
      */
     public static long getTotalRxPackets() {
         return nativeGetTotalStat(TYPE_RX_PACKETS);
     }
 
     /**
-     * Get the total number of bytes sent through all network interfaces.
-     *
-     * @return number of bytes.  If the statistics are not supported by this device,
-     * {@link #UNSUPPORTED} will be returned.
+     * Return number of bytes transmitted since device boot. Counts packets
+     * across all network interfaces, and always increases monotonically since
+     * device boot. Statistics are measured at the network layer, so they
+     * include both TCP and UDP usage.
+     * <p>
+     * Before {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}, this may
+     * return {@link #UNSUPPORTED} on devices where statistics aren't available.
      */
     public static long getTotalTxBytes() {
         return nativeGetTotalStat(TYPE_TX_BYTES);
     }
 
     /**
-     * Get the total number of bytes received through all network interfaces.
-     *
-     * @return number of bytes.  If the statistics are not supported by this device,
-     * {@link #UNSUPPORTED} will be returned.
+     * Return number of bytes received since device boot. Counts packets across
+     * all network interfaces, and always increases monotonically since device
+     * boot. Statistics are measured at the network layer, so they include both
+     * TCP and UDP usage.
+     * <p>
+     * Before {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}, this may
+     * return {@link #UNSUPPORTED} on devices where statistics aren't available.
      */
     public static long getTotalRxBytes() {
         return nativeGetTotalStat(TYPE_RX_BYTES);
@@ -580,6 +590,7 @@
      * special permission.
      */
     private static NetworkStats getDataLayerSnapshotForUid(Context context) {
+        // TODO: take snapshot locally, since proc file is now visible
         final int uid = android.os.Process.myUid();
         try {
             return getStatsService().getDataLayerSnapshotForUid(uid);
diff --git a/core/java/com/android/internal/view/IInputMethodCallback.aidl b/core/java/android/nfc/BeamShareData.aidl
similarity index 62%
rename from core/java/com/android/internal/view/IInputMethodCallback.aidl
rename to core/java/android/nfc/BeamShareData.aidl
index 717a82d..a47e240 100644
--- a/core/java/com/android/internal/view/IInputMethodCallback.aidl
+++ b/core/java/android/nfc/BeamShareData.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * 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.
@@ -14,13 +14,6 @@
  * limitations under the License.
  */
 
-package com.android.internal.view;
+package android.nfc;
 
-/**
- * Helper interface for IInputMethod to allow the input method to call back
- * to its client with results from incoming calls.
- * {@hide}
- */
-oneway interface IInputMethodCallback {
-    void finishedEvent(int seq, boolean handled);
-}
+parcelable BeamShareData;
diff --git a/core/java/android/nfc/BeamShareData.java b/core/java/android/nfc/BeamShareData.java
new file mode 100644
index 0000000..c30ba14
--- /dev/null
+++ b/core/java/android/nfc/BeamShareData.java
@@ -0,0 +1,62 @@
+package android.nfc;
+
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Class to IPC data to be shared over Android Beam.
+ * Allows bundling NdefMessage, Uris and flags in a single
+ * IPC call. This is important as we want to reduce the
+ * amount of IPC calls at "touch time".
+ * @hide
+ */
+public final class BeamShareData implements Parcelable {
+    public final NdefMessage ndefMessage;
+    public final Uri[] uris;
+    public final int flags;
+
+    public BeamShareData(NdefMessage msg, Uri[] uris, int flags) {
+        this.ndefMessage = msg;
+        this.uris = uris;
+        this.flags = flags;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        int urisLength = (uris != null) ? uris.length : 0;
+        dest.writeParcelable(ndefMessage, 0);
+        dest.writeInt(urisLength);
+        if (urisLength > 0) {
+            dest.writeTypedArray(uris, 0);
+        }
+        dest.writeInt(this.flags);
+    }
+
+    public static final Parcelable.Creator<BeamShareData> CREATOR =
+            new Parcelable.Creator<BeamShareData>() {
+        @Override
+        public BeamShareData createFromParcel(Parcel source) {
+            Uri[] uris = null;
+            NdefMessage msg = source.readParcelable(NdefMessage.class.getClassLoader());
+            int numUris = source.readInt();
+            if (numUris > 0) {
+                uris = new Uri[numUris];
+                source.readTypedArray(uris, Uri.CREATOR);
+            }
+            int flags = source.readInt();
+
+            return new BeamShareData(msg, uris, flags);
+        }
+
+        @Override
+        public BeamShareData[] newArray(int size) {
+            return new BeamShareData[size];
+        }
+    };
+}
diff --git a/core/java/android/nfc/INdefPushCallback.aidl b/core/java/android/nfc/INdefPushCallback.aidl
index 1c6d5d0..16771dc 100644
--- a/core/java/android/nfc/INdefPushCallback.aidl
+++ b/core/java/android/nfc/INdefPushCallback.aidl
@@ -16,15 +16,13 @@
 
 package android.nfc;
 
-import android.nfc.NdefMessage;
-import android.net.Uri;
+import android.nfc.BeamShareData;
 
 /**
  * @hide
  */
 interface INdefPushCallback
 {
-    NdefMessage createMessage();
-    Uri[] getUris();
+    BeamShareData createBeamShareData();
     void onNdefPushComplete();
 }
diff --git a/core/java/android/nfc/NfcActivityManager.java b/core/java/android/nfc/NfcActivityManager.java
index 7c3123f..10183c0 100644
--- a/core/java/android/nfc/NfcActivityManager.java
+++ b/core/java/android/nfc/NfcActivityManager.java
@@ -110,6 +110,7 @@
         NfcAdapter.OnNdefPushCompleteCallback onNdefPushCompleteCallback = null;
         NfcAdapter.CreateBeamUrisCallback uriCallback = null;
         Uri[] uris = null;
+        int flags = 0;
         public NfcActivityState(Activity activity) {
             if (activity.getWindow().isDestroyed()) {
                 throw new IllegalStateException("activity is already destroyed");
@@ -215,11 +216,12 @@
         }
     }
 
-    public void setNdefPushMessage(Activity activity, NdefMessage message) {
+    public void setNdefPushMessage(Activity activity, NdefMessage message, int flags) {
         boolean isResumed;
         synchronized (NfcActivityManager.this) {
             NfcActivityState state = getActivityState(activity);
             state.ndefMessage = message;
+            state.flags = flags;
             isResumed = state.resumed;
         }
         if (isResumed) {
@@ -228,11 +230,12 @@
     }
 
     public void setNdefPushMessageCallback(Activity activity,
-            NfcAdapter.CreateNdefMessageCallback callback) {
+            NfcAdapter.CreateNdefMessageCallback callback, int flags) {
         boolean isResumed;
         synchronized (NfcActivityManager.this) {
             NfcActivityState state = getActivityState(activity);
             state.ndefMessageCallback = callback;
+            state.flags = flags;
             isResumed = state.resumed;
         }
         if (isResumed) {
@@ -267,38 +270,29 @@
 
     /** Callback from NFC service, usually on binder thread */
     @Override
-    public NdefMessage createMessage() {
-        NfcAdapter.CreateNdefMessageCallback callback;
+    public BeamShareData createBeamShareData() {
+        NfcAdapter.CreateNdefMessageCallback ndefCallback;
+        NfcAdapter.CreateBeamUrisCallback urisCallback;
         NdefMessage message;
-        synchronized (NfcActivityManager.this) {
-            NfcActivityState state = findResumedActivityState();
-            if (state == null) return null;
-
-            callback = state.ndefMessageCallback;
-            message = state.ndefMessage;
-        }
-
-        // Make callback without lock
-        if (callback != null) {
-            return callback.createNdefMessage(mDefaultEvent);
-        } else {
-            return message;
-        }
-    }
-
-    /** Callback from NFC service, usually on binder thread */
-    @Override
-    public Uri[] getUris() {
         Uri[] uris;
-        NfcAdapter.CreateBeamUrisCallback callback;
+        int flags;
         synchronized (NfcActivityManager.this) {
             NfcActivityState state = findResumedActivityState();
             if (state == null) return null;
+
+            ndefCallback = state.ndefMessageCallback;
+            urisCallback = state.uriCallback;
+            message = state.ndefMessage;
             uris = state.uris;
-            callback = state.uriCallback;
+            flags = state.flags;
         }
-        if (callback != null) {
-            uris = callback.createBeamUris(mDefaultEvent);
+
+        // Make callbacks without lock
+        if (ndefCallback != null) {
+            message  = ndefCallback.createNdefMessage(mDefaultEvent);
+        }
+        if (urisCallback != null) {
+            uris = urisCallback.createBeamUris(mDefaultEvent);
             if (uris != null) {
                 for (Uri uri : uris) {
                     if (uri == null) {
@@ -314,10 +308,9 @@
                     }
                 }
             }
-            return uris;
-        } else {
-            return uris;
         }
+
+        return new BeamShareData(message, uris, flags);
     }
 
     /** Callback from NFC service, usually on binder thread */
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index 6ad382b..ca4a7d6 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -196,6 +196,9 @@
     public static final int STATE_TURNING_OFF = 4;
 
     /** @hide */
+    public static final int FLAG_NDEF_PUSH_NO_CONFIRM = 0x1;
+
+    /** @hide */
     public static final String ACTION_HANDOVER_TRANSFER_STARTED =
             "android.nfc.action.HANDOVER_TRANSFER_STARTED";
 
@@ -796,12 +799,12 @@
             if (activity == null) {
                 throw new NullPointerException("activity cannot be null");
             }
-            mNfcActivityManager.setNdefPushMessage(activity, message);
+            mNfcActivityManager.setNdefPushMessage(activity, message, 0);
             for (Activity a : activities) {
                 if (a == null) {
                     throw new NullPointerException("activities cannot contain null");
                 }
-                mNfcActivityManager.setNdefPushMessage(a, message);
+                mNfcActivityManager.setNdefPushMessage(a, message, 0);
             }
         } catch (IllegalStateException e) {
             if (targetSdkVersion < android.os.Build.VERSION_CODES.JELLY_BEAN) {
@@ -816,6 +819,16 @@
     }
 
     /**
+     * @hide
+     */
+    public void setNdefPushMessage(NdefMessage message, Activity activity, int flags) {
+        if (activity == null) {
+            throw new NullPointerException("activity cannot be null");
+        }
+        mNfcActivityManager.setNdefPushMessage(activity, message, flags);
+    }
+
+    /**
      * Set a callback that dynamically generates NDEF messages to send using Android Beam (TM).
      *
      * <p>This method may be called at any time before {@link Activity#onDestroy},
@@ -887,12 +900,12 @@
             if (activity == null) {
                 throw new NullPointerException("activity cannot be null");
             }
-            mNfcActivityManager.setNdefPushMessageCallback(activity, callback);
+            mNfcActivityManager.setNdefPushMessageCallback(activity, callback, 0);
             for (Activity a : activities) {
                 if (a == null) {
                     throw new NullPointerException("activities cannot contain null");
                 }
-                mNfcActivityManager.setNdefPushMessageCallback(a, callback);
+                mNfcActivityManager.setNdefPushMessageCallback(a, callback, 0);
             }
         } catch (IllegalStateException e) {
             if (targetSdkVersion < android.os.Build.VERSION_CODES.JELLY_BEAN) {
@@ -907,6 +920,17 @@
     }
 
     /**
+     * @hide
+     */
+    public void setNdefPushMessageCallback(CreateNdefMessageCallback callback, Activity activity,
+            int flags) {
+        if (activity == null) {
+            throw new NullPointerException("activity cannot be null");
+        }
+        mNfcActivityManager.setNdefPushMessageCallback(activity, callback, flags);
+    }
+
+    /**
      * Set a callback on successful Android Beam (TM).
      *
      * <p>This method may be called at any time before {@link Activity#onDestroy},
@@ -1095,7 +1119,7 @@
             throw new NullPointerException();
         }
         enforceResumed(activity);
-        mNfcActivityManager.setNdefPushMessage(activity, message);
+        mNfcActivityManager.setNdefPushMessage(activity, message, 0);
     }
 
     /**
@@ -1123,8 +1147,8 @@
             throw new NullPointerException();
         }
         enforceResumed(activity);
-        mNfcActivityManager.setNdefPushMessage(activity, null);
-        mNfcActivityManager.setNdefPushMessageCallback(activity, null);
+        mNfcActivityManager.setNdefPushMessage(activity, null, 0);
+        mNfcActivityManager.setNdefPushMessageCallback(activity, null, 0);
         mNfcActivityManager.setOnNdefPushCompleteCallback(activity, null);
     }
 
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index 4b83611..45524c8 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -152,16 +152,6 @@
     boolean isTetheringStarted();
 
     /**
-     * Start bluetooth reverse tethering services
-     */
-    void startReverseTethering(in String iface);
-
-    /**
-     * Stop currently running bluetooth reserse tethering services
-     */
-    void stopReverseTethering();
-
-    /**
      * Tethers the specified interface
      */
     void tetherInterface(String iface);
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 4c2d7a6..2e8092a 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -34,6 +34,7 @@
     Bitmap getUserIcon(int userHandle);
     List<UserInfo> getUsers(boolean excludeDying);
     UserInfo getUserInfo(int userHandle);
+    boolean isRestricted();
     void setGuestEnabled(boolean enable);
     boolean isGuestEnabled();
     void wipeUser(int userHandle);
diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java
index 02135bc..38f4d5e 100644
--- a/core/java/android/os/Looper.java
+++ b/core/java/android/os/Looper.java
@@ -50,7 +50,7 @@
   *      }
   *  }</pre>
   */
-public class Looper {
+public final class Looper {
     private static final String TAG = "Looper";
 
     // sThreadLocal.get() will return null unless you've called prepare().
@@ -223,7 +223,7 @@
      *
      * @hide
      */
-    public final int postSyncBarrier() {
+    public int postSyncBarrier() {
         return mQueue.enqueueSyncBarrier(SystemClock.uptimeMillis());
     }
 
@@ -238,7 +238,7 @@
      *
      * @hide
      */
-    public final void removeSyncBarrier(int token) {
+    public void removeSyncBarrier(int token) {
         mQueue.removeSyncBarrier(token);
     }
 
diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java
index 222578a..e0d40c9 100644
--- a/core/java/android/os/MessageQueue.java
+++ b/core/java/android/os/MessageQueue.java
@@ -29,7 +29,7 @@
  * <p>You can retrieve the MessageQueue for the current thread with
  * {@link Looper#myQueue() Looper.myQueue()}.
  */
-public class MessageQueue {
+public final class MessageQueue {
     // True if the message queue can be quit.
     private final boolean mQuitAllowed;
 
@@ -78,7 +78,7 @@
      * 
      * @param handler The IdleHandler to be added.
      */
-    public final void addIdleHandler(IdleHandler handler) {
+    public void addIdleHandler(IdleHandler handler) {
         if (handler == null) {
             throw new NullPointerException("Can't add a null IdleHandler");
         }
@@ -94,7 +94,7 @@
      * 
      * @param handler The IdleHandler to be removed.
      */
-    public final void removeIdleHandler(IdleHandler handler) {
+    public void removeIdleHandler(IdleHandler handler) {
         synchronized (this) {
             mIdleHandlers.remove(handler);
         }
@@ -121,7 +121,7 @@
         }
     }
 
-    final Message next() {
+    Message next() {
         int pendingIdleHandlerCount = -1; // -1 only during first iteration
         int nextPollTimeoutMillis = 0;
 
@@ -218,7 +218,7 @@
         }
     }
 
-    final void quit() {
+    void quit() {
         if (!mQuitAllowed) {
             throw new RuntimeException("Main thread not allowed to quit.");
         }
@@ -232,7 +232,7 @@
         nativeWake(mPtr);
     }
 
-    final int enqueueSyncBarrier(long when) {
+    int enqueueSyncBarrier(long when) {
         // Enqueue a new sync barrier token.
         // We don't need to wake the queue because the purpose of a barrier is to stall it.
         synchronized (this) {
@@ -259,7 +259,7 @@
         }
     }
 
-    final void removeSyncBarrier(int token) {
+    void removeSyncBarrier(int token) {
         // Remove a sync barrier token from the queue.
         // If the queue is no longer stalled by a barrier then wake it.
         final boolean needWake;
@@ -288,7 +288,7 @@
         }
     }
 
-    final boolean enqueueMessage(Message msg, long when) {
+    boolean enqueueMessage(Message msg, long when) {
         if (msg.isInUse()) {
             throw new AndroidRuntimeException(msg + " This message is already in use.");
         }
@@ -338,7 +338,7 @@
         return true;
     }
 
-    final boolean hasMessages(Handler h, int what, Object object) {
+    boolean hasMessages(Handler h, int what, Object object) {
         if (h == null) {
             return false;
         }
@@ -355,7 +355,7 @@
         }
     }
 
-    final boolean hasMessages(Handler h, Runnable r, Object object) {
+    boolean hasMessages(Handler h, Runnable r, Object object) {
         if (h == null) {
             return false;
         }
@@ -372,7 +372,7 @@
         }
     }
 
-    final void removeMessages(Handler h, int what, Object object) {
+    void removeMessages(Handler h, int what, Object object) {
         if (h == null) {
             return;
         }
@@ -406,7 +406,7 @@
         }
     }
 
-    final void removeMessages(Handler h, Runnable r, Object object) {
+    void removeMessages(Handler h, Runnable r, Object object) {
         if (h == null || r == null) {
             return;
         }
@@ -440,7 +440,7 @@
         }
     }
 
-    final void removeCallbacksAndMessages(Handler h, Object object) {
+    void removeCallbacksAndMessages(Handler h, Object object) {
         if (h == null) {
             return;
         }
diff --git a/core/java/android/os/SystemClock.java b/core/java/android/os/SystemClock.java
index c9adf45..729c64b 100644
--- a/core/java/android/os/SystemClock.java
+++ b/core/java/android/os/SystemClock.java
@@ -138,8 +138,6 @@
 
     /**
      * Returns milliseconds since boot, not counting time spent in deep sleep.
-     * <b>Note:</b> This value may get reset occasionally (before it would
-     * otherwise wrap around).
      *
      * @return milliseconds of non-sleep uptime since boot.
      */
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 7c05528..b9b8f08 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -37,48 +37,98 @@
     private final Context mContext;
 
     /**
-     * @hide
-     * Key for user restrictions. Specifies if a user is allowed to add or remove accounts.
+     * Key for user restrictions. Specifies if a user is disallowed from adding and removing
+     * accounts.
+     * The default value is <code>false</code>.
+     * <p/>
      * Type: Boolean
      * @see #setUserRestrictions(Bundle)
      * @see #getUserRestrictions()
      */
-    public static final String ALLOW_MODIFY_ACCOUNTS = "modify_accounts";
+    public static final String DISALLOW_MODIFY_ACCOUNTS = "no_modify_accounts";
 
     /**
-     * @hide
-     * Key for user restrictions. Specifies if a user is allowed to change Wi-Fi access points.
+     * Key for user restrictions. Specifies if a user is disallowed from changing Wi-Fi
+     * access points.
+     * The default value is <code>false</code>.
+     * <p/>
      * Type: Boolean
      * @see #setUserRestrictions(Bundle)
      * @see #getUserRestrictions()
      */
-    public static final String ALLOW_CONFIG_WIFI = "config_wifi";
+    public static final String DISALLOW_CONFIG_WIFI = "no_config_wifi";
 
     /**
-     * @hide
-     * Key for user restrictions. Specifies if a user is allowed to install applications.
+     * Key for user restrictions. Specifies if a user is disallowed from installing applications.
+     * The default value is <code>false</code>.
+     * <p/>
      * Type: Boolean
      * @see #setUserRestrictions(Bundle)
      * @see #getUserRestrictions()
      */
-    public static final String ALLOW_INSTALL_APPS = "install_apps";
+    public static final String DISALLOW_INSTALL_APPS = "no_install_apps";
 
     /**
-     * @hide
-     * Key for user restrictions. Specifies if a user is allowed to uninstall applications.
+     * Key for user restrictions. Specifies if a user is disallowed from uninstalling applications.
+     * The default value is <code>false</code>.
+     * <p/>
      * Type: Boolean
      * @see #setUserRestrictions(Bundle)
      * @see #getUserRestrictions()
      */
-    public static final String ALLOW_UNINSTALL_APPS = "uninstall_apps";
+    public static final String DISALLOW_UNINSTALL_APPS = "no_uninstall_apps";
 
-    /** @hide *
-     * Key for user restrictions. Specifies if a user is allowed to toggle location sharing.
+    /**
+     * Key for user restrictions. Specifies if a user is disallowed from toggling location sharing.
+     * The default value is <code>false</code>.
+     * <p/>
      * Type: Boolean
      * @see #setUserRestrictions(Bundle)
      * @see #getUserRestrictions()
      */
-    public static final String ALLOW_CONFIG_LOCATION_ACCESS = "config_location_access";
+
+    public static final String DISALLOW_SHARE_LOCATION = "no_share_location";
+
+    /**
+     * Key for user restrictions. Specifies if a user is disallowed from enabling the
+     * "Unknown Sources" setting, that allows installation of apps from unknown sources.
+     * The default value is <code>false</code>.
+     * <p/>
+     * Type: Boolean
+     * @see #setUserRestrictions(Bundle)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_INSTALL_UNKNOWN_SOURCES = "no_install_unknown_sources";
+
+    /**
+     * Key for user restrictions. Specifies if a user is disallowed from configuring bluetooth.
+     * The default value is <code>false</code>.
+     * <p/>
+     * Type: Boolean
+     * @see #setUserRestrictions(Bundle)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_CONFIG_BLUETOOTH = "no_config_bluetooth";
+
+
+    /**
+     * Key for user restrictions. Specifies if a user is disallowed from transferring files over
+     * USB. The default value is <code>false</code>.
+     * <p/>
+     * Type: Boolean
+     * @see #setUserRestrictions(Bundle)
+     * @see #getUserRestrictions()
+     */
+    public static final String DISALLOW_USB_FILE_TRANSFER = "no_usb_file_transfer";
+
+    private static UserManager sInstance = null;
+
+    public synchronized static UserManager get(Context context) {
+        if (sInstance == null) {
+            sInstance = (UserManager) context.getSystemService(Context.USER_SERVICE);
+        }
+        return sInstance;
+    }
 
     /** @hide */
     public UserManager(Context context, IUserManager service) {
@@ -122,18 +172,21 @@
    /**
      * Used to determine whether the user making this call is subject to
      * teleportations.
-     * @return whether the user making this call is a goat 
+     * @return whether the user making this call is a goat
      */
     public boolean isUserAGoat() {
         return false;
     }
 
     /**
-     * @hide
+     * Used to check if the user making this call is a restricted user. Restricted users may have
+     * application restrictions imposed on them. All apps should default to the most restrictive
+     * version, unless they have specific restrictions available through a call to
+     * {@link Context#getApplicationRestrictions()}.
      */
     public boolean isUserRestricted() {
         try {
-            return mService.getUserInfo(getUserHandle()).isRestricted();
+            return mService.isRestricted();
         } catch (RemoteException re) {
             Log.w(TAG, "Could not check if user restricted ", re);
             return false;
@@ -189,12 +242,19 @@
         }
     }
 
-    /** @hide */
+    /**
+     * Returns the user-wide restrictions imposed on this user.
+     * @return a Bundle containing all the restrictions.
+     */
     public Bundle getUserRestrictions() {
         return getUserRestrictions(Process.myUserHandle());
     }
 
-    /** @hide */
+    /**
+     * Returns the user-wide restrictions imposed on the user specified by <code>userHandle</code>.
+     * @param userHandle the UserHandle of the user for whom to retrieve the restrictions.
+     * @return a Bundle containing all the restrictions.
+     */
     public Bundle getUserRestrictions(UserHandle userHandle) {
         try {
             return mService.getUserRestrictions(userHandle.getIdentifier());
@@ -204,12 +264,21 @@
         }
     }
 
-    /** @hide */
+    /**
+     * Sets all the user-wide restrictions for this user.
+     * Requires the MANAGE_USERS permission.
+     * @param restrictions the Bundle containing all the restrictions.
+     */
     public void setUserRestrictions(Bundle restrictions) {
         setUserRestrictions(restrictions, Process.myUserHandle());
     }
 
-    /** @hide */
+    /**
+     * Sets all the user-wide restrictions for the specified user.
+     * Requires the MANAGE_USERS permission.
+     * @param restrictions the Bundle containing all the restrictions.
+     * @param userHandle the UserHandle of the user for whom to set the restrictions.
+     */
     public void setUserRestrictions(Bundle restrictions, UserHandle userHandle) {
         try {
             mService.setUserRestrictions(restrictions, userHandle.getIdentifier());
@@ -218,7 +287,26 @@
         }
     }
 
-    /** @hide */
+    /**
+     * Sets the value of a specific restriction.
+     * Requires the MANAGE_USERS permission.
+     * @param key the key of the restriction
+     * @param value the value for the restriction
+     */
+    public void setUserRestriction(String key, boolean value) {
+        Bundle bundle = getUserRestrictions();
+        bundle.putBoolean(key, value);
+        setUserRestrictions(bundle);
+    }
+
+    /**
+     * @hide
+     * Sets the value of a specific restriction on a specific user.
+     * Requires the {@link android.Manifest.permission#MANAGE_USERS} permission.
+     * @param key the key of the restriction
+     * @param value the value for the restriction
+     * @param userHandle the user whose restriction is to be changed.
+     */
     public void setUserRestriction(String key, boolean value, UserHandle userHandle) {
         Bundle bundle = getUserRestrictions(userHandle);
         bundle.putBoolean(key, value);
@@ -226,6 +314,16 @@
     }
 
     /**
+     * @hide
+     * Returns whether the current user has been disallowed from performing certain actions
+     * or setting certain settings.
+     * @param restrictionKey the string key representing the restriction
+     */
+    public boolean hasUserRestriction(String restrictionKey) {
+        return getUserRestrictions().getBoolean(restrictionKey, false);
+    }
+
+    /**
      * Return the serial number for a user.  This is a device-unique
      * number assigned to that user; if the user is deleted and then a new
      * user created, the new users will not be given the same serial number.
@@ -419,7 +517,7 @@
      * Returns the maximum number of users that can be created on this device. A return value
      * of 1 means that it is a single user device.
      * @hide
-     * @return a value greater than or equal to 1 
+     * @return a value greater than or equal to 1
      */
     public static int getMaxSupportedUsers() {
         // Don't allow multiple users on certain builds
@@ -462,13 +560,6 @@
         return -1;
     }
 
-    /**
-     * Returns whether the current user is allowed to toggle location sharing settings.
-     * @hide
-     */
-    public boolean isLocationSharingToggleAllowed() {
-        return getUserRestrictions().getBoolean(ALLOW_CONFIG_LOCATION_ACCESS);
-    }
 
     /**
      * @hide
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 8f54a38..66083c8 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -1359,7 +1359,7 @@
      * status definitions. Automatically computed as the highest presence of all
      * constituent raw contacts. The provider may choose not to store this value
      * in persistent storage. The expectation is that presence status will be
-     * updated on a regular basic.</td>
+     * updated on a regular basis.</td>
      * </tr>
      * <tr>
      * <td>String</td>
@@ -3793,13 +3793,63 @@
     }
 
     /**
+     * Columns in the Data_Usage_Stat table
+     */
+    protected interface DataUsageStatColumns {
+        /** What the referenced {@link Data} was used for.
+         * @see DataUsageStatColumns#USAGE_TYPE_CALL
+         * @see DataUsageStatColumns#USAGE_TYPE_LONG_TEXT
+         * @see DataUsageStatColumns#USAGE_TYPE_SHORT_TEXT
+         */
+        public static final String USAGE_TYPE = "usage_type";
+
+        /** The last time (in milliseconds) this {@link Data} was used. */
+        public static final String LAST_TIME_USED = "last_time_used";
+
+        /** The number of times the referenced {@link Data} has been used for the purpose described
+         * in {@link DataUsageStatColumns#USAGE_TYPE}.
+         */
+        public static final String TIMES_USED = "times_used";
+
+        /**
+         * Integer value for USAGE_TYPE.
+         * This type of usage refers to voice interaction, which includes phone calls, voice chat,
+         * and video chat.
+         *
+         * @see DataUsageFeedback#USAGE_TYPE
+         * @see DataUsageStatColumns#USAGE_TYPE
+         */
+        public static final int USAGE_TYPE_CALL = 0;
+
+        /**
+         * Integer value for USAGE_TYPE.
+         * This type of usage refers to text interaction involving longer messages, which includes
+         * email.
+         *
+         * @see DataUsageFeedback#USAGE_TYPE
+         * @see DataUsageStatColumns#USAGE_TYPE
+         */
+        public static final int USAGE_TYPE_LONG_TEXT = 1;
+
+        /**
+         * Integer value for USAGE_TYPE.
+         * This type of usage for text interaction involving shorter messages, which includes SMS
+         * and text chat with email addresses.
+         *
+         * @see DataUsageFeedback#USAGE_TYPE
+         * @see DataUsageStatColumns#USAGE_TYPE
+         */
+        public static final int USAGE_TYPE_SHORT_TEXT = 2;
+    }
+
+    /**
      * Combines all columns returned by {@link ContactsContract.Data} table queries.
      *
      * @see ContactsContract.Data
      */
     protected interface DataColumnsWithJoins extends BaseColumns, DataColumns, StatusColumns,
             RawContactsColumns, ContactsColumns, ContactNameColumns, ContactOptionsColumns,
-            ContactStatusColumns {
+            ContactStatusColumns, DataUsageStatColumns {
     }
 
     /**
@@ -4131,7 +4181,7 @@
      * all IM rows. See {@link StatusUpdates} for individual status definitions.
      * The provider may choose not to store this value
      * in persistent storage. The expectation is that presence status will be
-     * updated on a regular basic.
+     * updated on a regular basis.
      * </td>
      * </tr>
      * <tr>
@@ -4325,6 +4375,13 @@
         public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "data");
 
         /**
+         * A boolean parameter for {@link Data#CONTENT_URI}.
+         * This specifies whether or not the returned data items should be filtered to show
+         * data items belonging to visible contacts only.
+         */
+        public static final String VISIBLE_CONTACTS_ONLY = "visible_contacts_only";
+
+        /**
          * The MIME type of the results from {@link #CONTENT_URI}.
          */
         public static final String CONTENT_TYPE = "vnd.android.cursor.dir/data";
@@ -6833,6 +6890,38 @@
             public static final Uri CONTENT_FILTER_URI = Uri.withAppendedPath(CONTENT_URI,
                     "filter");
         }
+
+        /**
+         * A special class of data items, used to refer to types of data that can be used to attempt
+         * to start communicating with a person ({@link Phone} and {@link Email}). Note that this
+         * is NOT a separate data kind.
+         *
+         * This URI allows the ContactsProvider to return a unified result for data items that users
+         * can use to initiate communications with another contact. {@link Phone} and {@link Email}
+         * are the current data types in this category.
+         */
+        public static final class Contactables implements DataColumnsWithJoins, CommonColumns {
+            /**
+             * The content:// style URI for these data items, which requests a directory of data
+             * rows matching the selection criteria.
+             */
+            public static final Uri CONTENT_URI = Uri.withAppendedPath(Data.CONTENT_URI,
+                    "contactables");
+
+            /**
+             * The content:// style URI for these data items, which allows for a query parameter to
+             * be appended onto the end to filter for data items matching the query.
+             */
+            public static final Uri CONTENT_FILTER_URI = Uri.withAppendedPath(
+                    Contactables.CONTENT_URI, "filter");
+
+            /**
+             * A boolean parameter for {@link Data#CONTENT_URI}.
+             * This specifies whether or not the returned data items should be filtered to show
+             * data items belonging to visible contacts only.
+             */
+            public static final String VISIBLE_CONTACTS_ONLY = "visible_contacts_only";
+        }
     }
 
     /**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 19283be..03ee9eb 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -4411,6 +4411,14 @@
        public static final String DATA_ROAMING = "data_roaming";
 
        /**
+        * The value passed to a Mobile DataConnection via bringUp which defines the
+        * number of retries to preform when setting up the initial connection. The default
+        * value defined in DataConnectionTrackerBase#DEFAULT_MDC_INITIAL_RETRY is currently 1.
+        * @hide
+        */
+       public static final String MDC_INITIAL_MAX_RETRY = "mdc_initial_max_retry";
+
+       /**
         * Whether user has enabled development settings.
         */
        public static final String DEVELOPMENT_SETTINGS_ENABLED = "development_settings_enabled";
@@ -5340,6 +5348,62 @@
         public static final String AUDIO_SAFE_VOLUME_STATE = "audio_safe_volume_state";
 
         /**
+         * URL for tzinfo (time zone) updates
+         * @hide
+         */
+        public static final String TZINFO_UPDATE_CONTENT_URL = "tzinfo_content_url";
+
+        /**
+         * URL for tzinfo (time zone) update metadata
+         * @hide
+         */
+        public static final String TZINFO_UPDATE_METADATA_URL = "tzinfo_metadata_url";
+
+        /**
+         * URL for selinux (mandatory access control) updates
+         * @hide
+         */
+        public static final String SELINUX_UPDATE_CONTENT_URL = "selinux_content_url";
+
+        /**
+         * URL for selinux (mandatory access control) update metadata
+         * @hide
+         */
+        public static final String SELINUX_UPDATE_METADATA_URL = "selinux_metadata_url";
+
+        /**
+         * URL for sms short code updates
+         * @hide
+         */
+        public static final String SMS_SHORT_CODES_UPDATE_CONTENT_URL =
+                "sms_short_codes_content_url";
+
+        /**
+         * URL for sms short code update metadata
+         * @hide
+         */
+        public static final String SMS_SHORT_CODES_UPDATE_METADATA_URL =
+                "sms_short_codes_metadata_url";
+
+        /**
+         * URL for cert pinlist updates
+         * @hide
+         */
+        public static final String CERT_PIN_UPDATE_CONTENT_URL = "cert_pin_content_url";
+
+        /**
+         * URL for cert pinlist updates
+         * @hide
+         */
+        public static final String CERT_PIN_UPDATE_METADATA_URL = "cert_pin_metadata_url";
+
+        /**
+         * SELinux enforcement status. If 0, permissive; if 1, enforcing.
+         * @hide
+         */
+        public static final String SELINUX_STATUS = "selinux_status";
+
+        /**
          * Settings to backup. This is here so that it's in the same place as the settings
          * keys and easy to update.
          *
diff --git a/core/java/android/security/IKeystoreService.java b/core/java/android/security/IKeystoreService.java
index a890d9b..c365643 100644
--- a/core/java/android/security/IKeystoreService.java
+++ b/core/java/android/security/IKeystoreService.java
@@ -427,6 +427,23 @@
                 }
                 return _result;
             }
+
+            @Override
+            public int is_hardware_backed() throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                int _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    mRemote.transact(Stub.TRANSACTION_is_hardware_backed, _data, _reply, 0);
+                    _reply.readException();
+                    _result = _reply.readInt();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
         }
 
         private static final String DESCRIPTOR = "android.security.keystore";
@@ -452,6 +469,7 @@
         static final int TRANSACTION_ungrant = IBinder.FIRST_CALL_TRANSACTION + 18;
         static final int TRANSACTION_getmtime = IBinder.FIRST_CALL_TRANSACTION + 19;
         static final int TRANSACTION_duplicate = IBinder.FIRST_CALL_TRANSACTION + 20;
+        static final int TRANSACTION_is_hardware_backed = IBinder.FIRST_CALL_TRANSACTION + 21;
 
         /**
          * Cast an IBinder object into an IKeystoreService interface, generating
@@ -539,4 +557,6 @@
 
     public int duplicate(String srcKey, int srcUid, String destKey, int destUid)
             throws RemoteException;
+
+    public int is_hardware_backed() throws RemoteException;
 }
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 9051285..1291279 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -340,7 +340,7 @@
                         w += widths[j - paraStart];
                     }
 
-                    boolean isSpaceOrTab = c == CHAR_SPACE || c == CHAR_TAB;
+                    boolean isSpaceOrTab = c == CHAR_SPACE || c == CHAR_TAB || c == CHAR_ZWSP;
 
                     if (w <= width || isSpaceOrTab) {
                         fitWidth = w;
@@ -956,6 +956,7 @@
     private static final char CHAR_SPACE = ' ';
     private static final char CHAR_SLASH = '/';
     private static final char CHAR_HYPHEN = '-';
+    private static final char CHAR_ZWSP = '\u200B';
 
     private static final double EXTRA_ROUNDING = 0.5;
 
diff --git a/core/java/android/text/format/DateFormat.java b/core/java/android/text/format/DateFormat.java
index f813df3..c497e35 100644
--- a/core/java/android/text/format/DateFormat.java
+++ b/core/java/android/text/format/DateFormat.java
@@ -31,6 +31,7 @@
 import java.util.TimeZone;
 import java.text.SimpleDateFormat;
 
+import libcore.icu.ICU;
 import libcore.icu.LocaleData;
 
 /**
@@ -43,6 +44,9 @@
  * for both formatting and parsing dates. For the canonical documentation
  * of format strings, see {@link java.text.SimpleDateFormat}.
  *
+ * <p>In cases where the system does not provide a suitable pattern,
+ * this class offers the {@link #getBestDateTimePattern} method.
+ *
  * <p>The {@code format} methods in this class implement a subset of Unicode
  * <a href="http://www.unicode.org/reports/tr35/#Date_Format_Patterns">UTS #35</a> patterns.
  * The subset currently supported by this class includes the following format characters:
@@ -164,6 +168,37 @@
     }
 
     /**
+     * Returns the best possible localized form of the given skeleton for the given
+     * locale. A skeleton is similar to, and uses the same format characters as, a Unicode
+     * <a href="http://www.unicode.org/reports/tr35/#Date_Format_Patterns">UTS #35</a>
+     * pattern.
+     *
+     * <p>One difference is that order is irrelevant. For example, "MMMMd" will return
+     * "MMMM d" in the {@code en_US} locale, but "d. MMMM" in the {@code de_CH} locale.
+     *
+     * <p>Note also in that second example that the necessary punctuation for German was
+     * added. For the same input in {@code es_ES}, we'd have even more extra text:
+     * "d 'de' MMMM".
+     *
+     * <p>This method will automatically correct for grammatical necessity. Given the
+     * same "MMMMd" input, this method will return "d LLLL" in the {@code fa_IR} locale,
+     * where stand-alone months are necessary. Lengths are preserved where meaningful,
+     * so "Md" would give a different result to "MMMd", say, except in a locale such as
+     * {@code ja_JP} where there is only one length of month.
+     *
+     * <p>This method will only return patterns that are in CLDR, and is useful whenever
+     * you know what elements you want in your format string but don't want to make your
+     * code specific to any one locale.
+     *
+     * @param locale the locale into which the skeleton should be localized
+     * @param skeleton a skeleton as described above
+     * @return a string pattern suitable for use with {@link java.text.SimpleDateFormat}.
+     */
+    public static String getBestDateTimePattern(Locale locale, String skeleton) {
+        return ICU.getBestDateTimePattern(skeleton, locale.toString());
+    }
+
+    /**
      * Returns a {@link java.text.DateFormat} object that can format the time according
      * to the current locale and the user's 12-/24-hour clock preference.
      * @param context the application context
diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java
index 7e9d811..5a88cf6 100644
--- a/core/java/android/text/format/DateUtils.java
+++ b/core/java/android/text/format/DateUtils.java
@@ -39,7 +39,6 @@
 {
     private static final Object sLock = new Object();
     private static Configuration sLastConfig;
-    private static java.text.DateFormat sStatusTimeFormat;
     private static String sElapsedFormatMMSS;
     private static String sElapsedFormatHMMSS;
 
@@ -95,14 +94,14 @@
     // translated.
     /**
      * This is not actually the preferred 24-hour date format in all locales.
-     * @deprecated use {@link java.text.SimpleDateFormat} instead.
+     * @deprecated Use {@link java.text.SimpleDateFormat} instead.
      */
     @Deprecated
     public static final String HOUR_MINUTE_24 = "%H:%M";
     public static final String MONTH_FORMAT = "%B";
     /**
      * This is not actually a useful month name in all locales.
-     * @deprecated use {@link java.text.SimpleDateFormat} instead.
+     * @deprecated Use {@link java.text.SimpleDateFormat} instead.
      */
     @Deprecated
     public static final String ABBREV_MONTH_FORMAT = "%b";
@@ -118,7 +117,7 @@
     // The index is constructed from a bit-wise OR of the boolean values:
     // {showTime, showYear, showWeekDay}.  For example, if showYear and
     // showWeekDay are both true, then the index would be 3.
-    /** @deprecated do not use. */
+    /** @deprecated Do not use. */
     public static final int sameYearTable[] = {
         com.android.internal.R.string.same_year_md1_md2,
         com.android.internal.R.string.same_year_wday1_md1_wday2_md2,
@@ -145,7 +144,7 @@
     // The index is constructed from a bit-wise OR of the boolean values:
     // {showTime, showYear, showWeekDay}.  For example, if showYear and
     // showWeekDay are both true, then the index would be 3.
-    /** @deprecated do not use. */
+    /** @deprecated Do not use. */
     public static final int sameMonthTable[] = {
         com.android.internal.R.string.same_month_md1_md2,
         com.android.internal.R.string.same_month_wday1_md1_wday2_md2,
@@ -172,7 +171,7 @@
      *
      * @more <p>
      *       e.g. "Sunday" or "January"
-     * @deprecated use {@link java.text.SimpleDateFormat} instead.
+     * @deprecated Use {@link java.text.SimpleDateFormat} instead.
      */
     @Deprecated
     public static final int LENGTH_LONG = 10;
@@ -183,7 +182,7 @@
      *
      * @more <p>
      *       e.g. "Sun" or "Jan"
-     * @deprecated use {@link java.text.SimpleDateFormat} instead.
+     * @deprecated Use {@link java.text.SimpleDateFormat} instead.
      */
     @Deprecated
     public static final int LENGTH_MEDIUM = 20;
@@ -195,7 +194,7 @@
      * <p>e.g. "Su" or "Jan"
      * <p>In most languages, the results returned for LENGTH_SHORT will be the same as
      * the results returned for {@link #LENGTH_MEDIUM}.
-     * @deprecated use {@link java.text.SimpleDateFormat} instead.
+     * @deprecated Use {@link java.text.SimpleDateFormat} instead.
      */
     @Deprecated
     public static final int LENGTH_SHORT = 30;
@@ -204,7 +203,7 @@
      * Request an even shorter abbreviated version of the name.
      * Do not use this.  Currently this will always return the same result
      * as {@link #LENGTH_SHORT}.
-     * @deprecated use {@link java.text.SimpleDateFormat} instead.
+     * @deprecated Use {@link java.text.SimpleDateFormat} instead.
      */
     @Deprecated
     public static final int LENGTH_SHORTER = 40;
@@ -216,7 +215,7 @@
      * <p>e.g. "S", "T", "T" or "J"
      * <p>In some languages, the results returned for LENGTH_SHORTEST will be the same as
      * the results returned for {@link #LENGTH_SHORT}.
-     * @deprecated use {@link java.text.SimpleDateFormat} instead.
+     * @deprecated Use {@link java.text.SimpleDateFormat} instead.
      */
     @Deprecated
     public static final int LENGTH_SHORTEST = 50;
@@ -232,7 +231,7 @@
      *               Undefined lengths will return {@link #LENGTH_MEDIUM}
      *               but may return something different in the future.
      * @throws IndexOutOfBoundsException if the dayOfWeek is out of bounds.
-     * @deprecated use {@link java.text.SimpleDateFormat} instead.
+     * @deprecated Use {@link java.text.SimpleDateFormat} instead.
      */
     @Deprecated
     public static String getDayOfWeekString(int dayOfWeek, int abbrev) {
@@ -254,7 +253,7 @@
      * @param ampm Either {@link Calendar#AM Calendar.AM} or {@link Calendar#PM Calendar.PM}.
      * @throws IndexOutOfBoundsException if the ampm is out of bounds.
      * @return Localized version of "AM" or "PM".
-     * @deprecated use {@link java.text.SimpleDateFormat} instead.
+     * @deprecated Use {@link java.text.SimpleDateFormat} instead.
      */
     @Deprecated
     public static String getAMPMString(int ampm) {
@@ -270,7 +269,7 @@
      *               Undefined lengths will return {@link #LENGTH_MEDIUM}
      *               but may return something different in the future.
      * @return Localized month of the year.
-     * @deprecated use {@link java.text.SimpleDateFormat} instead.
+     * @deprecated Use {@link java.text.SimpleDateFormat} instead.
      */
     @Deprecated
     public static String getMonthString(int month, int abbrev) {
@@ -514,7 +513,6 @@
         Configuration cfg = r.getConfiguration();
         if (sLastConfig == null || !sLastConfig.equals(cfg)) {
             sLastConfig = cfg;
-            sStatusTimeFormat = java.text.DateFormat.getTimeInstance(java.text.DateFormat.SHORT);
             sElapsedFormatMMSS = r.getString(com.android.internal.R.string.elapsed_time_short_format_mm_ss);
             sElapsedFormatHMMSS = r.getString(com.android.internal.R.string.elapsed_time_short_format_h_mm_ss);
         }
@@ -651,99 +649,6 @@
     }
 
     /**
-     * Return a string containing the date and time in RFC2445 format.
-     * Ensures that the time is written in UTC.  The Calendar class doesn't
-     * really help out with this, so this is slower than it ought to be.
-     *
-     * @param cal the date and time to write
-     * @hide
-     * @deprecated use {@link android.text.format.Time}
-     */
-    public static String writeDateTime(Calendar cal)
-    {
-        TimeZone tz = TimeZone.getTimeZone("GMT");
-        GregorianCalendar c = new GregorianCalendar(tz);
-        c.setTimeInMillis(cal.getTimeInMillis());
-        return writeDateTime(c, true);
-    }
-
-    /**
-     * Return a string containing the date and time in RFC2445 format.
-     *
-     * @param cal the date and time to write
-     * @param zulu If the calendar is in UTC, pass true, and a Z will
-     * be written at the end as per RFC2445.  Otherwise, the time is
-     * considered in localtime.
-     * @hide
-     * @deprecated use {@link android.text.format.Time}
-     */
-    public static String writeDateTime(Calendar cal, boolean zulu)
-    {
-        StringBuilder sb = new StringBuilder();
-        sb.ensureCapacity(16);
-        if (zulu) {
-            sb.setLength(16);
-            sb.setCharAt(15, 'Z');
-        } else {
-            sb.setLength(15);
-        }
-        return writeDateTime(cal, sb);
-    }
-
-    /**
-     * Return a string containing the date and time in RFC2445 format.
-     *
-     * @param cal the date and time to write
-     * @param sb a StringBuilder to use.  It is assumed that setLength
-     *           has already been called on sb to the appropriate length
-     *           which is sb.setLength(zulu ? 16 : 15)
-     * @hide
-     * @deprecated use {@link android.text.format.Time}
-     */
-    public static String writeDateTime(Calendar cal, StringBuilder sb)
-    {
-        int n;
-
-        n = cal.get(Calendar.YEAR);
-        sb.setCharAt(3, (char)('0'+n%10));
-        n /= 10;
-        sb.setCharAt(2, (char)('0'+n%10));
-        n /= 10;
-        sb.setCharAt(1, (char)('0'+n%10));
-        n /= 10;
-        sb.setCharAt(0, (char)('0'+n%10));
-
-        n = cal.get(Calendar.MONTH) + 1;
-        sb.setCharAt(5, (char)('0'+n%10));
-        n /= 10;
-        sb.setCharAt(4, (char)('0'+n%10));
-
-        n = cal.get(Calendar.DAY_OF_MONTH);
-        sb.setCharAt(7, (char)('0'+n%10));
-        n /= 10;
-        sb.setCharAt(6, (char)('0'+n%10));
-
-        sb.setCharAt(8, 'T');
-
-        n = cal.get(Calendar.HOUR_OF_DAY);
-        sb.setCharAt(10, (char)('0'+n%10));
-        n /= 10;
-        sb.setCharAt(9, (char)('0'+n%10));
-
-        n = cal.get(Calendar.MINUTE);
-        sb.setCharAt(12, (char)('0'+n%10));
-        n /= 10;
-        sb.setCharAt(11, (char)('0'+n%10));
-
-        n = cal.get(Calendar.SECOND);
-        sb.setCharAt(14, (char)('0'+n%10));
-        n /= 10;
-        sb.setCharAt(13, (char)('0'+n%10));
-
-        return sb.toString();
-    }
-
-    /**
      * Formats a date or a time range according to the local conventions.
      * <p>
      * Note that this is a convenience method. Using it involves creating an
diff --git a/core/java/android/text/format/Formatter.java b/core/java/android/text/format/Formatter.java
index 121c6f2..9c98b98 100644
--- a/core/java/android/text/format/Formatter.java
+++ b/core/java/android/text/format/Formatter.java
@@ -95,16 +95,12 @@
     }
 
     /**
-     * Returns a string in the canonical IP format ###.###.###.### from a packed integer containing
-     * the IP address.  The IP address is expected to be in little-endian format (LSB first). That
-     * is, 0x01020304 will return "4.3.2.1".
+     * Returns a string in the canonical IPv4 format ###.###.###.### from a packed integer
+     * containing the IP address. The IPv4 address is expected to be in little-endian
+     * format (LSB first). That is, 0x01020304 will return "4.3.2.1".
      *
-     * @param ipv4Address the IP address as a packed integer with LSB first.
-     * @return string with canonical IP address format.
-     *
-     * @deprecated this method doesn't support IPv6 addresses. Prefer {@link
-     *     java.net.InetAddress#getHostAddress()}, which supports both IPv4 and
-     *     IPv6 addresses.
+     * @deprecated Use {@link java.net.InetAddress#getHostAddress()}, which supports both IPv4 and
+     *     IPv6 addresses. This method does not support IPv6 addresses.
      */
     @Deprecated
     public static String formatIpAddress(int ipv4Address) {
diff --git a/core/java/android/util/AttributeSet.java b/core/java/android/util/AttributeSet.java
index 470526c..74942ba 100644
--- a/core/java/android/util/AttributeSet.java
+++ b/core/java/android/util/AttributeSet.java
@@ -151,7 +151,7 @@
      * Return the value of 'attribute' as a resource identifier.
      * 
      * <p>Note that this is different than {@link #getAttributeNameResource}
-     * in that it returns a the value contained in this attribute as a
+     * in that it returns the value contained in this attribute as a
      * resource identifier (i.e., a value originally of the form
      * "@package:type/resource"); the other method returns a resource
      * identifier that identifies the name of the attribute.
@@ -230,7 +230,7 @@
      * Return the value of attribute at 'index' as a resource identifier.
      * 
      * <p>Note that this is different than {@link #getAttributeNameResource}
-     * in that it returns a the value contained in this attribute as a
+     * in that it returns the value contained in this attribute as a
      * resource identifier (i.e., a value originally of the form
      * "@package:type/resource"); the other method returns a resource
      * identifier that identifies the name of the attribute.
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index c369ebe..2ec9a7d 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -399,12 +399,13 @@
     ///////////////////////////////////////////////////////////////////////////
     
     void drawHardwareLayer(HardwareLayer layer, float x, float y, Paint paint) {
+        layer.setLayerPaint(paint);
+
         final GLES20Layer glLayer = (GLES20Layer) layer;
-        final int nativePaint = paint == null ? 0 : paint.mNativePaint;
-        nDrawLayer(mRenderer, glLayer.getLayer(), x, y, nativePaint);
+        nDrawLayer(mRenderer, glLayer.getLayer(), x, y);
     }
 
-    private static native void nDrawLayer(int renderer, int layer, float x, float y, int paint);
+    private static native void nDrawLayer(int renderer, int layer, float x, float y);
 
     void interrupt() {
         nInterrupt(mRenderer);
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index e086f5a..7918823 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -428,6 +428,8 @@
     interface HardwareDrawCallbacks {
         /**
          * Invoked before a view is drawn by a hardware renderer.
+         * This method can be used to apply transformations to the
+         * canvas but no drawing command should be issued.
          * 
          * @param canvas The Canvas used to render the view.
          */
@@ -435,6 +437,7 @@
 
         /**
          * Invoked after a view is drawn by a hardware renderer.
+         * It is safe to invoke drawing commands from this method.
          * 
          * @param canvas The Canvas used to render the view.
          */
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index f0c6241..a85a558 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -60,8 +60,12 @@
             in IInputContext inputContext);
     boolean inputMethodClientHasFocus(IInputMethodClient client);
 
+    void getInitialDisplaySize(int displayId, out Point size);
+    void getBaseDisplaySize(int displayId, out Point size);
     void setForcedDisplaySize(int displayId, int width, int height);
     void clearForcedDisplaySize(int displayId);
+    int getInitialDisplayDensity(int displayId);
+    int getBaseDisplayDensity(int displayId);
     void setForcedDisplayDensity(int displayId, int density);
     void clearForcedDisplayDensity(int displayId);
 
diff --git a/core/java/android/view/InputChannel.java b/core/java/android/view/InputChannel.java
index 523af04..a797176 100644
--- a/core/java/android/view/InputChannel.java
+++ b/core/java/android/view/InputChannel.java
@@ -78,7 +78,9 @@
      * Creates a new input channel pair.  One channel should be provided to the input
      * dispatcher and the other to the application's input queue.
      * @param name The descriptive (non-unique) name of the channel pair.
-     * @return A pair of input channels.  They are symmetric and indistinguishable.
+     * @return A pair of input channels.  The first channel is designated as the
+     * server channel and should be used to publish input events.  The second channel
+     * is designated as the client channel and should be used to consume input events.
      */
     public static InputChannel[] openInputChannelPair(String name) {
         if (name == null) {
@@ -123,10 +125,11 @@
         nativeTransferTo(outParameter);
     }
 
+    @Override
     public int describeContents() {
         return Parcelable.CONTENTS_FILE_DESCRIPTOR;
     }
-    
+
     public void readFromParcel(Parcel in) {
         if (in == null) {
             throw new IllegalArgumentException("in must not be null");
@@ -134,7 +137,8 @@
         
         nativeReadFromParcel(in);
     }
-    
+
+    @Override
     public void writeToParcel(Parcel out, int flags) {
         if (out == null) {
             throw new IllegalArgumentException("out must not be null");
diff --git a/core/java/android/view/InputEventSender.java b/core/java/android/view/InputEventSender.java
new file mode 100644
index 0000000..adf63fe
--- /dev/null
+++ b/core/java/android/view/InputEventSender.java
@@ -0,0 +1,140 @@
+/*
+ * 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.view;
+
+import dalvik.system.CloseGuard;
+
+import android.os.Looper;
+import android.os.MessageQueue;
+import android.util.Log;
+
+/**
+ * Provides a low-level mechanism for an application to send input events.
+ * @hide
+ */
+public abstract class InputEventSender {
+    private static final String TAG = "InputEventSender";
+
+    private final CloseGuard mCloseGuard = CloseGuard.get();
+
+    private int mSenderPtr;
+
+    // We keep references to the input channel and message queue objects here so that
+    // they are not GC'd while the native peer of the receiver is using them.
+    private InputChannel mInputChannel;
+    private MessageQueue mMessageQueue;
+
+    private static native int nativeInit(InputEventSender sender,
+            InputChannel inputChannel, MessageQueue messageQueue);
+    private static native void nativeDispose(int senderPtr);
+    private static native boolean nativeSendKeyEvent(int senderPtr, int seq, KeyEvent event);
+    private static native boolean nativeSendMotionEvent(int senderPtr, int seq, MotionEvent event);
+
+    /**
+     * Creates an input event sender bound to the specified input channel.
+     *
+     * @param inputChannel The input channel.
+     * @param looper The looper to use when invoking callbacks.
+     */
+    public InputEventSender(InputChannel inputChannel, Looper looper) {
+        if (inputChannel == null) {
+            throw new IllegalArgumentException("inputChannel must not be null");
+        }
+        if (looper == null) {
+            throw new IllegalArgumentException("looper must not be null");
+        }
+
+        mInputChannel = inputChannel;
+        mMessageQueue = looper.getQueue();
+        mSenderPtr = nativeInit(this, inputChannel, mMessageQueue);
+
+        mCloseGuard.open("dispose");
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            dispose(true);
+        } finally {
+            super.finalize();
+        }
+    }
+
+    /**
+     * Disposes the receiver.
+     */
+    public void dispose() {
+        dispose(false);
+    }
+
+    private void dispose(boolean finalized) {
+        if (mCloseGuard != null) {
+            if (finalized) {
+                mCloseGuard.warnIfOpen();
+            }
+            mCloseGuard.close();
+        }
+
+        if (mSenderPtr != 0) {
+            nativeDispose(mSenderPtr);
+            mSenderPtr = 0;
+        }
+        mInputChannel = null;
+        mMessageQueue = null;
+    }
+
+    /**
+     * Called when an input event is finished.
+     *
+     * @param seq The input event sequence number.
+     * @param handled True if the input event was handled.
+     */
+    public void onInputEventFinished(int seq, boolean handled) {
+    }
+
+    /**
+     * Sends an input event.
+     * Must be called on the same Looper thread to which the sender is attached.
+     *
+     * @param seq The input event sequence number.
+     * @param event The input event to send.
+     * @return True if the entire event was sent successfully.  May return false
+     * if the input channel buffer filled before all samples were dispatched.
+     */
+    public final boolean sendInputEvent(int seq, InputEvent event) {
+        if (event == null) {
+            throw new IllegalArgumentException("event must not be null");
+        }
+        if (mSenderPtr == 0) {
+            Log.w(TAG, "Attempted to send an input event but the input event "
+                    + "sender has already been disposed.");
+            return false;
+        }
+
+        if (event instanceof KeyEvent) {
+            return nativeSendKeyEvent(mSenderPtr, seq, (KeyEvent)event);
+        } else {
+            return nativeSendMotionEvent(mSenderPtr, seq, (MotionEvent)event);
+        }
+    }
+
+    // Called from native code.
+    @SuppressWarnings("unused")
+    private void dispatchInputEventFinished(int seq, boolean handled) {
+        onInputEventFinished(seq, handled);
+    }
+}
diff --git a/core/java/android/view/Overlay.java b/core/java/android/view/Overlay.java
index 210bc31..6630752 100644
--- a/core/java/android/view/Overlay.java
+++ b/core/java/android/view/Overlay.java
@@ -18,9 +18,10 @@
 import android.graphics.drawable.Drawable;
 
 /**
- * An overlay is an extra layer that sits on top of a View (the "host view") which is drawn after
- * all other content in that view (including children, if the view is a ViewGroup). Interaction
- * with the overlay layer is done in terms of adding/removing views and drawables.
+ * An overlay is an extra layer that sits on top of a View (the "host view")
+ * which is drawn after all other content in that view (including children,
+ * if the view is a ViewGroup). Interaction with the overlay layer is done in
+ * terms of adding/removing views and drawables.
  *
  * @see android.view.View#getOverlay()
  */
@@ -47,10 +48,16 @@
     void remove(Drawable drawable);
 
     /**
-     * Adds a View to the overlay. The bounds of the added view should be relative to
-     * the host view. Any view added to the overlay should be removed when it is no longer
-     * needed or no longer visible. The view must not be parented elsewhere when it is added
-     * to the overlay.
+     * Adds a View to the overlay. The bounds of the added view should be
+     * relative to the host view. Any view added to the overlay should be
+     * removed when it is no longer needed or no longer visible.
+     *
+     * <p>If the view has a parent, the view will be removed from that parent
+     * before being added to the overlay. Also, the view will be repositioned
+     * such that it is in the same relative location inside the activity. For
+     * example, if the view's current parent lies 100 pixels to the right
+     * and 200 pixels down from the origin of the overlay's
+     * host view, then the view will be offset by (100, 200).</p>
      *
      * @param view The View to be added to the overlay. The added view will be
      * drawn when the overlay is drawn.
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 34f5a2b..6207faa 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -9485,17 +9485,26 @@
      * <p>Sets the opacity of the view. This is a value from 0 to 1, where 0 means the view is
      * completely transparent and 1 means the view is completely opaque.</p>
      *
-     * <p>If this view overrides {@link #onSetAlpha(int)} to return true, then this view is
-     * responsible for applying the opacity itself. Otherwise, calling this method is
-     * equivalent to calling {@link #setLayerType(int, android.graphics.Paint)} and
-     * setting a hardware layer.</p>
+     * <p> Note that setting alpha to a translucent value (0 < alpha < 1) can have significant
+     * performance implications, especially for large views. It is best to use the alpha property
+     * sparingly and transiently, as in the case of fading animations.</p>
      *
-     * <p>Note that setting alpha to a translucent value (0 < alpha < 1) may have
-     * performance implications. It is generally best to use the alpha property sparingly and
-     * transiently, as in the case of fading animations.</p>
+     * <p>For a view with a frequently changing alpha, such as during a fading animation, it is
+     * strongly recommended for performance reasons to either override
+     * {@link #hasOverlappingRendering()} to return false if appropriate, or setting a
+     * {@link #setLayerType(int, android.graphics.Paint) layer type} on the view.</p>
+     *
+     * <p>If this view overrides {@link #onSetAlpha(int)} to return true, then this view is
+     * responsible for applying the opacity itself.</p>
+     *
+     * <p>Note that if the view is backed by a
+     * {@link #setLayerType(int, android.graphics.Paint) layer} and is associated with a
+     * {@link #setLayerPaint(android.graphics.Paint) layer paint}, setting an alpha value less than
+     * 1.0 will supercede the alpha of the layer paint.</p>
      *
      * @param alpha The opacity of the view.
      *
+     * @see #hasOverlappingRendering()
      * @see #setLayerType(int, android.graphics.Paint)
      *
      * @attr ref android.R.styleable#View_alpha
@@ -12098,7 +12107,7 @@
         //System.out.println("Attached! " + this);
         mAttachInfo = info;
         if (mOverlay != null) {
-            mOverlay.mAttachInfo = info;
+            mOverlay.dispatchAttachedToWindow(info, visibility);
         }
         mWindowAttachCount++;
         // We will need to evaluate the drawable state at least once.
@@ -12169,7 +12178,7 @@
 
         mAttachInfo = null;
         if (mOverlay != null) {
-            mOverlay.mAttachInfo = null;
+            mOverlay.dispatchDetachedFromWindow();
         }
     }
 
@@ -12365,13 +12374,11 @@
      * </ul>
      *
      * <p>If this view has an alpha value set to < 1.0 by calling
-     * {@link #setAlpha(float)}, the alpha value of the layer's paint is replaced by
-     * this view's alpha value. Calling {@link #setAlpha(float)} is therefore
-     * equivalent to setting a hardware layer on this view and providing a paint with
-     * the desired alpha value.</p>
+     * {@link #setAlpha(float)}, the alpha value of the layer's paint is superceded
+     * by this view's alpha value.</p>
      *
-     * <p>Refer to the documentation of {@link #LAYER_TYPE_NONE disabled},
-     * {@link #LAYER_TYPE_SOFTWARE software} and {@link #LAYER_TYPE_HARDWARE hardware}
+     * <p>Refer to the documentation of {@link #LAYER_TYPE_NONE},
+     * {@link #LAYER_TYPE_SOFTWARE} and {@link #LAYER_TYPE_HARDWARE}
      * for more information on when and how to use layers.</p>
      *
      * @param layerType The type of layer to use with this view, must be one of
@@ -12441,11 +12448,8 @@
      * <li>{@link android.graphics.Paint#getColorFilter() Color filter}</li>
      * </ul>
      *
-     * <p>If this view has an alpha value set to < 1.0 by calling
-     * {@link #setAlpha(float)}, the alpha value of the layer's paint is replaced by
-     * this view's alpha value. Calling {@link #setAlpha(float)} is therefore
-     * equivalent to setting a hardware layer on this view and providing a paint with
-     * the desired alpha value.</p>
+     * <p>If this view has an alpha value set to < 1.0 by calling {@link #setAlpha(float)}, the
+     * alpha value of the layer's paint is superceded by this view's alpha value.</p>
      *
      * @param paint The paint used to compose the layer. This argument is optional
      *        and can be null. It is ignored when the layer type is
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 987ff78..40b6a08 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -1054,8 +1054,7 @@
                 String categoryPrefix =
                         property.category().length() != 0 ? property.category() + ":" : "";
 
-                if (type == int.class) {
-
+                if (type == int.class || type == byte.class) {
                     if (property.resolveId() && context != null) {
                         final int id = field.getInt(view);
                         fieldValue = resolveId(context, id);
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index d63f7bc..725502d 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -51,6 +51,8 @@
 import java.util.Collections;
 import java.util.HashSet;
 
+import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
+
 /**
  * <p>
  * A <code>ViewGroup</code> is a special view that can contain other views
@@ -1868,13 +1870,37 @@
                     // have become out of sync.
                     removePointersFromTouchTargets(idBitsToAssign);
 
+                    final float x = ev.getX(actionIndex);
+                    final float y = ev.getY(actionIndex);
+
+                    if (mOverlay != null) {
+                        ViewOverlay overlay = (ViewOverlay) mOverlay;
+                        // Check to see whether the overlay can handle the event
+                        final View child = mOverlay;
+                        if (canViewReceivePointerEvents(child) &&
+                                isTransformedTouchPointInView(x, y, child, null)) {
+                            newTouchTarget = getTouchTarget(child);
+                            if (newTouchTarget != null) {
+                                newTouchTarget.pointerIdBits |= idBitsToAssign;
+                            } else {
+                                resetCancelNextUpFlag(child);
+                                if (dispatchTransformedTouchEvent(ev, false, child,
+                                        idBitsToAssign)) {
+                                    mLastTouchDownTime = ev.getDownTime();
+                                    mLastTouchDownX = ev.getX();
+                                    mLastTouchDownY = ev.getY();
+                                    newTouchTarget = addTouchTarget(child, idBitsToAssign);
+                                    alreadyDispatchedToNewTouchTarget = true;
+                                }
+                            }
+                        }
+                    }
+
                     final int childrenCount = mChildrenCount;
-                    if (childrenCount != 0 || mOverlay != null) {
+                    if (newTouchTarget == null && childrenCount != 0) {
                         // Find a child that can receive the event.
                         // Scan children from front to back.
                         final View[] children = mChildren;
-                        final float x = ev.getX(actionIndex);
-                        final float y = ev.getY(actionIndex);
 
                         final boolean customOrder = isChildrenDrawingOrderEnabled();
                         for (int i = childrenCount - 1; i >= 0; i--) {
@@ -1906,27 +1932,6 @@
                                 break;
                             }
                         }
-                        if (mOverlay != null && newTouchTarget == null) {
-                            // Check to see whether the overlay can handle the event
-                            final View child = mOverlay;
-                            if (canViewReceivePointerEvents(child) &&
-                                    isTransformedTouchPointInView(x, y, child, null)) {
-                                newTouchTarget = getTouchTarget(child);
-                                if (newTouchTarget != null) {
-                                    newTouchTarget.pointerIdBits |= idBitsToAssign;
-                                } else {
-                                    resetCancelNextUpFlag(child);
-                                    if (dispatchTransformedTouchEvent(ev, false, child,
-                                            idBitsToAssign)) {
-                                        mLastTouchDownTime = ev.getDownTime();
-                                        mLastTouchDownX = ev.getX();
-                                        mLastTouchDownY = ev.getY();
-                                        newTouchTarget = addTouchTarget(child, idBitsToAssign);
-                                        alreadyDispatchedToNewTouchTarget = true;
-                                    }
-                                }
-                            }
-                        }
                     }
 
                     if (newTouchTarget == null && mFirstTouchTarget != null) {
@@ -1957,7 +1962,7 @@
                         handled = true;
                     } else {
                         final boolean cancelChild = resetCancelNextUpFlag(target.child)
-                        || intercepted;
+                                || intercepted;
                         if (dispatchTransformedTouchEvent(ev, cancelChild,
                                 target.child, target.pointerIdBits)) {
                             handled = true;
@@ -5861,7 +5866,7 @@
          * to this field.
          */
         @ViewDebug.ExportedProperty(category = "layout")
-        private int startMargin = DEFAULT_RELATIVE;
+        private int startMargin = DEFAULT_MARGIN_RELATIVE;
 
         /**
          * The end margin in pixels of the child.
@@ -5869,21 +5874,48 @@
          * to this field.
          */
         @ViewDebug.ExportedProperty(category = "layout")
-        private int endMargin = DEFAULT_RELATIVE;
+        private int endMargin = DEFAULT_MARGIN_RELATIVE;
 
         /**
          * The default start and end margin.
          * @hide
          */
-        public static final int DEFAULT_RELATIVE = Integer.MIN_VALUE;
+        public static final int DEFAULT_MARGIN_RELATIVE = Integer.MIN_VALUE;
 
-        private int initialLeftMargin;
-        private int initialRightMargin;
+        /**
+         * Bit  0: layout direction
+         * Bit  1: layout direction
+         * Bit  2: left margin undefined
+         * Bit  3: right margin undefined
+         * Bit  4: is RTL compatibility mode
+         * Bit  5: need resolution
+         *
+         * Bit 6 to 7 not used
+         *
+         * @hide
+         */
+        @ViewDebug.ExportedProperty(category = "layout", flagMapping = {
+                @ViewDebug.FlagToString(mask = LAYOUT_DIRECTION_MASK,
+                        equals = LAYOUT_DIRECTION_MASK, name = "LAYOUT_DIRECTION"),
+                @ViewDebug.FlagToString(mask = LEFT_MARGIN_UNDEFINED_MASK,
+                        equals = LEFT_MARGIN_UNDEFINED_MASK, name = "LEFT_MARGIN_UNDEFINED_MASK"),
+                @ViewDebug.FlagToString(mask = RIGHT_MARGIN_UNDEFINED_MASK,
+                        equals = RIGHT_MARGIN_UNDEFINED_MASK, name = "RIGHT_MARGIN_UNDEFINED_MASK"),
+                @ViewDebug.FlagToString(mask = RTL_COMPATIBILITY_MODE_MASK,
+                        equals = RTL_COMPATIBILITY_MODE_MASK, name = "RTL_COMPATIBILITY_MODE_MASK"),
+                @ViewDebug.FlagToString(mask = NEED_RESOLUTION_MASK,
+                        equals = NEED_RESOLUTION_MASK, name = "NEED_RESOLUTION_MASK")
+        })
+        byte mMarginFlags;
 
-        private static int LAYOUT_DIRECTION_UNDEFINED = -1;
+        private static final int LAYOUT_DIRECTION_MASK = 0x00000003;
+        private static final int LEFT_MARGIN_UNDEFINED_MASK = 0x00000004;
+        private static final int RIGHT_MARGIN_UNDEFINED_MASK = 0x00000008;
+        private static final int RTL_COMPATIBILITY_MODE_MASK = 0x00000010;
+        private static final int NEED_RESOLUTION_MASK = 0x00000020;
 
-        // Layout direction undefined by default
-        private int layoutDirection = LAYOUT_DIRECTION_UNDEFINED;
+        private static final int DEFAULT_MARGIN_RESOLVED = 0;
+        private static final int UNDEFINED_MARGIN = DEFAULT_MARGIN_RELATIVE;
 
         /**
          * Creates a new set of layout parameters. The values are extracted from
@@ -5910,21 +5942,47 @@
                 bottomMargin = margin;
             } else {
                 leftMargin = a.getDimensionPixelSize(
-                        R.styleable.ViewGroup_MarginLayout_layout_marginLeft, 0);
-                topMargin = a.getDimensionPixelSize(
-                        R.styleable.ViewGroup_MarginLayout_layout_marginTop, 0);
+                        R.styleable.ViewGroup_MarginLayout_layout_marginLeft,
+                        UNDEFINED_MARGIN);
+                if (leftMargin == UNDEFINED_MARGIN) {
+                    mMarginFlags |= LEFT_MARGIN_UNDEFINED_MASK;
+                    leftMargin = DEFAULT_MARGIN_RESOLVED;
+                }
                 rightMargin = a.getDimensionPixelSize(
-                        R.styleable.ViewGroup_MarginLayout_layout_marginRight, 0);
+                        R.styleable.ViewGroup_MarginLayout_layout_marginRight,
+                        UNDEFINED_MARGIN);
+                if (rightMargin == UNDEFINED_MARGIN) {
+                    mMarginFlags |= RIGHT_MARGIN_UNDEFINED_MASK;
+                    rightMargin = DEFAULT_MARGIN_RESOLVED;
+                }
+
+                topMargin = a.getDimensionPixelSize(
+                        R.styleable.ViewGroup_MarginLayout_layout_marginTop,
+                        DEFAULT_MARGIN_RESOLVED);
                 bottomMargin = a.getDimensionPixelSize(
-                        R.styleable.ViewGroup_MarginLayout_layout_marginBottom, 0);
+                        R.styleable.ViewGroup_MarginLayout_layout_marginBottom,
+                        DEFAULT_MARGIN_RESOLVED);
+
                 startMargin = a.getDimensionPixelSize(
-                        R.styleable.ViewGroup_MarginLayout_layout_marginStart, DEFAULT_RELATIVE);
+                        R.styleable.ViewGroup_MarginLayout_layout_marginStart,
+                        DEFAULT_MARGIN_RELATIVE);
                 endMargin = a.getDimensionPixelSize(
-                        R.styleable.ViewGroup_MarginLayout_layout_marginEnd, DEFAULT_RELATIVE);
+                        R.styleable.ViewGroup_MarginLayout_layout_marginEnd,
+                        DEFAULT_MARGIN_RELATIVE);
+
+                if (isMarginRelative()) {
+                   mMarginFlags |= NEED_RESOLUTION_MASK;
+                }
             }
 
-            initialLeftMargin = leftMargin;
-            initialRightMargin = rightMargin;
+            final boolean hasRtlSupport = c.getApplicationInfo().hasRtlSupport();
+            final int targetSdkVersion = c.getApplicationInfo().targetSdkVersion;
+            if (targetSdkVersion < JELLY_BEAN_MR1 || !hasRtlSupport) {
+                mMarginFlags |= RTL_COMPATIBILITY_MODE_MASK;
+            }
+
+            // Layout direction is LTR by default
+            mMarginFlags |= LAYOUT_DIRECTION_LTR;
 
             a.recycle();
         }
@@ -5934,6 +5992,12 @@
          */
         public MarginLayoutParams(int width, int height) {
             super(width, height);
+
+            mMarginFlags |= LEFT_MARGIN_UNDEFINED_MASK;
+            mMarginFlags |= RIGHT_MARGIN_UNDEFINED_MASK;
+
+            mMarginFlags &= ~NEED_RESOLUTION_MASK;
+            mMarginFlags &= ~RTL_COMPATIBILITY_MODE_MASK;
         }
 
         /**
@@ -5952,10 +6016,7 @@
             this.startMargin = source.startMargin;
             this.endMargin = source.endMargin;
 
-            this.initialLeftMargin = source.leftMargin;
-            this.initialRightMargin = source.rightMargin;
-
-            setLayoutDirection(source.layoutDirection);
+            this.mMarginFlags = source.mMarginFlags;
         }
 
         /**
@@ -5963,6 +6024,12 @@
          */
         public MarginLayoutParams(LayoutParams source) {
             super(source);
+
+            mMarginFlags |= LEFT_MARGIN_UNDEFINED_MASK;
+            mMarginFlags |= RIGHT_MARGIN_UNDEFINED_MASK;
+
+            mMarginFlags &= ~NEED_RESOLUTION_MASK;
+            mMarginFlags &= ~RTL_COMPATIBILITY_MODE_MASK;
         }
 
         /**
@@ -5985,8 +6052,13 @@
             topMargin = top;
             rightMargin = right;
             bottomMargin = bottom;
-            initialLeftMargin = left;
-            initialRightMargin = right;
+            mMarginFlags &= ~LEFT_MARGIN_UNDEFINED_MASK;
+            mMarginFlags &= ~RIGHT_MARGIN_UNDEFINED_MASK;
+            if (isMarginRelative()) {
+                mMarginFlags |= NEED_RESOLUTION_MASK;
+            } else {
+                mMarginFlags &= ~NEED_RESOLUTION_MASK;
+            }
         }
 
         /**
@@ -6012,8 +6084,7 @@
             topMargin = top;
             endMargin = end;
             bottomMargin = bottom;
-            initialLeftMargin = 0;
-            initialRightMargin = 0;
+            mMarginFlags |= NEED_RESOLUTION_MASK;
         }
 
         /**
@@ -6025,6 +6096,7 @@
          */
         public void setMarginStart(int start) {
             startMargin = start;
+            mMarginFlags |= NEED_RESOLUTION_MASK;
         }
 
         /**
@@ -6035,8 +6107,11 @@
          * @return the start margin in pixels.
          */
         public int getMarginStart() {
-            if (startMargin != DEFAULT_RELATIVE) return startMargin;
-            switch(layoutDirection) {
+            if (startMargin != DEFAULT_MARGIN_RELATIVE) return startMargin;
+            if ((mMarginFlags & NEED_RESOLUTION_MASK) == NEED_RESOLUTION_MASK) {
+                doResolveMargins();
+            }
+            switch(mMarginFlags & LAYOUT_DIRECTION_MASK) {
                 case View.LAYOUT_DIRECTION_RTL:
                     return rightMargin;
                 case View.LAYOUT_DIRECTION_LTR:
@@ -6054,6 +6129,7 @@
          */
         public void setMarginEnd(int end) {
             endMargin = end;
+            mMarginFlags |= NEED_RESOLUTION_MASK;
         }
 
         /**
@@ -6064,8 +6140,11 @@
          * @return the end margin in pixels.
          */
         public int getMarginEnd() {
-            if (endMargin != DEFAULT_RELATIVE) return endMargin;
-            switch(layoutDirection) {
+            if (endMargin != DEFAULT_MARGIN_RELATIVE) return endMargin;
+            if ((mMarginFlags & NEED_RESOLUTION_MASK) == NEED_RESOLUTION_MASK) {
+                doResolveMargins();
+            }
+            switch(mMarginFlags & LAYOUT_DIRECTION_MASK) {
                 case View.LAYOUT_DIRECTION_RTL:
                     return leftMargin;
                 case View.LAYOUT_DIRECTION_LTR:
@@ -6083,7 +6162,7 @@
          * @return true if either marginStart or marginEnd has been set.
          */
         public boolean isMarginRelative() {
-            return (startMargin != DEFAULT_RELATIVE) || (endMargin != DEFAULT_RELATIVE);
+            return (startMargin != DEFAULT_MARGIN_RELATIVE || endMargin != DEFAULT_MARGIN_RELATIVE);
         }
 
         /**
@@ -6095,7 +6174,15 @@
         public void setLayoutDirection(int layoutDirection) {
             if (layoutDirection != View.LAYOUT_DIRECTION_LTR &&
                     layoutDirection != View.LAYOUT_DIRECTION_RTL) return;
-            this.layoutDirection = layoutDirection;
+            if (layoutDirection != (mMarginFlags & LAYOUT_DIRECTION_MASK)) {
+                mMarginFlags &= ~LAYOUT_DIRECTION_MASK;
+                mMarginFlags |= (layoutDirection & LAYOUT_DIRECTION_MASK);
+                if (isMarginRelative()) {
+                    mMarginFlags |= NEED_RESOLUTION_MASK;
+                } else {
+                    mMarginFlags &= ~NEED_RESOLUTION_MASK;
+                }
+            }
         }
 
         /**
@@ -6105,7 +6192,7 @@
          * @return the layout direction.
          */
         public int getLayoutDirection() {
-            return layoutDirection;
+            return (mMarginFlags & LAYOUT_DIRECTION_MASK);
         }
 
         /**
@@ -6116,26 +6203,55 @@
         public void resolveLayoutDirection(int layoutDirection) {
             setLayoutDirection(layoutDirection);
 
-            if (!isMarginRelative()) return;
+            // No relative margin or pre JB-MR1 case or no need to resolve, just dont do anything
+            // Will use the left and right margins if no relative margin is defined.
+            if (!isMarginRelative() ||
+                    (mMarginFlags & NEED_RESOLUTION_MASK) != NEED_RESOLUTION_MASK) return;
 
-            switch(layoutDirection) {
-                case View.LAYOUT_DIRECTION_RTL:
-                    leftMargin = (endMargin > DEFAULT_RELATIVE) ? endMargin : initialLeftMargin;
-                    rightMargin = (startMargin > DEFAULT_RELATIVE) ? startMargin : initialRightMargin;
-                    break;
-                case View.LAYOUT_DIRECTION_LTR:
-                default:
-                    leftMargin = (startMargin > DEFAULT_RELATIVE) ? startMargin : initialLeftMargin;
-                    rightMargin = (endMargin > DEFAULT_RELATIVE) ? endMargin : initialRightMargin;
-                    break;
+            // Proceed with resolution
+            doResolveMargins();
+        }
+
+        private void doResolveMargins() {
+            if ((mMarginFlags & RTL_COMPATIBILITY_MODE_MASK) == RTL_COMPATIBILITY_MODE_MASK) {
+                // if left or right margins are not defined and if we have some start or end margin
+                // defined then use those start and end margins.
+                if ((mMarginFlags & LEFT_MARGIN_UNDEFINED_MASK) == LEFT_MARGIN_UNDEFINED_MASK
+                        && startMargin > DEFAULT_MARGIN_RELATIVE) {
+                    leftMargin = startMargin;
+                }
+                if ((mMarginFlags & RIGHT_MARGIN_UNDEFINED_MASK) == RIGHT_MARGIN_UNDEFINED_MASK
+                        && endMargin > DEFAULT_MARGIN_RELATIVE) {
+                    rightMargin = endMargin;
+                }
+            } else {
+                // We have some relative margins (either the start one or the end one or both). So use
+                // them and override what has been defined for left and right margins. If either start
+                // or end margin is not defined, just set it to default "0".
+                switch(mMarginFlags & LAYOUT_DIRECTION_MASK) {
+                    case View.LAYOUT_DIRECTION_RTL:
+                        leftMargin = (endMargin > DEFAULT_MARGIN_RELATIVE) ?
+                                endMargin : DEFAULT_MARGIN_RESOLVED;
+                        rightMargin = (startMargin > DEFAULT_MARGIN_RELATIVE) ?
+                                startMargin : DEFAULT_MARGIN_RESOLVED;
+                        break;
+                    case View.LAYOUT_DIRECTION_LTR:
+                    default:
+                        leftMargin = (startMargin > DEFAULT_MARGIN_RELATIVE) ?
+                                startMargin : DEFAULT_MARGIN_RESOLVED;
+                        rightMargin = (endMargin > DEFAULT_MARGIN_RELATIVE) ?
+                                endMargin : DEFAULT_MARGIN_RESOLVED;
+                        break;
+                }
             }
+            mMarginFlags &= ~NEED_RESOLUTION_MASK;
         }
 
         /**
          * @hide
          */
         public boolean isLayoutRtl() {
-            return (layoutDirection == View.LAYOUT_DIRECTION_RTL);
+            return ((mMarginFlags & LAYOUT_DIRECTION_MASK) == View.LAYOUT_DIRECTION_RTL);
         }
 
         /**
diff --git a/core/java/android/view/ViewOverlay.java b/core/java/android/view/ViewOverlay.java
index 939377d..8b18d53 100644
--- a/core/java/android/view/ViewOverlay.java
+++ b/core/java/android/view/ViewOverlay.java
@@ -23,17 +23,21 @@
 import java.util.ArrayList;
 
 /**
- * ViewOverlay is a container that View uses to host all objects (views and drawables) that
- * are added to its "overlay", gotten through {@link View#getOverlay()}. Views and drawables are
- * added to the overlay via the add/remove methods in this class. These views and drawables are
- * drawn whenever the view itself is drawn; first the view draws its own content (and children,
- * if it is a ViewGroup), then it draws its overlay (if it has one).
+ * ViewOverlay is a container that View uses to host all objects (views and
+ * drawables) that are added to its "overlay", gotten through
+ * {@link View#getOverlay()}. Views and drawables are added to the overlay
+ * via the add/remove methods in this class. These views and drawables are
+ * drawn whenever the view itself is drawn; first the view draws its own
+ * content (and children, if it is a ViewGroup), then it draws its overlay
+ * (if it has one).
  *
- * Besides managing and drawing the list of drawables, this class serves two purposes:
+ * Besides managing and drawing the list of drawables, this class serves
+ * two purposes:
  * (1) it noops layout calls because children are absolutely positioned and
- * (2) it forwards all invalidation calls to its host view. The invalidation redirect is
- * necessary because the overlay is not a child of the host view and invalidation cannot
- * therefore follow the normal path up through the parent hierarchy.
+ * (2) it forwards all invalidation calls to its host view. The invalidation
+ * redirect is necessary because the overlay is not a child of the host view
+ * and invalidation cannot therefore follow the normal path up through the
+ * parent hierarchy.
  *
  * @hide
  */
@@ -85,6 +89,22 @@
 
     @Override
     public void add(View child) {
+        int deltaX = 0;
+        int deltaY = 0;
+        if (child.getParent() instanceof ViewGroup) {
+            ViewGroup parent = (ViewGroup) child.getParent();
+            if (parent != mHostView) {
+                // Moving to different container; figure out how to position child such that
+                // it is in the same location on the screen
+                int[] parentLocation = new int[2];
+                int[] hostViewLocation = new int[2];
+                parent.getLocationOnScreen(parentLocation);
+                mHostView.getLocationOnScreen(hostViewLocation);
+                child.offsetLeftAndRight(parentLocation[0] - hostViewLocation[0]);
+                child.offsetTopAndBottom(parentLocation[1] - hostViewLocation[1]);
+            }
+            parent.removeView(child);
+        }
         super.addView(child);
     }
 
@@ -133,7 +153,6 @@
     public void invalidate(Rect dirty) {
         super.invalidate(dirty);
         if (mHostView != null) {
-            dirty.offset(getLeft(), getTop());
             mHostView.invalidate(dirty);
         }
     }
@@ -203,7 +222,15 @@
     @Override
     public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
         if (mHostView != null) {
-            mHostView.invalidate(dirty);
+            dirty.offset(location[0], location[1]);
+            if (mHostView instanceof ViewGroup) {
+                location[0] = 0;
+                location[1] = 0;
+                super.invalidateChildInParent(location, dirty);
+                return ((ViewGroup) mHostView).invalidateChildInParent(location, dirty);
+            } else {
+                invalidate(dirty);
+            }
         }
         return null;
     }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index f06387c..9387624 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -107,6 +107,7 @@
     private static final boolean DEBUG_IMF = false || LOCAL_LOGV;
     private static final boolean DEBUG_CONFIGURATION = false || LOCAL_LOGV;
     private static final boolean DEBUG_FPS = false;
+    private static final boolean DEBUG_INPUT_PROCESSING = false || LOCAL_LOGV;
 
     private static final boolean USE_RENDER_THREAD = false;
 
@@ -231,24 +232,38 @@
     int mClientWindowLayoutFlags;
     boolean mLastOverscanRequested;
 
-    /** @hide */
+     /** Event was not handled and is finished.
+      * @hide */
     public static final int EVENT_NOT_HANDLED = 0;
-    /** @hide */
+     /** Event was handled and is finished.
+      * @hide */
     public static final int EVENT_HANDLED = 1;
-    /** @hide */
-    public static final int EVENT_IN_PROGRESS = 2;
+    /** Event is waiting on the IME.
+     * @hide */
+    public static final int EVENT_PENDING_IME = 2;
+    /** Event requires post-IME dispatch.
+     * @hide */
+    public static final int EVENT_POST_IME = 3;
 
     // Pool of queued input events.
     private static final int MAX_QUEUED_INPUT_EVENT_POOL_SIZE = 10;
     private QueuedInputEvent mQueuedInputEventPool;
     private int mQueuedInputEventPoolSize;
 
-    // Input event queue.
-    QueuedInputEvent mFirstPendingInputEvent;
+    /* Input event queue.
+     * Pending input events are input events waiting to be handled by the application. Current
+     * input events are input events which are being handled but are waiting on some action by the
+     * IME, even if they themselves may not need to be handled by the IME.
+     */
+    QueuedInputEvent mPendingInputEventHead;
+    QueuedInputEvent mPendingInputEventTail;
     int mPendingInputEventCount;
-    QueuedInputEvent mCurrentInputEvent;
+    QueuedInputEvent mActiveInputEventHead;
+    QueuedInputEvent mActiveInputEventTail;
+    int mActiveInputEventCount;
     boolean mProcessInputEventsScheduled;
     String mPendingInputEventQueueLengthCounterName = "pq";
+    String mActiveInputEventQueueLengthCounterName = "aq";
 
     boolean mWindowAttributesChanged = false;
     int mWindowAttributesChangesFlag = 0;
@@ -646,6 +661,7 @@
                 }
 
                 mPendingInputEventQueueLengthCounterName = "pq:" + attrs.getTitle();
+                mActiveInputEventQueueLengthCounterName = "aq:" + attrs.getTitle();
             }
         }
     }
@@ -1217,6 +1233,7 @@
                 host.setLayoutDirection(mLastConfiguration.getLayoutDirection());
             }
             host.dispatchAttachedToWindow(attachInfo, 0);
+            attachInfo.mTreeObserver.dispatchOnWindowAttachedChange(true);
             mFitSystemWindowsInsets.set(mAttachInfo.mContentInsets);
             host.fitSystemWindows(mFitSystemWindowsInsets);
             //Log.i(TAG, "Screen on initialized: " + attachInfo.mKeepScreenOn);
@@ -1408,6 +1425,8 @@
 
                 final int surfaceGenerationId = mSurface.getGenerationId();
                 relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
+                mWindowsAnimating |=
+                        (relayoutResult & WindowManagerGlobal.RELAYOUT_RES_ANIMATING) != 0;
 
                 if (DEBUG_LAYOUT) Log.v(TAG, "relayout: frame=" + frame.toShortString()
                         + " overscan=" + mPendingOverscanInsets.toShortString()
@@ -2811,6 +2830,7 @@
                     mAttachInfo.mHardwareRenderer.isEnabled()) {
                 mAttachInfo.mHardwareRenderer.validate();
             }
+            mAttachInfo.mTreeObserver.dispatchOnWindowAttachedChange(false);
             mView.dispatchDetachedFromWindow();
         }
 
@@ -2919,7 +2939,6 @@
     private final static int MSG_DISPATCH_KEY = 7;
     private final static int MSG_DISPATCH_APP_VISIBILITY = 8;
     private final static int MSG_DISPATCH_GET_NEW_SURFACE = 9;
-    private final static int MSG_IME_FINISHED_EVENT = 10;
     private final static int MSG_DISPATCH_KEY_FROM_IME = 11;
     private final static int MSG_FINISH_INPUT_CONNECTION = 12;
     private final static int MSG_CHECK_FOCUS = 13;
@@ -2934,6 +2953,8 @@
     private final static int MSG_DISPATCH_DONE_ANIMATING = 22;
     private final static int MSG_INVALIDATE_WORLD = 23;
     private final static int MSG_WINDOW_MOVED = 24;
+    private final static int MSG_ENQUEUE_X_AXIS_KEY_REPEAT = 25;
+    private final static int MSG_ENQUEUE_Y_AXIS_KEY_REPEAT = 26;
 
     final class ViewRootHandler extends Handler {
         @Override
@@ -2957,8 +2978,6 @@
                     return "MSG_DISPATCH_APP_VISIBILITY";
                 case MSG_DISPATCH_GET_NEW_SURFACE:
                     return "MSG_DISPATCH_GET_NEW_SURFACE";
-                case MSG_IME_FINISHED_EVENT:
-                    return "MSG_IME_FINISHED_EVENT";
                 case MSG_DISPATCH_KEY_FROM_IME:
                     return "MSG_DISPATCH_KEY_FROM_IME";
                 case MSG_FINISH_INPUT_CONNECTION:
@@ -2985,6 +3004,10 @@
                     return "MSG_DISPATCH_DONE_ANIMATING";
                 case MSG_WINDOW_MOVED:
                     return "MSG_WINDOW_MOVED";
+                case MSG_ENQUEUE_X_AXIS_KEY_REPEAT:
+                    return "MSG_ENQUEUE_X_AXIS_KEY_REPEAT";
+                case MSG_ENQUEUE_Y_AXIS_KEY_REPEAT:
+                    return "MSG_ENQUEUE_Y_AXIS_KEY_REPEAT";
             }
             return super.getMessageName(message);
         }
@@ -3000,9 +3023,6 @@
                 info.target.invalidate(info.left, info.top, info.right, info.bottom);
                 info.recycle();
                 break;
-            case MSG_IME_FINISHED_EVENT:
-                handleImeFinishedEvent(msg.arg1, msg.arg2 != 0);
-                break;
             case MSG_PROCESS_INPUT_EVENTS:
                 mProcessInputEventsScheduled = false;
                 doProcessInputEvents();
@@ -3111,6 +3131,7 @@
                         }
                         mAttachInfo.mKeyDispatchState.reset();
                         mView.dispatchWindowFocusChanged(hasWindowFocus);
+                        mAttachInfo.mTreeObserver.dispatchOnWindowFocusChange(hasWindowFocus);
                     }
 
                     // Note: must be done after the focus change callbacks,
@@ -3210,6 +3231,18 @@
                     invalidateWorld(mView);
                 }
             } break;
+            case MSG_ENQUEUE_X_AXIS_KEY_REPEAT:
+            case MSG_ENQUEUE_Y_AXIS_KEY_REPEAT: {
+                KeyEvent oldEvent = (KeyEvent)msg.obj;
+                KeyEvent e = KeyEvent.changeTimeRepeat(oldEvent, SystemClock.uptimeMillis(),
+                        oldEvent.getRepeatCount() + 1);
+                if (mAttachInfo.mHasWindowFocus) {
+                    enqueueInputEvent(e);
+                    Message m = obtainMessage(msg.what, e);
+                    m.setAsynchronous(true);
+                    sendMessageDelayed(m, mViewConfiguration.getKeyRepeatDelay());
+                }
+            } break;
             }
         }
     }
@@ -3425,30 +3458,15 @@
             mInputEventConsistencyVerifier.onTrackballEvent(event, 0);
         }
 
+        int result = EVENT_POST_IME;
         if (mView != null && mAdded && (q.mFlags & QueuedInputEvent.FLAG_DELIVER_POST_IME) == 0) {
             if (LOCAL_LOGV)
                 Log.v(TAG, "Dispatching trackball " + event + " to " + mView);
 
             // Dispatch to the IME before propagating down the view hierarchy.
-            // The IME will eventually call back into handleImeFinishedEvent.
-            if (mLastWasImTarget) {
-                InputMethodManager imm = InputMethodManager.peekInstance();
-                if (imm != null) {
-                    final int seq = event.getSequenceNumber();
-                    if (DEBUG_IMF)
-                        Log.v(TAG, "Sending trackball event to IME: seq="
-                                + seq + " event=" + event);
-                    int result = imm.dispatchTrackballEvent(mView.getContext(), seq, event,
-                            mInputMethodCallback);
-                    if (result != EVENT_NOT_HANDLED) {
-                        return result;
-                    }
-                }
-            }
+            result = dispatchImeInputEvent(q);
         }
-
-        // Not dispatching to IME, continue with post IME actions.
-        return deliverTrackballEventPostIme(q);
+        return result;
     }
 
     private int deliverTrackballEventPostIme(QueuedInputEvent q) {
@@ -3547,7 +3565,7 @@
                     + " accelMovement=" + accelMovement
                     + " accel=" + accel);
             if (accelMovement > movement) {
-                if (DEBUG_TRACKBALL) Log.v("foo", "Delivering fake DPAD: "
+                if (DEBUG_TRACKBALL) Log.v(TAG, "Delivering fake DPAD: "
                         + keycode);
                 movement--;
                 int repeatCount = accelMovement - movement;
@@ -3557,7 +3575,7 @@
                         InputDevice.SOURCE_KEYBOARD));
             }
             while (movement > 0) {
-                if (DEBUG_TRACKBALL) Log.v("foo", "Delivering fake DPAD: "
+                if (DEBUG_TRACKBALL) Log.v(TAG, "Delivering fake DPAD: "
                         + keycode);
                 movement--;
                 curTime = SystemClock.uptimeMillis();
@@ -3583,30 +3601,16 @@
         if (mInputEventConsistencyVerifier != null) {
             mInputEventConsistencyVerifier.onGenericMotionEvent(event, 0);
         }
+
+        int result = EVENT_POST_IME;
         if (mView != null && mAdded && (q.mFlags & QueuedInputEvent.FLAG_DELIVER_POST_IME) == 0) {
             if (LOCAL_LOGV)
                 Log.v(TAG, "Dispatching generic motion " + event + " to " + mView);
 
             // Dispatch to the IME before propagating down the view hierarchy.
-            // The IME will eventually call back into handleImeFinishedEvent.
-            if (mLastWasImTarget) {
-                InputMethodManager imm = InputMethodManager.peekInstance();
-                if (imm != null) {
-                    final int seq = event.getSequenceNumber();
-                    if (DEBUG_IMF)
-                        Log.v(TAG, "Sending generic motion event to IME: seq="
-                                + seq + " event=" + event);
-                    int result = imm.dispatchGenericMotionEvent(mView.getContext(), seq, event,
-                            mInputMethodCallback);
-                    if (result != EVENT_NOT_HANDLED) {
-                        return result;
-                    }
-                }
-            }
+            result = dispatchImeInputEvent(q);
         }
-
-        // Not dispatching to IME, continue with post IME actions.
-        return deliverGenericMotionEventPostIme(q);
+        return result;
     }
 
     private int deliverGenericMotionEventPostIme(QueuedInputEvent q) {
@@ -3666,6 +3670,7 @@
 
         if (xDirection != mLastJoystickXDirection) {
             if (mLastJoystickXKeyCode != 0) {
+                mHandler.removeMessages(MSG_ENQUEUE_X_AXIS_KEY_REPEAT);
                 enqueueInputEvent(new KeyEvent(time, time,
                         KeyEvent.ACTION_UP, mLastJoystickXKeyCode, 0, metaState,
                         deviceId, 0, KeyEvent.FLAG_FALLBACK, source));
@@ -3677,14 +3682,19 @@
             if (xDirection != 0 && synthesizeNewKeys) {
                 mLastJoystickXKeyCode = xDirection > 0
                         ? KeyEvent.KEYCODE_DPAD_RIGHT : KeyEvent.KEYCODE_DPAD_LEFT;
-                enqueueInputEvent(new KeyEvent(time, time,
+                final KeyEvent e = new KeyEvent(time, time,
                         KeyEvent.ACTION_DOWN, mLastJoystickXKeyCode, 0, metaState,
-                        deviceId, 0, KeyEvent.FLAG_FALLBACK, source));
+                        deviceId, 0, KeyEvent.FLAG_FALLBACK, source);
+                enqueueInputEvent(e);
+                Message m = mHandler.obtainMessage(MSG_ENQUEUE_X_AXIS_KEY_REPEAT, e);
+                m.setAsynchronous(true);
+                mHandler.sendMessageDelayed(m, mViewConfiguration.getKeyRepeatTimeout());
             }
         }
 
         if (yDirection != mLastJoystickYDirection) {
             if (mLastJoystickYKeyCode != 0) {
+                mHandler.removeMessages(MSG_ENQUEUE_Y_AXIS_KEY_REPEAT);
                 enqueueInputEvent(new KeyEvent(time, time,
                         KeyEvent.ACTION_UP, mLastJoystickYKeyCode, 0, metaState,
                         deviceId, 0, KeyEvent.FLAG_FALLBACK, source));
@@ -3696,9 +3706,13 @@
             if (yDirection != 0 && synthesizeNewKeys) {
                 mLastJoystickYKeyCode = yDirection > 0
                         ? KeyEvent.KEYCODE_DPAD_DOWN : KeyEvent.KEYCODE_DPAD_UP;
-                enqueueInputEvent(new KeyEvent(time, time,
+                final KeyEvent e = new KeyEvent(time, time,
                         KeyEvent.ACTION_DOWN, mLastJoystickYKeyCode, 0, metaState,
-                        deviceId, 0, KeyEvent.FLAG_FALLBACK, source));
+                        deviceId, 0, KeyEvent.FLAG_FALLBACK, source);
+                enqueueInputEvent(e);
+                Message m = mHandler.obtainMessage(MSG_ENQUEUE_Y_AXIS_KEY_REPEAT, e);
+                m.setAsynchronous(true);
+                mHandler.sendMessageDelayed(m, mViewConfiguration.getKeyRepeatTimeout());
             }
         }
     }
@@ -3795,6 +3809,7 @@
             mInputEventConsistencyVerifier.onKeyEvent(event, 0);
         }
 
+        int result = EVENT_POST_IME;
         if (mView != null && mAdded && (q.mFlags & QueuedInputEvent.FLAG_DELIVER_POST_IME) == 0) {
             if (LOCAL_LOGV) Log.v(TAG, "Dispatching key " + event + " to " + mView);
 
@@ -3804,24 +3819,9 @@
             }
 
             // Dispatch to the IME before propagating down the view hierarchy.
-            // The IME will eventually call back into handleImeFinishedEvent.
-            if (mLastWasImTarget) {
-                InputMethodManager imm = InputMethodManager.peekInstance();
-                if (imm != null) {
-                    final int seq = event.getSequenceNumber();
-                    if (DEBUG_IMF) Log.v(TAG, "Sending key event to IME: seq="
-                            + seq + " event=" + event);
-                    int result = imm.dispatchKeyEvent(mView.getContext(), seq, event,
-                            mInputMethodCallback);
-                    if (result != EVENT_NOT_HANDLED) {
-                        return result;
-                    }
-                }
-            }
+            result = dispatchImeInputEvent(q);
         }
-
-        // Not dispatching to IME, continue with post IME actions.
-        return deliverKeyEventPostIme(q);
+        return result;
     }
 
     private int deliverKeyEventPostIme(QueuedInputEvent q) {
@@ -4310,14 +4310,6 @@
         }
     }
 
-    void dispatchImeFinishedEvent(int seq, boolean handled) {
-        Message msg = mHandler.obtainMessage(MSG_IME_FINISHED_EVENT);
-        msg.arg1 = seq;
-        msg.arg2 = handled ? 1 : 0;
-        msg.setAsynchronous(true);
-        mHandler.sendMessage(msg);
-    }
-
     public void dispatchFinishInputConnection(InputConnection connection) {
         Message msg = mHandler.obtainMessage(MSG_FINISH_INPUT_CONNECTION, connection);
         mHandler.sendMessage(msg);
@@ -4422,14 +4414,13 @@
         // in response to touch events and we want to ensure that the injected keys
         // are processed in the order they were received and we cannot trust that
         // the time stamp of injected events are monotonic.
-        QueuedInputEvent last = mFirstPendingInputEvent;
+        QueuedInputEvent last = mPendingInputEventTail;
         if (last == null) {
-            mFirstPendingInputEvent = q;
+            mPendingInputEventHead = q;
+            mPendingInputEventTail = q;
         } else {
-            while (last.mNext != null) {
-                last = last.mNext;
-            }
             last.mNext = q;
+            mPendingInputEventTail = q;
         }
         mPendingInputEventCount += 1;
         Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
@@ -4452,19 +4443,44 @@
     }
 
     void doProcessInputEvents() {
-        while (mCurrentInputEvent == null && mFirstPendingInputEvent != null) {
-            QueuedInputEvent q = mFirstPendingInputEvent;
-            mFirstPendingInputEvent = q.mNext;
+        // Handle all of the available pending input events. Currently this will immediately
+        // process all of the events it can until it encounters one that must go through the IME.
+        // After that it will continue adding events to the active input queue but will wait for a
+        // response from the IME, regardless of whether that particular event needs it or not, in
+        // order to guarantee ordering consistency. This could be slightly improved by only
+        // queueing events whose source has previously encountered something that needs to be
+        // handled by the IME, and otherwise handling them immediately since we only need to
+        // guarantee ordering within a given source.
+        while (mPendingInputEventHead != null) {
+            QueuedInputEvent q = mPendingInputEventHead;
+            mPendingInputEventHead = q.mNext;
+            if (mPendingInputEventHead == null) {
+                mPendingInputEventTail = null;
+            }
             q.mNext = null;
-            mCurrentInputEvent = q;
 
             mPendingInputEventCount -= 1;
             Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
                     mPendingInputEventCount);
 
-            final int result = deliverInputEvent(q);
-            if (result != EVENT_IN_PROGRESS) {
-                finishCurrentInputEvent(result == EVENT_HANDLED);
+            int result = deliverInputEvent(q);
+
+            if (result == EVENT_HANDLED || result == EVENT_NOT_HANDLED) {
+                finishInputEvent(q, result == EVENT_HANDLED);
+            } else if (result == EVENT_PENDING_IME) {
+                enqueueActiveInputEvent(q);
+            } else {
+                q.mFlags |= QueuedInputEvent.FLAG_DELIVER_POST_IME;
+                // If the IME decided not to handle this event, and we have no events already being
+                // handled by the IME, go ahead and handle this one and then continue to the next
+                // input event. Otherwise, queue it up and handle it after whatever in front of it
+                // in the queue has been handled.
+                if (mActiveInputEventHead == null) {
+                    result = deliverInputEventPostIme(q);
+                    finishInputEvent(q, result == EVENT_HANDLED);
+                } else {
+                    enqueueActiveInputEvent(q);
+                }
             }
         }
 
@@ -4476,43 +4492,113 @@
         }
     }
 
+    private void enqueueActiveInputEvent(QueuedInputEvent q) {
+        if (mActiveInputEventHead == null) {
+            mActiveInputEventHead = q;
+            mActiveInputEventTail = q;
+        } else {
+            mActiveInputEventTail.mNext = q;
+            mActiveInputEventTail = q;
+        }
+        mActiveInputEventCount += 1;
+        Trace.traceCounter(Trace.TRACE_TAG_INPUT, mActiveInputEventQueueLengthCounterName,
+                mActiveInputEventCount);
+    }
+
+    private QueuedInputEvent dequeueActiveInputEvent() {
+        return dequeueActiveInputEvent(mActiveInputEventHead);
+    }
+
+
+    private QueuedInputEvent dequeueActiveInputEvent(QueuedInputEvent q) {
+        QueuedInputEvent curr = mActiveInputEventHead;
+        QueuedInputEvent prev = null;
+        while (curr != null && curr != q) {
+            prev = curr;
+            curr = curr.mNext;
+        }
+        if (curr != null) {
+            if (mActiveInputEventHead == curr) {
+                mActiveInputEventHead = curr.mNext;
+            } else {
+                prev.mNext = curr.mNext;
+            }
+            if (mActiveInputEventTail == curr) {
+                mActiveInputEventTail = prev;
+            }
+            curr.mNext = null;
+
+            mActiveInputEventCount -= 1;
+            Trace.traceCounter(Trace.TRACE_TAG_INPUT, mActiveInputEventQueueLengthCounterName,
+                    mActiveInputEventCount);
+        }
+        return curr;
+    }
+
+    private QueuedInputEvent findActiveInputEvent(int seq) {
+        QueuedInputEvent q = mActiveInputEventHead;
+        while (q != null && q.mEvent.getSequenceNumber() != seq) {
+            q = q.mNext;
+        }
+        return q;
+    }
+
+    int dispatchImeInputEvent(QueuedInputEvent q) {
+        if (mLastWasImTarget) {
+            InputMethodManager imm = InputMethodManager.peekInstance();
+            if (imm != null) {
+                final InputEvent event = q.mEvent;
+                final int seq = event.getSequenceNumber();
+                if (DEBUG_IMF)
+                    Log.v(TAG, "Sending input event to IME: seq=" + seq + " event=" + event);
+                return imm.dispatchInputEvent(mView.getContext(), seq, event,
+                        mInputMethodCallback);
+            }
+        }
+        return EVENT_POST_IME;
+    }
+
     void handleImeFinishedEvent(int seq, boolean handled) {
-        final QueuedInputEvent q = mCurrentInputEvent;
-        if (q != null && q.mEvent.getSequenceNumber() == seq) {
+        QueuedInputEvent q = findActiveInputEvent(seq);
+        if (q != null) {
             if (DEBUG_IMF) {
                 Log.v(TAG, "IME finished event: seq=" + seq
                         + " handled=" + handled + " event=" + q);
             }
 
-            if (!handled) {
+            if (handled) {
+                dequeueActiveInputEvent(q);
+                finishInputEvent(q, true);
+            } else {
+                q.mFlags |= QueuedInputEvent.FLAG_DELIVER_POST_IME;
+            }
+
+
+            // Flush all of the input events that are no longer waiting on the IME
+            while (mActiveInputEventHead != null && (mActiveInputEventHead.mFlags &
+                        QueuedInputEvent.FLAG_DELIVER_POST_IME) != 0) {
+                q = dequeueActiveInputEvent();
                 // If the window doesn't currently have input focus, then drop
                 // this event.  This could be an event that came back from the
                 // IME dispatch but the window has lost focus in the meantime.
+                handled = false;
                 if (!mAttachInfo.mHasWindowFocus && !isTerminalInputEvent(q.mEvent)) {
                     Slog.w(TAG, "Dropping event due to no window focus: " + q.mEvent);
                 } else {
-                    final int result = deliverInputEventPostIme(q);
-                    if (result == EVENT_HANDLED) {
-                        handled = true;
-                    }
+                    handled = (deliverInputEventPostIme(q) == EVENT_HANDLED);
                 }
+                finishInputEvent(q, handled);
             }
-            finishCurrentInputEvent(handled);
-
-            // Immediately start processing the next input event.
-            doProcessInputEvents();
         } else {
             if (DEBUG_IMF) {
                 Log.v(TAG, "IME finished event: seq=" + seq
                         + " handled=" + handled + ", event not found!");
             }
         }
+
     }
 
-    private void finishCurrentInputEvent(boolean handled) {
-        final QueuedInputEvent q = mCurrentInputEvent;
-        mCurrentInputEvent = null;
-
+    private void finishInputEvent(QueuedInputEvent q, boolean handled) {
         if (q.mReceiver != null) {
             q.mReceiver.finishInputEvent(q.mEvent, handled);
         } else {
@@ -5070,7 +5156,7 @@
         public void finishedEvent(int seq, boolean handled) {
             final ViewRootImpl viewAncestor = mViewAncestor.get();
             if (viewAncestor != null) {
-                viewAncestor.dispatchImeFinishedEvent(seq, handled);
+                viewAncestor.handleImeFinishedEvent(seq, handled);
             }
         }
     }
diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java
index cfcf3c0..072c95f 100644
--- a/core/java/android/view/ViewTreeObserver.java
+++ b/core/java/android/view/ViewTreeObserver.java
@@ -33,6 +33,8 @@
  */
 public final class ViewTreeObserver {
     // Recursive listeners use CopyOnWriteArrayList
+    private CopyOnWriteArrayList<OnWindowFocusChangeListener> mOnWindowFocusListeners;
+    private CopyOnWriteArrayList<OnWindowAttachListener> mOnWindowAttachListeners;
     private CopyOnWriteArrayList<OnGlobalFocusChangeListener> mOnGlobalFocusListeners;
     private CopyOnWriteArrayList<OnTouchModeChangeListener> mOnTouchModeChangeListeners;
 
@@ -49,6 +51,36 @@
     private boolean mAlive = true;
 
     /**
+     * Interface definition for a callback to be invoked when the view hierarchy is
+     * attached to and detached from its window.
+     */
+    public interface OnWindowAttachListener {
+        /**
+         * Callback method to be invoked when the view hierarchy is attached to a window
+         */
+        public void onWindowAttached();
+
+        /**
+         * Callback method to be invoked when the view hierarchy is detached from a window
+         */
+        public void onWindowDetached();
+    }
+
+    /**
+     * Interface definition for a callback to be invoked when the view hierarchy's window
+     * focus state changes.
+     */
+    public interface OnWindowFocusChangeListener {
+        /**
+         * Callback method to be invoked when the window focus changes in the view tree.
+         *
+         * @param hasFocus Set to true if the window is gaining focus, false if it is
+         * losing focus.
+         */
+        public void onWindowFocusChanged(boolean hasFocus);
+    }
+
+    /**
      * Interface definition for a callback to be invoked when the focus state within
      * the view tree changes.
      */
@@ -272,6 +304,22 @@
      * @param observer The ViewTreeObserver whose listeners must be added to this observer
      */
     void merge(ViewTreeObserver observer) {
+        if (observer.mOnWindowAttachListeners != null) {
+            if (mOnWindowAttachListeners != null) {
+                mOnWindowAttachListeners.addAll(observer.mOnWindowAttachListeners);
+            } else {
+                mOnWindowAttachListeners = observer.mOnWindowAttachListeners;
+            }
+        }
+
+        if (observer.mOnWindowFocusListeners != null) {
+            if (mOnWindowFocusListeners != null) {
+                mOnWindowFocusListeners.addAll(observer.mOnWindowFocusListeners);
+            } else {
+                mOnWindowFocusListeners = observer.mOnWindowFocusListeners;
+            }
+        }
+
         if (observer.mOnGlobalFocusListeners != null) {
             if (mOnGlobalFocusListeners != null) {
                 mOnGlobalFocusListeners.addAll(observer.mOnGlobalFocusListeners);
@@ -324,6 +372,76 @@
     }
 
     /**
+     * Register a callback to be invoked when the view hierarchy is attached to a window.
+     *
+     * @param listener The callback to add
+     *
+     * @throws IllegalStateException If {@link #isAlive()} returns false
+     */
+    public void addOnWindowAttachListener(OnWindowAttachListener listener) {
+        checkIsAlive();
+
+        if (mOnWindowAttachListeners == null) {
+            mOnWindowAttachListeners
+                    = new CopyOnWriteArrayList<OnWindowAttachListener>();
+        }
+
+        mOnWindowAttachListeners.add(listener);
+    }
+
+    /**
+     * Remove a previously installed window attach callback.
+     *
+     * @param victim The callback to remove
+     *
+     * @throws IllegalStateException If {@link #isAlive()} returns false
+     *
+     * @see #addOnWindowAttachListener(android.view.ViewTreeObserver.OnWindowAttachListener)
+     */
+    public void removeOnWindowAttachListener(OnWindowAttachListener victim) {
+        checkIsAlive();
+        if (mOnWindowAttachListeners == null) {
+            return;
+        }
+        mOnWindowAttachListeners.remove(victim);
+    }
+
+    /**
+     * Register a callback to be invoked when the window focus state within the view tree changes.
+     *
+     * @param listener The callback to add
+     *
+     * @throws IllegalStateException If {@link #isAlive()} returns false
+     */
+    public void addOnWindowFocusChangeListener(OnWindowFocusChangeListener listener) {
+        checkIsAlive();
+
+        if (mOnWindowFocusListeners == null) {
+            mOnWindowFocusListeners
+                    = new CopyOnWriteArrayList<OnWindowFocusChangeListener>();
+        }
+
+        mOnWindowFocusListeners.add(listener);
+    }
+
+    /**
+     * Remove a previously installed window focus change callback.
+     *
+     * @param victim The callback to remove
+     *
+     * @throws IllegalStateException If {@link #isAlive()} returns false
+     *
+     * @see #addOnWindowFocusChangeListener(android.view.ViewTreeObserver.OnWindowFocusChangeListener)
+     */
+    public void removeOnWindowFocusChangeListener(OnWindowFocusChangeListener victim) {
+        checkIsAlive();
+        if (mOnWindowFocusListeners == null) {
+            return;
+        }
+        mOnWindowFocusListeners.remove(victim);
+    }
+
+    /**
      * Register a callback to be invoked when the focus state within the view tree changes.
      *
      * @param listener The callback to add
@@ -621,6 +739,41 @@
     }
 
     /**
+     * Notifies registered listeners that window has been attached/detached.
+     */
+    final void dispatchOnWindowAttachedChange(boolean attached) {
+        // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
+        // perform the dispatching. The iterator is a safe guard against listeners that
+        // could mutate the list by calling the various add/remove methods. This prevents
+        // the array from being modified while we iterate it.
+        final CopyOnWriteArrayList<OnWindowAttachListener> listeners
+                = mOnWindowAttachListeners;
+        if (listeners != null && listeners.size() > 0) {
+            for (OnWindowAttachListener listener : listeners) {
+                if (attached) listener.onWindowAttached();
+                else listener.onWindowDetached();
+            }
+        }
+    }
+
+    /**
+     * Notifies registered listeners that window focus has changed.
+     */
+    final void dispatchOnWindowFocusChange(boolean hasFocus) {
+        // NOTE: because of the use of CopyOnWriteArrayList, we *must* use an iterator to
+        // perform the dispatching. The iterator is a safe guard against listeners that
+        // could mutate the list by calling the various add/remove methods. This prevents
+        // the array from being modified while we iterate it.
+        final CopyOnWriteArrayList<OnWindowFocusChangeListener> listeners
+                = mOnWindowFocusListeners;
+        if (listeners != null && listeners.size() > 0) {
+            for (OnWindowFocusChangeListener listener : listeners) {
+                listener.onWindowFocusChanged(hasFocus);
+            }
+        }
+    }
+
+    /**
      * Notifies registered listeners that focus has changed.
      */
     final void dispatchOnGlobalFocusChange(View oldFocus, View newFocus) {
diff --git a/core/java/android/view/VolumePanel.java b/core/java/android/view/VolumePanel.java
index e711b94..9c00b7f 100644
--- a/core/java/android/view/VolumePanel.java
+++ b/core/java/android/view/VolumePanel.java
@@ -468,7 +468,8 @@
         // Force reloading the image resource
         sc.icon.setImageDrawable(null);
         sc.icon.setImageResource(muted ? sc.iconMuteRes : sc.iconRes);
-        if (sc.streamType == AudioManager.STREAM_RING &&
+        if (((sc.streamType == AudioManager.STREAM_RING) ||
+                (sc.streamType == AudioManager.STREAM_NOTIFICATION)) &&
                 mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
             sc.icon.setImageResource(R.drawable.ic_audio_ring_notif_vibrate);
         }
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index dba2354..7f9969c 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -19,7 +19,6 @@
 import com.android.internal.os.SomeArgs;
 import com.android.internal.view.IInputConnectionWrapper;
 import com.android.internal.view.IInputContext;
-import com.android.internal.view.IInputMethodCallback;
 import com.android.internal.view.IInputMethodClient;
 import com.android.internal.view.IInputMethodManager;
 import com.android.internal.view.IInputMethodSession;
@@ -40,8 +39,10 @@
 import android.util.Log;
 import android.util.PrintWriterPrinter;
 import android.util.Printer;
+import android.view.InputChannel;
+import android.view.InputEvent;
+import android.view.InputEventSender;
 import android.view.KeyEvent;
-import android.view.MotionEvent;
 import android.view.View;
 import android.view.ViewRootImpl;
 
@@ -319,6 +320,8 @@
      * The actual instance of the method to make calls on it.
      */
     IInputMethodSession mCurMethod;
+    InputChannel mCurChannel;
+    ImeInputEventSender mCurSender;
 
     PendingEvent mPendingEventPool;
     int mPendingEventPoolSize;
@@ -363,10 +366,18 @@
                         if (mBindSequence < 0 || mBindSequence != res.sequence) {
                             Log.w(TAG, "Ignoring onBind: cur seq=" + mBindSequence
                                     + ", given seq=" + res.sequence);
+                            if (res.channel != null) {
+                                res.channel.dispose();
+                            }
                             return;
                         }
                         
+                        flushPendingEventsLocked();
                         mCurMethod = res.method;
+                        if (mCurChannel != null) {
+                            mCurChannel.dispose();
+                        }
+                        mCurChannel = res.channel;
                         mCurId = res.id;
                         mBindSequence = res.sequence;
                     }
@@ -482,10 +493,10 @@
     }
     
     final IInputMethodClient.Stub mClient = new IInputMethodClient.Stub() {
-        @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+        @Override
+        protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
             // No need to check for dump permission, since we only give this
             // interface to the system.
-            
             CountDownLatch latch = new CountDownLatch(1);
             SomeArgs sargs = SomeArgs.obtain();
             sargs.arg1 = fd;
@@ -501,32 +512,29 @@
                 fout.println("Interrupted waiting for dump");
             }
         }
-        
+
+        @Override
         public void setUsingInputMethod(boolean state) {
         }
-        
+
+        @Override
         public void onBindMethod(InputBindResult res) {
             mH.sendMessage(mH.obtainMessage(MSG_BIND, res));
         }
-        
+
+        @Override
         public void onUnbindMethod(int sequence) {
             mH.sendMessage(mH.obtainMessage(MSG_UNBIND, sequence, 0));
         }
-        
+
+        @Override
         public void setActive(boolean active) {
             mH.sendMessage(mH.obtainMessage(MSG_SET_ACTIVE, active ? 1 : 0, 0));
         }
-    };    
-    
+    };
+
     final InputConnection mDummyInputConnection = new BaseInputConnection(this, false);
 
-    final IInputMethodCallback mInputMethodCallback = new IInputMethodCallback.Stub() {
-        @Override
-        public void finishedEvent(int seq, boolean handled) {
-            InputMethodManager.this.finishedEvent(seq, handled);
-        }
-    };
-    
     InputMethodManager(IInputMethodManager service, Looper looper) {
         mService = service;
         mMainLooper = looper;
@@ -711,9 +719,18 @@
      */
     void clearBindingLocked() {
         clearConnectionLocked();
+        flushPendingEventsLocked();
         mBindSequence = -1;
         mCurId = null;
         mCurMethod = null;
+        if (mCurSender != null) {
+            mCurSender.dispose();
+            mCurSender = null;
+        }
+        if (mCurChannel != null) {
+            mCurChannel.dispose();
+            mCurChannel = null;
+        }
     }
     
     /**
@@ -1085,6 +1102,7 @@
             // we need to reschedule our work for over there.
             if (DEBUG) Log.v(TAG, "Starting input: reschedule to view thread");
             vh.post(new Runnable() {
+                @Override
                 public void run() {
                     startInputInner(null, 0, 0, 0);
                 }
@@ -1158,11 +1176,20 @@
                     if (res.id != null) {
                         mBindSequence = res.sequence;
                         mCurMethod = res.method;
+                        if (mCurChannel != null) {
+                            mCurChannel.dispose();
+                        }
+                        mCurChannel = res.channel;
                         mCurId = res.id;
-                    } else if (mCurMethod == null) {
-                        // This means there is no input method available.
-                        if (DEBUG) Log.v(TAG, "ABORT input: no input method!");
-                        return true;
+                    } else {
+                        if (res.channel != null) {
+                            res.channel.dispose();
+                        }
+                        if (mCurMethod == null) {
+                            // This means there is no input method available.
+                            if (DEBUG) Log.v(TAG, "ABORT input: no input method!");
+                            return true;
+                        }
                     }
                 }
                 if (mCurMethod != null && mCompletions != null) {
@@ -1556,80 +1583,43 @@
             throw new RuntimeException(e);
         }
     }
-    
+
     /**
      * @hide
      */
-    public int dispatchKeyEvent(Context context, int seq, KeyEvent key,
+    public int dispatchInputEvent(Context context, int seq, InputEvent event,
             FinishedEventCallback callback) {
         synchronized (mH) {
-            if (DEBUG) Log.d(TAG, "dispatchKeyEvent");
+            if (DEBUG) Log.d(TAG, "dispatchInputEvent");
 
             if (mCurMethod != null) {
-                if (key.getAction() == KeyEvent.ACTION_DOWN
-                        && key.getKeyCode() == KeyEvent.KEYCODE_SYM
-                        && key.getRepeatCount() == 0) {
-                    showInputMethodPickerLocked();
-                    return ViewRootImpl.EVENT_HANDLED;
+                if (event instanceof KeyEvent) {
+                    KeyEvent keyEvent = (KeyEvent)event;
+                    if (keyEvent.getAction() == KeyEvent.ACTION_DOWN
+                            && keyEvent.getKeyCode() == KeyEvent.KEYCODE_SYM
+                            && keyEvent.getRepeatCount() == 0) {
+                        showInputMethodPickerLocked();
+                        return ViewRootImpl.EVENT_HANDLED;
+                    }
                 }
-                try {
-                    if (DEBUG) Log.v(TAG, "DISPATCH KEY: " + mCurMethod);
-                    final long startTime = SystemClock.uptimeMillis();
-                    enqueuePendingEventLocked(startTime, seq, mCurId, callback);
-                    mCurMethod.dispatchKeyEvent(seq, key, mInputMethodCallback);
-                    return ViewRootImpl.EVENT_IN_PROGRESS;
-                } catch (RemoteException e) {
-                    Log.w(TAG, "IME died: " + mCurId + " dropping: " + key, e);
+
+                if (DEBUG) Log.v(TAG, "DISPATCH INPUT EVENT: " + mCurMethod);
+                final long startTime = SystemClock.uptimeMillis();
+                if (mCurChannel != null) {
+                    if (mCurSender == null) {
+                        mCurSender = new ImeInputEventSender(mCurChannel, mH.getLooper());
+                    }
+                    if (mCurSender.sendInputEvent(seq, event)) {
+                        enqueuePendingEventLocked(startTime, seq, mCurId, callback);
+                        return ViewRootImpl.EVENT_PENDING_IME;
+                    } else {
+                        Log.w(TAG, "Unable to send input event to IME: "
+                                + mCurId + " dropping: " + event);
+                    }
                 }
             }
         }
-        return ViewRootImpl.EVENT_NOT_HANDLED;
-    }
-
-    /**
-     * @hide
-     */
-    public int dispatchTrackballEvent(Context context, int seq, MotionEvent motion,
-            FinishedEventCallback callback) {
-        synchronized (mH) {
-            if (DEBUG) Log.d(TAG, "dispatchTrackballEvent");
-
-            if (mCurMethod != null && mCurrentTextBoxAttribute != null) {
-                try {
-                    if (DEBUG) Log.v(TAG, "DISPATCH TRACKBALL: " + mCurMethod);
-                    final long startTime = SystemClock.uptimeMillis();
-                    enqueuePendingEventLocked(startTime, seq, mCurId, callback);
-                    mCurMethod.dispatchTrackballEvent(seq, motion, mInputMethodCallback);
-                    return ViewRootImpl.EVENT_IN_PROGRESS;
-                } catch (RemoteException e) {
-                    Log.w(TAG, "IME died: " + mCurId + " dropping trackball: " + motion, e);
-                }
-            }
-        }
-        return ViewRootImpl.EVENT_NOT_HANDLED;
-    }
-
-    /**
-     * @hide
-     */
-    public int dispatchGenericMotionEvent(Context context, int seq, MotionEvent motion,
-            FinishedEventCallback callback) {
-        synchronized (mH) {
-            if (DEBUG) Log.d(TAG, "dispatchGenericMotionEvent");
-
-            if (mCurMethod != null && mCurrentTextBoxAttribute != null) {
-                try {
-                    if (DEBUG) Log.v(TAG, "DISPATCH GENERIC MOTION: " + mCurMethod);
-                    final long startTime = SystemClock.uptimeMillis();
-                    enqueuePendingEventLocked(startTime, seq, mCurId, callback);
-                    mCurMethod.dispatchGenericMotionEvent(seq, motion, mInputMethodCallback);
-                    return ViewRootImpl.EVENT_IN_PROGRESS;
-                } catch (RemoteException e) {
-                    Log.w(TAG, "IME died: " + mCurId + " dropping generic motion: " + motion, e);
-                }
-            }
-        }
-        return ViewRootImpl.EVENT_NOT_HANDLED;
+        return ViewRootImpl.EVENT_POST_IME;
     }
 
     void finishedEvent(int seq, boolean handled) {
@@ -1724,6 +1714,30 @@
         }
     }
 
+    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);
+            msg.setAsynchronous(true);
+            mH.sendMessage(msg);
+            next = curr.mNext;
+            curr.mNext = prev;
+            prev = curr;
+            curr = next;
+        }
+    }
+
     public void showInputMethodPicker() {
         synchronized (mH) {
             showInputMethodPickerLocked();
@@ -1937,6 +1951,17 @@
         public void finishedEvent(int seq, boolean handled);
     }
 
+    private final class ImeInputEventSender extends InputEventSender {
+        public ImeInputEventSender(InputChannel inputChannel, Looper looper) {
+            super(inputChannel, looper);
+        }
+
+        @Override
+        public void onInputEventFinished(int seq, boolean handled) {
+            finishedEvent(seq, handled);
+        }
+    }
+
     private static final class PendingEvent {
         public PendingEvent mNext;
 
diff --git a/core/java/android/widget/AppSecurityPermissions.java b/core/java/android/widget/AppSecurityPermissions.java
index 06dadb0..b2073b1 100644
--- a/core/java/android/widget/AppSecurityPermissions.java
+++ b/core/java/android/widget/AppSecurityPermissions.java
@@ -66,20 +66,17 @@
 
     private final static String TAG = "AppSecurityPermissions";
     private final static boolean localLOGV = false;
-    private Context mContext;
-    private LayoutInflater mInflater;
-    private PackageManager mPm;
-    private PackageInfo mInstalledPackageInfo;
+    private final Context mContext;
+    private final LayoutInflater mInflater;
+    private final PackageManager mPm;
     private final Map<String, MyPermissionGroupInfo> mPermGroups
             = new HashMap<String, MyPermissionGroupInfo>();
     private final List<MyPermissionGroupInfo> mPermGroupsList
             = new ArrayList<MyPermissionGroupInfo>();
-    private final PermissionGroupInfoComparator mPermGroupComparator;
-    private final PermissionInfoComparator mPermComparator;
-    private List<MyPermissionInfo> mPermsList;
-    private CharSequence mNewPermPrefix;
-    private Drawable mNormalIcon;
-    private Drawable mDangerousIcon;
+    private final PermissionGroupInfoComparator mPermGroupComparator = new PermissionGroupInfoComparator();
+    private final PermissionInfoComparator mPermComparator = new PermissionInfoComparator();
+    private final List<MyPermissionInfo> mPermsList = new ArrayList<MyPermissionInfo>();
+    private final CharSequence mNewPermPrefix;
 
     static class MyPermissionGroupInfo extends PermissionGroupInfo {
         CharSequence mLabel;
@@ -113,7 +110,7 @@
         }
     }
 
-    static class MyPermissionInfo extends PermissionInfo {
+    private static class MyPermissionInfo extends PermissionInfo {
         CharSequence mLabel;
 
         /**
@@ -132,19 +129,9 @@
          */
         boolean mNew;
 
-        MyPermissionInfo() {
-        }
-
         MyPermissionInfo(PermissionInfo info) {
             super(info);
         }
-
-        MyPermissionInfo(MyPermissionInfo info) {
-            super(info);
-            mNewReqFlags = info.mNewReqFlags;
-            mExistingReqFlags = info.mExistingReqFlags;
-            mNew = info.mNew;
-        }
     }
 
     public static class PermissionItemView extends LinearLayout implements View.OnClickListener {
@@ -233,25 +220,16 @@
         }
     }
 
-    public AppSecurityPermissions(Context context, List<PermissionInfo> permList) {
+    private AppSecurityPermissions(Context context) {
         mContext = context;
+        mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
         mPm = mContext.getPackageManager();
-        loadResources();
-        mPermComparator = new PermissionInfoComparator();
-        mPermGroupComparator = new PermissionGroupInfoComparator();
-        for (PermissionInfo pi : permList) {
-            mPermsList.add(new MyPermissionInfo(pi));
-        }
-        setPermissions(mPermsList);
+        // Pick up from framework resources instead.
+        mNewPermPrefix = mContext.getText(R.string.perms_new_perm_prefix);
     }
-    
+
     public AppSecurityPermissions(Context context, String packageName) {
-        mContext = context;
-        mPm = mContext.getPackageManager();
-        loadResources();
-        mPermComparator = new PermissionInfoComparator();
-        mPermGroupComparator = new PermissionGroupInfoComparator();
-        mPermsList = new ArrayList<MyPermissionInfo>();
+        this(context);
         Set<MyPermissionInfo> permSet = new HashSet<MyPermissionInfo>();
         PackageInfo pkgInfo;
         try {
@@ -264,19 +242,12 @@
         if((pkgInfo.applicationInfo != null) && (pkgInfo.applicationInfo.uid != -1)) {
             getAllUsedPermissions(pkgInfo.applicationInfo.uid, permSet);
         }
-        for(MyPermissionInfo tmpInfo : permSet) {
-            mPermsList.add(tmpInfo);
-        }
+        mPermsList.addAll(permSet);
         setPermissions(mPermsList);
     }
 
     public AppSecurityPermissions(Context context, PackageInfo info) {
-        mContext = context;
-        mPm = mContext.getPackageManager();
-        loadResources();
-        mPermComparator = new PermissionInfoComparator();
-        mPermGroupComparator = new PermissionGroupInfoComparator();
-        mPermsList = new ArrayList<MyPermissionInfo>();
+        this(context);
         Set<MyPermissionInfo> permSet = new HashSet<MyPermissionInfo>();
         if(info == null) {
             return;
@@ -300,23 +271,14 @@
                 sharedUid = mPm.getUidForSharedUser(info.sharedUserId);
                 getAllUsedPermissions(sharedUid, permSet);
             } catch (NameNotFoundException e) {
-                Log.w(TAG, "Could'nt retrieve shared user id for:"+info.packageName);
+                Log.w(TAG, "Couldn't retrieve shared user id for: " + info.packageName);
             }
         }
         // Retrieve list of permissions
-        for (MyPermissionInfo tmpInfo : permSet) {
-            mPermsList.add(tmpInfo);
-        }
+        mPermsList.addAll(permSet);
         setPermissions(mPermsList);
     }
 
-    private void loadResources() {
-        // Pick up from framework resources instead.
-        mNewPermPrefix = mContext.getText(R.string.perms_new_perm_prefix);
-        mNormalIcon = mContext.getResources().getDrawable(R.drawable.ic_text_dot);
-        mDangerousIcon = mContext.getResources().getDrawable(R.drawable.ic_bullet_key_permission);
-    }
-
     /**
      * Utility to retrieve a view displaying a single permission.  This provides
      * the old UI layout for permissions; it is only here for the device admin
@@ -332,10 +294,6 @@
                 description, dangerous, icon);
     }
     
-    public PackageInfo getInstalledPackageInfo() {
-        return mInstalledPackageInfo;
-    }
-
     private void getAllUsedPermissions(int sharedUid, Set<MyPermissionInfo> permSet) {
         String sharedPkgList[] = mPm.getPackagesForUid(sharedUid);
         if(sharedPkgList == null || (sharedPkgList.length == 0)) {
@@ -346,17 +304,12 @@
         }
     }
     
-    private void getPermissionsForPackage(String packageName, 
-            Set<MyPermissionInfo> permSet) {
-        PackageInfo pkgInfo;
+    private void getPermissionsForPackage(String packageName, Set<MyPermissionInfo> permSet) {
         try {
-            pkgInfo = mPm.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS);
-        } catch (NameNotFoundException e) {
-            Log.w(TAG, "Couldn't retrieve permissions for package:"+packageName);
-            return;
-        }
-        if ((pkgInfo != null) && (pkgInfo.requestedPermissions != null)) {
+            PackageInfo pkgInfo = mPm.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS);
             extractPerms(pkgInfo, permSet, pkgInfo);
+        } catch (NameNotFoundException e) {
+            Log.w(TAG, "Couldn't retrieve permissions for package: " + packageName);
         }
     }
 
@@ -367,7 +320,6 @@
         if ((strList == null) || (strList.length == 0)) {
             return;
         }
-        mInstalledPackageInfo = installedPkgInfo;
         for (int i=0; i<strList.length; i++) {
             String permName = strList[i];
             // If we are only looking at an existing app, then we only
@@ -471,8 +423,6 @@
     }
 
     public View getPermissionsView(int which) {
-        mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
         LinearLayout permsView = (LinearLayout) mInflater.inflate(R.layout.app_perms_summary, null);
         LinearLayout displayList = (LinearLayout) permsView.findViewById(R.id.perms_list);
         View noPermsView = permsView.findViewById(R.id.no_permissions);
@@ -557,16 +507,27 @@
     private boolean isDisplayablePermission(PermissionInfo pInfo, int newReqFlags,
             int existingReqFlags) {
         final int base = pInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
-        // Dangerous and normal permissions are always shown to the user.
-        if (base == PermissionInfo.PROTECTION_DANGEROUS ||
-                base == PermissionInfo.PROTECTION_NORMAL) {
+        final boolean isNormal = (base == PermissionInfo.PROTECTION_NORMAL);
+        final boolean isDangerous = (base == PermissionInfo.PROTECTION_DANGEROUS);
+        final boolean isRequired =
+                ((newReqFlags&PackageInfo.REQUESTED_PERMISSION_REQUIRED) != 0);
+        final boolean isDevelopment =
+                ((pInfo.protectionLevel&PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0);
+        final boolean wasGranted =
+                ((existingReqFlags&PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0);
+        final boolean isGranted =
+                ((newReqFlags&PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0);
+
+        // Dangerous and normal permissions are always shown to the user if the permission
+        // is required, or it was previously granted
+        if ((isNormal || isDangerous) && (isRequired || wasGranted || isGranted)) {
             return true;
         }
+
         // Development permissions are only shown to the user if they are already
         // granted to the app -- if we are installing an app and they are not
         // already granted, they will not be granted as part of the install.
-        if ((existingReqFlags&PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0
-                && (pInfo.protectionLevel & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0) {
+        if (isDevelopment && wasGranted) {
             if (localLOGV) Log.i(TAG, "Special perm " + pInfo.name
                     + ": protlevel=0x" + Integer.toHexString(pInfo.protectionLevel));
             return true;
diff --git a/core/java/android/widget/ExpandableListView.java b/core/java/android/widget/ExpandableListView.java
index 0dd567b..7b81aa8 100644
--- a/core/java/android/widget/ExpandableListView.java
+++ b/core/java/android/widget/ExpandableListView.java
@@ -36,6 +36,8 @@
 
 import java.util.ArrayList;
 
+import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
+
 /**
  * A view that shows items in a vertically scrolling two-level list. This
  * differs from the {@link ListView} by allowing two levels: groups which can
@@ -76,6 +78,10 @@
  * @attr ref android.R.styleable#ExpandableListView_childIndicatorLeft
  * @attr ref android.R.styleable#ExpandableListView_childIndicatorRight
  * @attr ref android.R.styleable#ExpandableListView_childDivider
+ * @attr ref android.R.styleable#ExpandableListView_indicatorStart
+ * @attr ref android.R.styleable#ExpandableListView_indicatorEnd
+ * @attr ref android.R.styleable#ExpandableListView_childIndicatorStart
+ * @attr ref android.R.styleable#ExpandableListView_childIndicatorEnd
  */
 public class ExpandableListView extends ListView {
 
@@ -134,6 +140,12 @@
     /** Right bound for drawing the indicator. */
     private int mIndicatorRight;
 
+    /** Start bound for drawing the indicator. */
+    private int mIndicatorStart;
+
+    /** End bound for drawing the indicator. */
+    private int mIndicatorEnd;
+
     /**
      * Left bound for drawing the indicator of a child. Value of
      * {@link #CHILD_INDICATOR_INHERIT} means use mIndicatorLeft.
@@ -147,11 +159,28 @@
     private int mChildIndicatorRight;
 
     /**
+     * Start bound for drawing the indicator of a child. Value of
+     * {@link #CHILD_INDICATOR_INHERIT} means use mIndicatorStart.
+     */
+    private int mChildIndicatorStart;
+
+    /**
+     * End bound for drawing the indicator of a child. Value of
+     * {@link #CHILD_INDICATOR_INHERIT} means use mIndicatorEnd.
+     */
+    private int mChildIndicatorEnd;
+
+    /**
      * Denotes when a child indicator should inherit this bound from the generic
      * indicator bounds
      */
     public static final int CHILD_INDICATOR_INHERIT = -1;
-    
+
+    /**
+     * Denotes an undefined value for an indicator
+     */
+    private static final int INDICATOR_UNDEFINED = -2;
+
     /** The indicator drawn next to a group. */
     private Drawable mGroupIndicator;
 
@@ -202,30 +231,118 @@
         super(context, attrs, defStyle);
 
         TypedArray a =
-            context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.ExpandableListView, defStyle,
-                    0);
+            context.obtainStyledAttributes(attrs,
+                    com.android.internal.R.styleable.ExpandableListView, defStyle, 0);
 
-        mGroupIndicator = a
-                .getDrawable(com.android.internal.R.styleable.ExpandableListView_groupIndicator);
-        mChildIndicator = a
-                .getDrawable(com.android.internal.R.styleable.ExpandableListView_childIndicator);
-        mIndicatorLeft = a
-                .getDimensionPixelSize(com.android.internal.R.styleable.ExpandableListView_indicatorLeft, 0);
-        mIndicatorRight = a
-                .getDimensionPixelSize(com.android.internal.R.styleable.ExpandableListView_indicatorRight, 0);
+        mGroupIndicator = a.getDrawable(
+                com.android.internal.R.styleable.ExpandableListView_groupIndicator);
+        mChildIndicator = a.getDrawable(
+                com.android.internal.R.styleable.ExpandableListView_childIndicator);
+        mIndicatorLeft = a.getDimensionPixelSize(
+                com.android.internal.R.styleable.ExpandableListView_indicatorLeft, 0);
+        mIndicatorRight = a.getDimensionPixelSize(
+                com.android.internal.R.styleable.ExpandableListView_indicatorRight, 0);
         if (mIndicatorRight == 0 && mGroupIndicator != null) {
             mIndicatorRight = mIndicatorLeft + mGroupIndicator.getIntrinsicWidth();
         }
         mChildIndicatorLeft = a.getDimensionPixelSize(
-                com.android.internal.R.styleable.ExpandableListView_childIndicatorLeft, CHILD_INDICATOR_INHERIT);
+                com.android.internal.R.styleable.ExpandableListView_childIndicatorLeft,
+                CHILD_INDICATOR_INHERIT);
         mChildIndicatorRight = a.getDimensionPixelSize(
-                com.android.internal.R.styleable.ExpandableListView_childIndicatorRight, CHILD_INDICATOR_INHERIT);
-        mChildDivider = a.getDrawable(com.android.internal.R.styleable.ExpandableListView_childDivider);
-        
+                com.android.internal.R.styleable.ExpandableListView_childIndicatorRight,
+                CHILD_INDICATOR_INHERIT);
+        mChildDivider = a.getDrawable(
+                com.android.internal.R.styleable.ExpandableListView_childDivider);
+
+        if (!isRtlCompatibilityMode()) {
+            mIndicatorStart = a.getDimensionPixelSize(
+                    com.android.internal.R.styleable.ExpandableListView_indicatorStart,
+                    INDICATOR_UNDEFINED);
+            mIndicatorEnd = a.getDimensionPixelSize(
+                    com.android.internal.R.styleable.ExpandableListView_indicatorEnd,
+                    INDICATOR_UNDEFINED);
+
+            mChildIndicatorStart = a.getDimensionPixelSize(
+                    com.android.internal.R.styleable.ExpandableListView_childIndicatorStart,
+                    CHILD_INDICATOR_INHERIT);
+            mChildIndicatorEnd = a.getDimensionPixelSize(
+                    com.android.internal.R.styleable.ExpandableListView_childIndicatorEnd,
+                    CHILD_INDICATOR_INHERIT);
+        }
+
         a.recycle();
     }
-    
-    
+
+    /**
+     * Return true if we are in RTL compatibility mode (either before Jelly Bean MR1 or
+     * RTL not supported)
+     */
+    private boolean isRtlCompatibilityMode() {
+        final int targetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
+        return targetSdkVersion < JELLY_BEAN_MR1 || !hasRtlSupport();
+    }
+
+    /**
+     * Return true if the application tag in the AndroidManifest has set "supportRtl" to true
+     */
+    private boolean hasRtlSupport() {
+        return mContext.getApplicationInfo().hasRtlSupport();
+    }
+
+    public void onRtlPropertiesChanged(int layoutDirection) {
+        resolveIndicator();
+        resolveChildIndicator();
+    }
+
+    /**
+     * Resolve start/end indicator. start/end indicator always takes precedence over left/right
+     * indicator when defined.
+     */
+    private void resolveIndicator() {
+        final boolean isLayoutRtl = isLayoutRtl();
+        if (isLayoutRtl) {
+            if (mIndicatorStart >= 0) {
+                mIndicatorRight = mIndicatorStart;
+            }
+            if (mIndicatorEnd >= 0) {
+                mIndicatorLeft = mIndicatorEnd;
+            }
+        } else {
+            if (mIndicatorStart >= 0) {
+                mIndicatorLeft = mIndicatorStart;
+            }
+            if (mIndicatorEnd >= 0) {
+                mIndicatorRight = mIndicatorEnd;
+            }
+        }
+        if (mIndicatorRight == 0 && mGroupIndicator != null) {
+            mIndicatorRight = mIndicatorLeft + mGroupIndicator.getIntrinsicWidth();
+        }
+    }
+
+    /**
+     * Resolve start/end child indicator. start/end child indicator always takes precedence over
+     * left/right child indicator when defined.
+     */
+    private void resolveChildIndicator() {
+        final boolean isLayoutRtl = isLayoutRtl();
+        if (isLayoutRtl) {
+            if (mChildIndicatorStart >= CHILD_INDICATOR_INHERIT) {
+                mChildIndicatorRight = mChildIndicatorStart;
+            }
+            if (mChildIndicatorEnd >= CHILD_INDICATOR_INHERIT) {
+                mChildIndicatorLeft = mChildIndicatorEnd;
+            }
+        } else {
+            if (mChildIndicatorStart >= CHILD_INDICATOR_INHERIT) {
+                mChildIndicatorLeft = mChildIndicatorStart;
+            }
+            if (mChildIndicatorEnd >= CHILD_INDICATOR_INHERIT) {
+                mChildIndicatorRight = mChildIndicatorEnd;
+            }
+        }
+    }
+
     @Override
     protected void dispatchDraw(Canvas canvas) {
         // Draw children, etc.
@@ -1053,8 +1170,26 @@
     public void setChildIndicatorBounds(int left, int right) {
         mChildIndicatorLeft = left;
         mChildIndicatorRight = right;
+        resolveChildIndicator();
     }
-    
+
+    /**
+     * Sets the relative drawing bounds for the child indicator. For either, you can
+     * specify {@link #CHILD_INDICATOR_INHERIT} to use inherit from the general
+     * indicator's bounds.
+     *
+     * @see #setIndicatorBounds(int, int)
+     * @param start The start position (relative to the start bounds of this View)
+     *            to start drawing the indicator.
+     * @param end The end position (relative to the end bounds of this
+     *            View) to end the drawing of the indicator.
+     */
+    public void setChildIndicatorBoundsRelative(int start, int end) {
+        mChildIndicatorStart = start;
+        mChildIndicatorEnd = end;
+        resolveChildIndicator();
+    }
+
     /**
      * Sets the indicator to be drawn next to a group.
      * 
@@ -1084,8 +1219,26 @@
     public void setIndicatorBounds(int left, int right) {
         mIndicatorLeft = left;
         mIndicatorRight = right;
+        resolveIndicator();
     }
-    
+
+    /**
+     * Sets the relative drawing bounds for the indicators (at minimum, the group indicator
+     * is affected by this; the child indicator is affected by this if the
+     * child indicator bounds are set to inherit).
+     *
+     * @see #setChildIndicatorBounds(int, int)
+     * @param start The start position (relative to the start bounds of this View)
+     *            to start drawing the indicator.
+     * @param end The end position (relative to the end bounds of this
+     *            View) to end the drawing of the indicator.
+     */
+    public void setIndicatorBoundsRelative(int start, int end) {
+        mIndicatorStart = start;
+        mIndicatorEnd = end;
+        resolveIndicator();
+    }
+
     /**
      * Extra menu information specific to an {@link ExpandableListView} provided
      * to the
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index c9d2d95..529de2e 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -1212,8 +1212,8 @@
 
         private int mLeft, mTop, mRight, mBottom;
 
-        private int mStart = DEFAULT_RELATIVE;
-        private int mEnd = DEFAULT_RELATIVE;
+        private int mStart = DEFAULT_MARGIN_RELATIVE;
+        private int mEnd = DEFAULT_MARGIN_RELATIVE;
 
         private boolean mRulesChanged = false;
         private boolean mIsRtlCompatibilityMode = false;
@@ -1314,7 +1314,7 @@
                         break;
                 }
             }
-
+            mRulesChanged = true;
             System.arraycopy(rules, LEFT_OF, initialRules, LEFT_OF, VERB_COUNT);
 
             a.recycle();
@@ -1574,11 +1574,11 @@
         public void resolveLayoutDirection(int layoutDirection) {
             final boolean isLayoutRtl = isLayoutRtl();
             if (isLayoutRtl) {
-                if (mStart != DEFAULT_RELATIVE) mRight = mStart;
-                if (mEnd != DEFAULT_RELATIVE) mLeft = mEnd;
+                if (mStart != DEFAULT_MARGIN_RELATIVE) mRight = mStart;
+                if (mEnd != DEFAULT_MARGIN_RELATIVE) mLeft = mEnd;
             } else {
-                if (mStart != DEFAULT_RELATIVE) mLeft = mStart;
-                if (mEnd != DEFAULT_RELATIVE) mRight = mEnd;
+                if (mStart != DEFAULT_MARGIN_RELATIVE) mLeft = mStart;
+                if (mEnd != DEFAULT_MARGIN_RELATIVE) mRight = mEnd;
             }
 
             if (hasRelativeRules() && layoutDirection != getLayoutDirection()) {
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index eed3e67..eb2d1fe 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -27,6 +27,7 @@
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
+import android.os.SELinux;
 import android.util.Log;
 
 import com.android.org.bouncycastle.util.encoders.Base64;
@@ -64,6 +65,10 @@
 
     public LocalTransport(Context context) {
         mContext = context;
+        mDataDir.mkdirs();
+        if (!SELinux.restorecon(mDataDir)) {
+            Log.e(TAG, "SELinux restorecon failed for " + mDataDir);
+        }
     }
 
     public Intent configurationIntent() {
diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java
index d44df0c..9137d3c 100644
--- a/core/java/com/android/internal/util/ArrayUtils.java
+++ b/core/java/com/android/internal/util/ArrayUtils.java
@@ -79,6 +79,10 @@
      * @return true if they're equal, false otherwise
      */
     public static boolean equals(byte[] array1, byte[] array2, int length) {
+        if (length < 0) {
+            throw new IllegalArgumentException();
+        }
+
         if (array1 == array2) {
             return true;
         }
diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java
index 91b109e..b380403 100644
--- a/core/java/com/android/internal/util/Protocol.java
+++ b/core/java/com/android/internal/util/Protocol.java
@@ -52,5 +52,6 @@
     public static final int BASE_DATA_CONNECTION_TRACKER                            = 0x00042000;
     public static final int BASE_DNS_PINGER                                         = 0x00050000;
     public static final int BASE_NSD_MANAGER                                        = 0x00060000;
+    public static final int BASE_NETWORK_STATE_TRACKER                              = 0x00070000;
     //TODO: define all used protocols
 }
diff --git a/core/java/com/android/internal/util/XmlUtils.java b/core/java/com/android/internal/util/XmlUtils.java
index 93f6cf6..fa35308 100644
--- a/core/java/com/android/internal/util/XmlUtils.java
+++ b/core/java/com/android/internal/util/XmlUtils.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.util;
 
+import android.util.Xml;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -24,6 +25,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.net.ProtocolException;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -32,11 +34,8 @@
 import java.util.Map;
 import java.util.Set;
 
-import android.util.Xml;
-
 /** {@hide} */
-public class XmlUtils
-{
+public class XmlUtils {
 
     public static void skipCurrentTag(XmlPullParser parser)
             throws XmlPullParserException, IOException {
@@ -900,4 +899,42 @@
             }
         }
     }
+
+    public static int readIntAttribute(XmlPullParser in, String name) throws IOException {
+        final String value = in.getAttributeValue(null, name);
+        try {
+            return Integer.parseInt(value);
+        } catch (NumberFormatException e) {
+            throw new ProtocolException("problem parsing " + name + "=" + value + " as int");
+        }
+    }
+
+    public static void writeIntAttribute(XmlSerializer out, String name, int value)
+            throws IOException {
+        out.attribute(null, name, Integer.toString(value));
+    }
+
+    public static long readLongAttribute(XmlPullParser in, String name) throws IOException {
+        final String value = in.getAttributeValue(null, name);
+        try {
+            return Long.parseLong(value);
+        } catch (NumberFormatException e) {
+            throw new ProtocolException("problem parsing " + name + "=" + value + " as long");
+        }
+    }
+
+    public static void writeLongAttribute(XmlSerializer out, String name, long value)
+            throws IOException {
+        out.attribute(null, name, Long.toString(value));
+    }
+
+    public static boolean readBooleanAttribute(XmlPullParser in, String name) {
+        final String value = in.getAttributeValue(null, name);
+        return Boolean.parseBoolean(value);
+    }
+
+    public static void writeBooleanAttribute(XmlSerializer out, String name, boolean value)
+            throws IOException {
+        out.attribute(null, name, Boolean.toString(value));
+    }
 }
diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl
index bd947e9..77456da 100644
--- a/core/java/com/android/internal/view/IInputMethod.aidl
+++ b/core/java/com/android/internal/view/IInputMethod.aidl
@@ -18,6 +18,7 @@
 
 import android.os.IBinder;
 import android.os.ResultReceiver;
+import android.view.InputChannel;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputBinding;
 import android.view.inputmethod.InputMethodSubtype;
@@ -41,7 +42,7 @@
 
     void restartInput(in IInputContext inputContext, in EditorInfo attribute);
 
-    void createSession(IInputSessionCallback  callback);
+    void createSession(in InputChannel channel, IInputSessionCallback callback);
 
     void setSessionEnabled(IInputMethodSession session, boolean enabled);
 
diff --git a/core/java/com/android/internal/view/IInputMethodSession.aidl b/core/java/com/android/internal/view/IInputMethodSession.aidl
index cdec254..90210ce 100644
--- a/core/java/com/android/internal/view/IInputMethodSession.aidl
+++ b/core/java/com/android/internal/view/IInputMethodSession.aidl
@@ -22,7 +22,6 @@
 import android.view.MotionEvent;
 import android.view.inputmethod.CompletionInfo;
 import android.view.inputmethod.ExtractedText;
-import com.android.internal.view.IInputMethodCallback;
 
 /**
  * Sub-interface of IInputMethod which is safe to give to client applications.
@@ -40,14 +39,8 @@
     void viewClicked(boolean focusChanged);
 
     void updateCursor(in Rect newCursor);
-    
+
     void displayCompletions(in CompletionInfo[] completions);
-    
-    void dispatchKeyEvent(int seq, in KeyEvent event, IInputMethodCallback callback);
-
-    void dispatchTrackballEvent(int seq, in MotionEvent event, IInputMethodCallback callback);
-
-    void dispatchGenericMotionEvent(int seq, in MotionEvent event, IInputMethodCallback callback);
 
     void appPrivateCommand(String action, in Bundle data);
 
diff --git a/core/java/com/android/internal/view/InputBindResult.java b/core/java/com/android/internal/view/InputBindResult.java
index 658f098..9143c61 100644
--- a/core/java/com/android/internal/view/InputBindResult.java
+++ b/core/java/com/android/internal/view/InputBindResult.java
@@ -18,6 +18,7 @@
 
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.view.InputChannel;
 
 /**
  * Bundle of information returned by input method manager about a successful
@@ -30,7 +31,12 @@
      * The input method service.
      */
     public final IInputMethodSession method;
-    
+
+    /**
+     * The input channel used to send input events to this IME.
+     */
+    public final InputChannel channel;
+
     /**
      * The ID for this input method, as found in InputMethodInfo; null if
      * no input method will be bound.
@@ -42,18 +48,25 @@
      */
     public final int sequence;
     
-    public InputBindResult(IInputMethodSession _method, String _id, int _sequence) {
+    public InputBindResult(IInputMethodSession _method, InputChannel _channel,
+            String _id, int _sequence) {
         method = _method;
+        channel = _channel;
         id = _id;
         sequence = _sequence;
     }
     
     InputBindResult(Parcel source) {
         method = IInputMethodSession.Stub.asInterface(source.readStrongBinder());
+        if (source.readInt() != 0) {
+            channel = InputChannel.CREATOR.createFromParcel(source);
+        } else {
+            channel = null;
+        }
         id = source.readString();
         sequence = source.readInt();
     }
-    
+
     @Override
     public String toString() {
         return "InputBindResult{" + method + " " + id
@@ -62,12 +75,19 @@
 
     /**
      * Used to package this object into a {@link Parcel}.
-     * 
+     *
      * @param dest The {@link Parcel} to be written.
      * @param flags The flags used for parceling.
      */
+    @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeStrongInterface(method);
+        if (channel != null) {
+            dest.writeInt(1);
+            channel.writeToParcel(dest, 0);
+        } else {
+            dest.writeInt(0);
+        }
         dest.writeString(id);
         dest.writeInt(sequence);
     }
@@ -75,17 +95,21 @@
     /**
      * Used to make this class parcelable.
      */
-    public static final Parcelable.Creator<InputBindResult> CREATOR = new Parcelable.Creator<InputBindResult>() {
+    public static final Parcelable.Creator<InputBindResult> CREATOR =
+            new Parcelable.Creator<InputBindResult>() {
+        @Override
         public InputBindResult createFromParcel(Parcel source) {
             return new InputBindResult(source);
         }
 
+        @Override
         public InputBindResult[] newArray(int size) {
             return new InputBindResult[size];
         }
     };
 
+    @Override
     public int describeContents() {
-        return 0;
+        return channel != null ? channel.describeContents() : 0;
     }
 }
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index 6bb7ac7..d1db230 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -835,6 +835,8 @@
                 (TextUtils.isEmpty(mTitle) && TextUtils.isEmpty(mSubtitle))) {
             // Don't show while in expanded mode or with empty text
             mTitleLayout.setVisibility(GONE);
+        } else {
+            mTitleLayout.setVisibility(VISIBLE);
         }
     }
 
@@ -1587,15 +1589,10 @@
                     mTitleLayout.setVisibility(VISIBLE);
                 }
             }
-            if (mTabScrollView != null && mNavigationMode == ActionBar.NAVIGATION_MODE_TABS) {
-                mTabScrollView.setVisibility(VISIBLE);
-            }
-            if (mSpinner != null && mNavigationMode == ActionBar.NAVIGATION_MODE_LIST) {
-                mSpinner.setVisibility(VISIBLE);
-            }
-            if (mCustomNavView != null && (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) {
-                mCustomNavView.setVisibility(VISIBLE);
-            }
+            if (mTabScrollView != null) mTabScrollView.setVisibility(VISIBLE);
+            if (mSpinner != null) mSpinner.setVisibility(VISIBLE);
+            if (mCustomNavView != null) mCustomNavView.setVisibility(VISIBLE);
+
             mExpandedHomeLayout.setIcon(null);
             mCurrentExpandedItem = null;
             setHomeButtonEnabled(mWasHomeEnabled); // Set by expandItemActionView above
diff --git a/core/java/com/android/internal/widget/TransportControlView.java b/core/java/com/android/internal/widget/TransportControlView.java
index c33f038..8ebe94c 100644
--- a/core/java/com/android/internal/widget/TransportControlView.java
+++ b/core/java/com/android/internal/widget/TransportControlView.java
@@ -143,7 +143,8 @@
             mLocalHandler = new WeakReference<Handler>(handler);
         }
 
-        public void setPlaybackState(int generationId, int state, long stateChangeTimeMs) {
+        public void setPlaybackState(int generationId, int state, long stateChangeTimeMs,
+                long currentPosMs, float speed) {
             Handler handler = mLocalHandler.get();
             if (handler != null) {
                 handler.obtainMessage(MSG_UPDATE_STATE, generationId, state).sendToTarget();
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 1e27be8..66cea9d7 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -44,6 +44,7 @@
 	android_view_InputChannel.cpp \
 	android_view_InputDevice.cpp \
 	android_view_InputEventReceiver.cpp \
+	android_view_InputEventSender.cpp \
 	android_view_KeyEvent.cpp \
 	android_view_KeyCharacterMap.cpp \
 	android_view_HardwareRenderer.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 86d3cb6..1300d01 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -163,6 +163,7 @@
 extern int register_android_view_InputChannel(JNIEnv* env);
 extern int register_android_view_InputDevice(JNIEnv* env);
 extern int register_android_view_InputEventReceiver(JNIEnv* env);
+extern int register_android_view_InputEventSender(JNIEnv* env);
 extern int register_android_view_KeyCharacterMap(JNIEnv *env);
 extern int register_android_view_KeyEvent(JNIEnv* env);
 extern int register_android_view_MotionEvent(JNIEnv* env);
@@ -1195,6 +1196,7 @@
     REG_JNI(register_android_app_NativeActivity),
     REG_JNI(register_android_view_InputChannel),
     REG_JNI(register_android_view_InputEventReceiver),
+    REG_JNI(register_android_view_InputEventSender),
     REG_JNI(register_android_view_KeyEvent),
     REG_JNI(register_android_view_MotionEvent),
     REG_JNI(register_android_view_PointerIcon),
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 90161fd..5454c08 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -383,7 +383,8 @@
         return descent - ascent + leading;
     }
 
-    static jfloat measureText_CII(JNIEnv* env, jobject jpaint, jcharArray text, int index, int count) {
+    static jfloat measureText_CIII(JNIEnv* env, jobject jpaint, jcharArray text, int index, int count,
+            jint bidiFlags) {
         NPE_CHECK_RETURN_ZERO(env, jpaint);
         NPE_CHECK_RETURN_ZERO(env, text);
 
@@ -401,13 +402,14 @@
         jfloat result = 0;
 
         TextLayout::getTextRunAdvances(paint, textArray, index, count, textLength,
-                paint->getFlags(), NULL /* dont need all advances */, &result);
+                bidiFlags, NULL /* dont need all advances */, &result);
 
         env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray), JNI_ABORT);
         return result;
     }
 
-    static jfloat measureText_StringII(JNIEnv* env, jobject jpaint, jstring text, int start, int end) {
+    static jfloat measureText_StringIII(JNIEnv* env, jobject jpaint, jstring text, int start, int end,
+            jint bidiFlags) {
         NPE_CHECK_RETURN_ZERO(env, jpaint);
         NPE_CHECK_RETURN_ZERO(env, text);
 
@@ -426,13 +428,13 @@
         jfloat width = 0;
 
         TextLayout::getTextRunAdvances(paint, textArray, start, count, textLength,
-                paint->getFlags(), NULL /* dont need all advances */, &width);
+                bidiFlags, NULL /* dont need all advances */, &width);
 
         env->ReleaseStringChars(text, textArray);
         return width;
     }
 
-    static jfloat measureText_String(JNIEnv* env, jobject jpaint, jstring text) {
+    static jfloat measureText_StringI(JNIEnv* env, jobject jpaint, jstring text, jint bidiFlags) {
         NPE_CHECK_RETURN_ZERO(env, jpaint);
         NPE_CHECK_RETURN_ZERO(env, text);
 
@@ -446,13 +448,14 @@
         jfloat width = 0;
 
         TextLayout::getTextRunAdvances(paint, textArray, 0, textLength, textLength,
-                paint->getFlags(), NULL /* dont need all advances */, &width);
+                bidiFlags, NULL /* dont need all advances */, &width);
 
         env->ReleaseStringChars(text, textArray);
         return width;
     }
 
-    static int dotextwidths(JNIEnv* env, SkPaint* paint, const jchar text[], int count, jfloatArray widths) {
+    static int dotextwidths(JNIEnv* env, SkPaint* paint, const jchar text[], int count, jfloatArray widths,
+            jint bidiFlags) {
         NPE_CHECK_RETURN_ZERO(env, paint);
         NPE_CHECK_RETURN_ZERO(env, text);
 
@@ -473,23 +476,24 @@
         jfloat* widthsArray = autoWidths.ptr();
 
         TextLayout::getTextRunAdvances(paint, text, 0, count, count,
-                paint->getFlags(), widthsArray, NULL /* dont need totalAdvance */);
+                bidiFlags, widthsArray, NULL /* dont need totalAdvance */);
 
         return count;
     }
 
-    static int getTextWidths___CII_F(JNIEnv* env, jobject clazz, SkPaint* paint, jcharArray text, int index, int count, jfloatArray widths) {
+    static int getTextWidths___CIII_F(JNIEnv* env, jobject clazz, SkPaint* paint, jcharArray text,
+            int index, int count, jint bidiFlags, jfloatArray widths) {
         const jchar* textArray = env->GetCharArrayElements(text, NULL);
-        count = dotextwidths(env, paint, textArray + index, count, widths);
+        count = dotextwidths(env, paint, textArray + index, count, widths, bidiFlags);
         env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray),
                                       JNI_ABORT);
         return count;
     }
 
-    static int getTextWidths__StringII_F(JNIEnv* env, jobject clazz, SkPaint* paint, jstring text,
-            int start, int end, jfloatArray widths) {
+    static int getTextWidths__StringIII_F(JNIEnv* env, jobject clazz, SkPaint* paint, jstring text,
+            int start, int end, jint bidiFlags, jfloatArray widths) {
         const jchar* textArray = env->GetStringChars(text, NULL);
-        int count = dotextwidths(env, paint, textArray + start, end - start, widths);
+        int count = dotextwidths(env, paint, textArray + start, end - start, widths, bidiFlags);
         env->ReleaseStringChars(text, textArray);
         return count;
     }
@@ -685,10 +689,10 @@
     }
 
     static int breakText(JNIEnv* env, SkPaint& paint, const jchar text[],
-                         int count, float maxWidth, jfloatArray jmeasured,
+                         int count, float maxWidth, jint bidiFlags, jfloatArray jmeasured,
                          SkPaint::TextBufferDirection tbd) {
         sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(&paint,
-                text, 0, count, count, paint.getFlags());
+                text, 0, count, count, bidiFlags);
         if (value == NULL) {
             return 0;
         }
@@ -706,7 +710,7 @@
     }
 
     static int breakTextC(JNIEnv* env, jobject jpaint, jcharArray jtext,
-            int index, int count, float maxWidth, jfloatArray jmeasuredWidth) {
+            int index, int count, float maxWidth, jint bidiFlags, jfloatArray jmeasuredWidth) {
         NPE_CHECK_RETURN_ZERO(env, jpaint);
         NPE_CHECK_RETURN_ZERO(env, jtext);
 
@@ -727,14 +731,14 @@
         SkPaint*     paint = GraphicsJNI::getNativePaint(env, jpaint);
         const jchar* text = env->GetCharArrayElements(jtext, NULL);
         count = breakText(env, *paint, text + index, count, maxWidth,
-                          jmeasuredWidth, tbd);
+                          bidiFlags, jmeasuredWidth, tbd);
         env->ReleaseCharArrayElements(jtext, const_cast<jchar*>(text),
                                       JNI_ABORT);
         return count;
     }
 
     static int breakTextS(JNIEnv* env, jobject jpaint, jstring jtext,
-                bool forwards, float maxWidth, jfloatArray jmeasuredWidth) {
+                bool forwards, float maxWidth, jint bidiFlags, jfloatArray jmeasuredWidth) {
         NPE_CHECK_RETURN_ZERO(env, jpaint);
         NPE_CHECK_RETURN_ZERO(env, jtext);
 
@@ -745,22 +749,20 @@
         SkPaint* paint = GraphicsJNI::getNativePaint(env, jpaint);
         int count = env->GetStringLength(jtext);
         const jchar* text = env->GetStringChars(jtext, NULL);
-        count = breakText(env, *paint, text, count, maxWidth,
-                          jmeasuredWidth, tbd);
+        count = breakText(env, *paint, text, count, maxWidth, bidiFlags, jmeasuredWidth, tbd);
         env->ReleaseStringChars(jtext, text);
         return count;
     }
 
     static void doTextBounds(JNIEnv* env, const jchar* text, int count,
-                             jobject bounds, const SkPaint& paint)
-    {
+                             jobject bounds, const SkPaint& paint, jint bidiFlags) {
         SkRect  r;
         r.set(0,0,0,0);
 
         SkIRect ir;
 
         sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(&paint,
-                text, 0, count, count, paint.getFlags());
+                text, 0, count, count, bidiFlags);
         if (value == NULL) {
             return;
         }
@@ -770,18 +772,16 @@
     }
 
     static void getStringBounds(JNIEnv* env, jobject, const SkPaint* paint,
-                                jstring text, int start, int end, jobject bounds)
-    {
+                                jstring text, int start, int end, jint bidiFlags, jobject bounds) {
         const jchar* textArray = env->GetStringChars(text, NULL);
-        doTextBounds(env, textArray + start, end - start, bounds, *paint);
+        doTextBounds(env, textArray + start, end - start, bounds, *paint, bidiFlags);
         env->ReleaseStringChars(text, textArray);
     }
 
     static void getCharArrayBounds(JNIEnv* env, jobject, const SkPaint* paint,
-                        jcharArray text, int index, int count, jobject bounds)
-    {
+                        jcharArray text, int index, int count, jint bidiFlags, jobject bounds) {
         const jchar* textArray = env->GetCharArrayElements(text, NULL);
-        doTextBounds(env, textArray + index, count, bounds, *paint);
+        doTextBounds(env, textArray + index, count, bounds, *paint, bidiFlags);
         env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray),
                                       JNI_ABORT);
     }
@@ -841,13 +841,13 @@
     {"descent","()F", (void*) SkPaintGlue::descent},
     {"getFontMetrics", "(Landroid/graphics/Paint$FontMetrics;)F", (void*)SkPaintGlue::getFontMetrics},
     {"getFontMetricsInt", "(Landroid/graphics/Paint$FontMetricsInt;)I", (void*)SkPaintGlue::getFontMetricsInt},
-    {"native_measureText","([CII)F", (void*) SkPaintGlue::measureText_CII},
-    {"native_measureText","(Ljava/lang/String;)F", (void*) SkPaintGlue::measureText_String},
-    {"native_measureText","(Ljava/lang/String;II)F", (void*) SkPaintGlue::measureText_StringII},
-    {"native_breakText","([CIIF[F)I", (void*) SkPaintGlue::breakTextC},
-    {"native_breakText","(Ljava/lang/String;ZF[F)I", (void*) SkPaintGlue::breakTextS},
-    {"native_getTextWidths","(I[CII[F)I", (void*) SkPaintGlue::getTextWidths___CII_F},
-    {"native_getTextWidths","(ILjava/lang/String;II[F)I", (void*) SkPaintGlue::getTextWidths__StringII_F},
+    {"native_measureText","([CIII)F", (void*) SkPaintGlue::measureText_CIII},
+    {"native_measureText","(Ljava/lang/String;I)F", (void*) SkPaintGlue::measureText_StringI},
+    {"native_measureText","(Ljava/lang/String;III)F", (void*) SkPaintGlue::measureText_StringIII},
+    {"native_breakText","([CIIFI[F)I", (void*) SkPaintGlue::breakTextC},
+    {"native_breakText","(Ljava/lang/String;ZFI[F)I", (void*) SkPaintGlue::breakTextS},
+    {"native_getTextWidths","(I[CIII[F)I", (void*) SkPaintGlue::getTextWidths___CIII_F},
+    {"native_getTextWidths","(ILjava/lang/String;III[F)I", (void*) SkPaintGlue::getTextWidths__StringIII_F},
     {"native_getTextRunAdvances","(I[CIIIII[FI)F",
         (void*) SkPaintGlue::getTextRunAdvances___CIIIII_FI},
     {"native_getTextRunAdvances","(ILjava/lang/String;IIIII[FI)F",
@@ -861,9 +861,9 @@
         (void*) SkPaintGlue::getTextRunCursor__String},
     {"native_getTextPath","(II[CIIFFI)V", (void*) SkPaintGlue::getTextPath___C},
     {"native_getTextPath","(IILjava/lang/String;IIFFI)V", (void*) SkPaintGlue::getTextPath__String},
-    {"nativeGetStringBounds", "(ILjava/lang/String;IILandroid/graphics/Rect;)V",
+    {"nativeGetStringBounds", "(ILjava/lang/String;IIILandroid/graphics/Rect;)V",
                                         (void*) SkPaintGlue::getStringBounds },
-    {"nativeGetCharArrayBounds", "(I[CIILandroid/graphics/Rect;)V",
+    {"nativeGetCharArrayBounds", "(I[CIIILandroid/graphics/Rect;)V",
                                     (void*) SkPaintGlue::getCharArrayBounds },
     {"nSetShadowLayer", "(FFFI)V", (void*)SkPaintGlue::setShadowLayer}
 };
diff --git a/core/jni/android_ddm_DdmHandleNativeHeap.cpp b/core/jni/android_ddm_DdmHandleNativeHeap.cpp
index 42d408d..f5eaf94 100644
--- a/core/jni/android_ddm_DdmHandleNativeHeap.cpp
+++ b/core/jni/android_ddm_DdmHandleNativeHeap.cpp
@@ -2,16 +2,16 @@
 **
 ** Copyright 2006, 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 
+** 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 
+**     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 
+** 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.
 */
 
@@ -23,20 +23,17 @@
 #include <android_runtime/AndroidRuntime.h>
 
 #include <utils/Log.h>
+#include <utils/String8.h>
 
 #include <fcntl.h>
 #include <errno.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 
-#if defined(__arm__)
-extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overallSize, 
-        size_t* infoSize, size_t* totalMemory, size_t* backtraceSize);
-        
-extern "C" void free_malloc_leak_info(uint8_t* info);
-#endif
+extern "C" void get_malloc_leak_info(uint8_t** info, size_t* overallSize,
+                                     size_t* infoSize, size_t* totalMemory, size_t* backtraceSize);
 
-#define MAPS_FILE_SIZE 65 * 1024
+extern "C" void free_malloc_leak_info(uint8_t* info);
 
 struct Header {
     size_t mapSize;
@@ -48,96 +45,57 @@
 
 namespace android {
 
+static void ReadFile(const char* path, String8& s) {
+    int fd = open(path, O_RDONLY);
+    if (fd != -1) {
+        char bytes[1024];
+        ssize_t byteCount;
+        while ((byteCount = TEMP_FAILURE_RETRY(read(fd, bytes, sizeof(bytes)))) > 0) {
+            s.append(bytes, byteCount);
+        }
+        close(fd);
+    }
+}
+
 /*
- * Retrieve the native heap information and the info from /proc/<self>/maps,
+ * Retrieve the native heap information and the info from /proc/self/maps,
  * copy them into a byte[] with a "struct Header" that holds data offsets,
  * and return the array.
  */
-static jbyteArray getLeakInfo(JNIEnv *env, jobject clazz)
-{
-#if defined(__arm__)
-    // get the info in /proc/[pid]/map
+static jbyteArray DdmHandleNativeHeap_getLeakInfo(JNIEnv* env, jobject) {
     Header header;
     memset(&header, 0, sizeof(header));
 
-    pid_t pid = getpid();
-
-    char path[FILENAME_MAX];
-    sprintf(path, "/proc/%d/maps", pid);
-
-    struct stat sb;
-    int ret = stat(path, &sb);
-
-    uint8_t* mapsFile = NULL;
-    if (ret == 0) {
-        mapsFile = (uint8_t*)malloc(MAPS_FILE_SIZE);
-        int fd = open(path, O_RDONLY);
-    
-        if (mapsFile != NULL && fd != -1) {
-            int amount = 0;
-            do {
-                uint8_t* ptr = mapsFile + header.mapSize;
-                amount = read(fd, ptr, MAPS_FILE_SIZE);
-                if (amount <= 0) {
-                    if (errno != EINTR)
-                        break; 
-                    else
-                        continue;
-                }
-                header.mapSize += amount;
-            } while (header.mapSize < MAPS_FILE_SIZE);
-            
-            ALOGD("**** read %d bytes from '%s'", (int) header.mapSize, path);
-        }
-    }
+    String8 maps;
+    ReadFile("/proc/self/maps", maps);
+    header.mapSize = maps.size();
 
     uint8_t* allocBytes;
-    get_malloc_leak_info(&allocBytes, &header.allocSize, &header.allocInfoSize, 
-            &header.totalMemory, &header.backtraceSize);
+    get_malloc_leak_info(&allocBytes, &header.allocSize, &header.allocInfoSize,
+                         &header.totalMemory, &header.backtraceSize);
 
-    jbyte* bytes = NULL;
-    jbyte* ptr = NULL;
+    ALOGD("*** mapSize: %d allocSize: %d allocInfoSize: %d totalMemory: %d",
+          header.mapSize, header.allocSize, header.allocInfoSize, header.totalMemory);
+
     jbyteArray array = env->NewByteArray(sizeof(Header) + header.mapSize + header.allocSize);
-    if (array == NULL) {
-        goto done;
+    if (array != NULL) {
+        env->SetByteArrayRegion(array, 0,
+                                sizeof(header), reinterpret_cast<jbyte*>(&header));
+        env->SetByteArrayRegion(array, sizeof(header),
+                                maps.size(), reinterpret_cast<const jbyte*>(maps.string()));
+        env->SetByteArrayRegion(array, sizeof(header) + maps.size(),
+                                header.allocSize, reinterpret_cast<jbyte*>(allocBytes));
     }
 
-    bytes = env->GetByteArrayElements(array, NULL);
-    ptr = bytes;
-
-//    ALOGD("*** mapSize: %d allocSize: %d allocInfoSize: %d totalMemory: %d", 
-//            header.mapSize, header.allocSize, header.allocInfoSize, header.totalMemory);
-
-    memcpy(ptr, &header, sizeof(header));
-    ptr += sizeof(header);
-    
-    if (header.mapSize > 0 && mapsFile != NULL) {
-        memcpy(ptr, mapsFile, header.mapSize);
-        ptr += header.mapSize;
-    }
-    
-    memcpy(ptr, allocBytes, header.allocSize);
-    env->ReleaseByteArrayElements(array, bytes, 0);
-
-done:
-    if (mapsFile != NULL) {
-        free(mapsFile);
-    }
-    // free the info up!
     free_malloc_leak_info(allocBytes);
-
     return array;
-#else
-    return NULL;
-#endif
 }
 
 static JNINativeMethod method_table[] = {
-    { "getLeakInfo", "()[B", (void*)getLeakInfo },
+    { "getLeakInfo", "()[B", (void*) DdmHandleNativeHeap_getLeakInfo },
 };
 
-int register_android_ddm_DdmHandleNativeHeap(JNIEnv *env)
-{
+int register_android_ddm_DdmHandleNativeHeap(JNIEnv* env) {
     return AndroidRuntime::registerNativeMethods(env, "android/ddm/DdmHandleNativeHeap", method_table, NELEM(method_table));
 }
 
diff --git a/core/jni/android_hardware_UsbDeviceConnection.cpp b/core/jni/android_hardware_UsbDeviceConnection.cpp
index 923781e..cea5bbf 100644
--- a/core/jni/android_hardware_UsbDeviceConnection.cpp
+++ b/core/jni/android_hardware_UsbDeviceConnection.cpp
@@ -142,7 +142,7 @@
 static jint
 android_hardware_UsbDeviceConnection_control_request(JNIEnv *env, jobject thiz,
         jint requestType, jint request, jint value, jint index,
-        jbyteArray buffer, jint length, jint timeout)
+        jbyteArray buffer, jint start, jint length, jint timeout)
 {
     struct usb_device* device = get_device_from_object(env, thiz);
     if (!device) {
@@ -152,25 +152,22 @@
 
     jbyte* bufferBytes = NULL;
     if (buffer) {
-        if (env->GetArrayLength(buffer) < length) {
-            jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", NULL);
-            return -1;
-        }
-        bufferBytes = env->GetByteArrayElements(buffer, 0);
+        bufferBytes = (jbyte*)env->GetPrimitiveArrayCritical(buffer, NULL);
     }
 
     jint result = usb_device_control_transfer(device, requestType, request,
-            value, index, bufferBytes, length, timeout);
+            value, index, bufferBytes + start, length, timeout);
 
-    if (bufferBytes)
-        env->ReleaseByteArrayElements(buffer, bufferBytes, 0);
+    if (bufferBytes) {
+        env->ReleasePrimitiveArrayCritical(buffer, bufferBytes, 0);
+    }
 
     return result;
 }
 
 static jint
 android_hardware_UsbDeviceConnection_bulk_request(JNIEnv *env, jobject thiz,
-        jint endpoint, jbyteArray buffer, jint length, jint timeout)
+        jint endpoint, jbyteArray buffer, jint start, jint length, jint timeout)
 {
     struct usb_device* device = get_device_from_object(env, thiz);
     if (!device) {
@@ -180,17 +177,14 @@
 
     jbyte* bufferBytes = NULL;
     if (buffer) {
-        if (env->GetArrayLength(buffer) < length) {
-            jniThrowException(env, "java/lang/ArrayIndexOutOfBoundsException", NULL);
-            return -1;
-        }
-        bufferBytes = env->GetByteArrayElements(buffer, 0);
+        bufferBytes = (jbyte*)env->GetPrimitiveArrayCritical(buffer, NULL);
     }
 
-    jint result = usb_device_bulk_transfer(device, endpoint, bufferBytes, length, timeout);
+    jint result = usb_device_bulk_transfer(device, endpoint, bufferBytes + start, length, timeout);
 
-    if (bufferBytes)
-        env->ReleaseByteArrayElements(buffer, bufferBytes, 0);
+    if (bufferBytes) {
+        env->ReleasePrimitiveArrayCritical(buffer, bufferBytes, 0);
+    }
 
     return result;
 }
@@ -235,9 +229,9 @@
     {"native_get_desc",         "()[B", (void *)android_hardware_UsbDeviceConnection_get_desc},
     {"native_claim_interface",  "(IZ)Z",(void *)android_hardware_UsbDeviceConnection_claim_interface},
     {"native_release_interface","(I)Z", (void *)android_hardware_UsbDeviceConnection_release_interface},
-    {"native_control_request",  "(IIII[BII)I",
+    {"native_control_request",  "(IIII[BIII)I",
                                         (void *)android_hardware_UsbDeviceConnection_control_request},
-    {"native_bulk_request",     "(I[BII)I",
+    {"native_bulk_request",     "(I[BIII)I",
                                         (void *)android_hardware_UsbDeviceConnection_bulk_request},
     {"native_request_wait",             "()Landroid/hardware/usb/UsbRequest;",
                                         (void *)android_hardware_UsbDeviceConnection_request_wait},
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 3a23aa1..b87fe27 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -868,8 +868,7 @@
 }
 
 static void android_view_GLES20Canvas_drawLayer(JNIEnv* env, jobject clazz,
-        OpenGLRenderer* renderer, Layer* layer, jfloat x, jfloat y, SkPaint* paint) {
-    // TODO: don't pass the paint from java
+        OpenGLRenderer* renderer, Layer* layer, jfloat x, jfloat y) {
     renderer->drawLayer(layer, x, y);
 }
 
@@ -1051,7 +1050,7 @@
     { "nClearLayerTexture",      "(I)V",       (void*) android_view_GLES20Canvas_clearLayerTexture },
     { "nDestroyLayer",           "(I)V",       (void*) android_view_GLES20Canvas_destroyLayer },
     { "nDestroyLayerDeferred",   "(I)V",       (void*) android_view_GLES20Canvas_destroyLayerDeferred },
-    { "nDrawLayer",              "(IIFFI)V",   (void*) android_view_GLES20Canvas_drawLayer },
+    { "nDrawLayer",              "(IIFF)V",    (void*) android_view_GLES20Canvas_drawLayer },
     { "nCopyLayer",              "(II)Z",      (void*) android_view_GLES20Canvas_copyLayer },
     { "nClearLayerUpdates",      "(I)V",       (void*) android_view_GLES20Canvas_clearLayerUpdates },
     { "nPushLayerUpdate",        "(II)V",      (void*) android_view_GLES20Canvas_pushLayerUpdate },
diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp
index 9501cf2..198814a 100644
--- a/core/jni/android_view_InputEventReceiver.cpp
+++ b/core/jni/android_view_InputEventReceiver.cpp
@@ -118,8 +118,13 @@
 
 int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
     if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
-        ALOGE("channel '%s' ~ Publisher closed input channel or an error occurred.  "
+#if DEBUG_DISPATCH_CYCLE
+        // This error typically occurs when the publisher has closed the input channel
+        // as part of removing a window or finishing an IME session, in which case
+        // the consumer will soon be disposed as well.
+        ALOGD("channel '%s' ~ Publisher closed input channel or an error occurred.  "
                 "events=0x%x", getInputChannelName(), events);
+#endif
         return 0; // remove the callback
     }
 
diff --git a/core/jni/android_view_InputEventSender.cpp b/core/jni/android_view_InputEventSender.cpp
new file mode 100644
index 0000000..8a149d8
--- /dev/null
+++ b/core/jni/android_view_InputEventSender.cpp
@@ -0,0 +1,312 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "InputEventSender"
+
+//#define LOG_NDEBUG 0
+
+// Log debug messages about the dispatch cycle.
+#define DEBUG_DISPATCH_CYCLE 0
+
+
+#include "JNIHelp.h"
+
+#include <android_runtime/AndroidRuntime.h>
+#include <utils/Log.h>
+#include <utils/Looper.h>
+#include <utils/threads.h>
+#include <utils/KeyedVector.h>
+#include <androidfw/InputTransport.h>
+#include "android_os_MessageQueue.h"
+#include "android_view_InputChannel.h"
+#include "android_view_KeyEvent.h"
+#include "android_view_MotionEvent.h"
+
+namespace android {
+
+static struct {
+    jclass clazz;
+
+    jmethodID dispatchInputEventFinished;
+} gInputEventSenderClassInfo;
+
+
+class NativeInputEventSender : public LooperCallback {
+public:
+    NativeInputEventSender(JNIEnv* env,
+            jobject senderObj, const sp<InputChannel>& inputChannel,
+            const sp<MessageQueue>& messageQueue);
+
+    status_t initialize();
+    void dispose();
+    status_t sendKeyEvent(uint32_t seq, const KeyEvent* event);
+    status_t sendMotionEvent(uint32_t seq, const MotionEvent* event);
+
+protected:
+    virtual ~NativeInputEventSender();
+
+private:
+    jobject mSenderObjGlobal;
+    InputPublisher mInputPublisher;
+    sp<MessageQueue> mMessageQueue;
+    KeyedVector<uint32_t, uint32_t> mPublishedSeqMap;
+    uint32_t mNextPublishedSeq;
+
+    const char* getInputChannelName() {
+        return mInputPublisher.getChannel()->getName().string();
+    }
+
+    virtual int handleEvent(int receiveFd, int events, void* data);
+    status_t receiveFinishedSignals(JNIEnv* env);
+};
+
+
+NativeInputEventSender::NativeInputEventSender(JNIEnv* env,
+        jobject senderObj, const sp<InputChannel>& inputChannel,
+        const sp<MessageQueue>& messageQueue) :
+        mSenderObjGlobal(env->NewGlobalRef(senderObj)),
+        mInputPublisher(inputChannel), mMessageQueue(messageQueue),
+        mNextPublishedSeq(1) {
+#if DEBUG_DISPATCH_CYCLE
+    ALOGD("channel '%s' ~ Initializing input event sender.", getInputChannelName());
+#endif
+}
+
+NativeInputEventSender::~NativeInputEventSender() {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    env->DeleteGlobalRef(mSenderObjGlobal);
+}
+
+status_t NativeInputEventSender::initialize() {
+    int receiveFd = mInputPublisher.getChannel()->getFd();
+    mMessageQueue->getLooper()->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, this, NULL);
+    return OK;
+}
+
+void NativeInputEventSender::dispose() {
+#if DEBUG_DISPATCH_CYCLE
+    ALOGD("channel '%s' ~ Disposing input event sender.", getInputChannelName());
+#endif
+
+    mMessageQueue->getLooper()->removeFd(mInputPublisher.getChannel()->getFd());
+}
+
+status_t NativeInputEventSender::sendKeyEvent(uint32_t seq, const KeyEvent* event) {
+#if DEBUG_DISPATCH_CYCLE
+    ALOGD("channel '%s' ~ Sending key event, seq=%u.", getInputChannelName(), seq);
+#endif
+
+    uint32_t publishedSeq = mNextPublishedSeq++;
+    status_t status = mInputPublisher.publishKeyEvent(publishedSeq,
+            event->getDeviceId(), event->getSource(), event->getAction(), event->getFlags(),
+            event->getKeyCode(), event->getScanCode(), event->getMetaState(),
+            event->getRepeatCount(), event->getDownTime(), event->getEventTime());
+    if (status) {
+        ALOGW("Failed to send key event on channel '%s'.  status=%d",
+                getInputChannelName(), status);
+        return status;
+    }
+    mPublishedSeqMap.add(publishedSeq, seq);
+    return OK;
+}
+
+status_t NativeInputEventSender::sendMotionEvent(uint32_t seq, const MotionEvent* event) {
+#if DEBUG_DISPATCH_CYCLE
+    ALOGD("channel '%s' ~ Sending motion event, seq=%u.", getInputChannelName(), seq);
+#endif
+
+    uint32_t publishedSeq;
+    for (size_t i = 0; i <= event->getHistorySize(); i++) {
+        publishedSeq = mNextPublishedSeq++;
+        status_t status = mInputPublisher.publishMotionEvent(publishedSeq,
+                event->getDeviceId(), event->getSource(), event->getAction(), event->getFlags(),
+                event->getEdgeFlags(), event->getMetaState(), event->getButtonState(),
+                event->getXOffset(), event->getYOffset(),
+                event->getXPrecision(), event->getYPrecision(),
+                event->getDownTime(), event->getHistoricalEventTime(i),
+                event->getPointerCount(), event->getPointerProperties(),
+                event->getHistoricalRawPointerCoords(0, i));
+        if (status) {
+            ALOGW("Failed to send motion event sample on channel '%s'.  status=%d",
+                    getInputChannelName(), status);
+            return status;
+        }
+    }
+    mPublishedSeqMap.add(publishedSeq, seq);
+    return OK;
+}
+
+int NativeInputEventSender::handleEvent(int receiveFd, int events, void* data) {
+    if (events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP)) {
+#if DEBUG_DISPATCH_CYCLE
+        // This error typically occurs when the consumer has closed the input channel
+        // as part of finishing an IME session, in which case the publisher will
+        // soon be disposed as well.
+        ALOGD("channel '%s' ~ Consumer closed input channel or an error occurred.  "
+                "events=0x%x", getInputChannelName(), events);
+#endif
+        return 0; // remove the callback
+    }
+
+    if (!(events & ALOOPER_EVENT_INPUT)) {
+        ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event.  "
+                "events=0x%x", getInputChannelName(), events);
+        return 1;
+    }
+
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    status_t status = receiveFinishedSignals(env);
+    mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
+    return status == OK || status == NO_MEMORY ? 1 : 0;
+}
+
+status_t NativeInputEventSender::receiveFinishedSignals(JNIEnv* env) {
+#if DEBUG_DISPATCH_CYCLE
+    ALOGD("channel '%s' ~ Receiving finished signals.", getInputChannelName());
+#endif
+
+    bool skipCallbacks = false;
+    for (;;) {
+        uint32_t publishedSeq;
+        bool handled;
+        status_t status = mInputPublisher.receiveFinishedSignal(&publishedSeq, &handled);
+        if (status) {
+            if (status == WOULD_BLOCK) {
+                return OK;
+            }
+            ALOGE("channel '%s' ~ Failed to consume finished signals.  status=%d",
+                    getInputChannelName(), status);
+            return status;
+        }
+
+        ssize_t index = mPublishedSeqMap.indexOfKey(publishedSeq);
+        if (index >= 0) {
+            uint32_t seq = mPublishedSeqMap.valueAt(index);
+            mPublishedSeqMap.removeItemsAt(index);
+
+#if DEBUG_DISPATCH_CYCLE
+            ALOGD("channel '%s' ~ Received finished signal, seq=%u, handled=%s, "
+                    "pendingEvents=%u.",
+                    getInputChannelName(), seq, handled ? "true" : "false",
+                    mPublishedSeqMap.size());
+#endif
+
+            if (!skipCallbacks) {
+                env->CallVoidMethod(mSenderObjGlobal,
+                        gInputEventSenderClassInfo.dispatchInputEventFinished,
+                        jint(seq), jboolean(handled));
+                if (env->ExceptionCheck()) {
+                    ALOGE("Exception dispatching finished signal.");
+                    skipCallbacks = true;
+                }
+            }
+        }
+    }
+}
+
+
+static jint nativeInit(JNIEnv* env, jclass clazz, jobject senderObj,
+        jobject inputChannelObj, jobject messageQueueObj) {
+    sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
+            inputChannelObj);
+    if (inputChannel == NULL) {
+        jniThrowRuntimeException(env, "InputChannel is not initialized.");
+        return 0;
+    }
+
+    sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
+    if (messageQueue == NULL) {
+        jniThrowRuntimeException(env, "MessageQueue is not initialized.");
+        return 0;
+    }
+
+    sp<NativeInputEventSender> sender = new NativeInputEventSender(env,
+            senderObj, inputChannel, messageQueue);
+    status_t status = sender->initialize();
+    if (status) {
+        String8 message;
+        message.appendFormat("Failed to initialize input event sender.  status=%d", status);
+        jniThrowRuntimeException(env, message.string());
+        return 0;
+    }
+
+    sender->incStrong(gInputEventSenderClassInfo.clazz); // retain a reference for the object
+    return reinterpret_cast<jint>(sender.get());
+}
+
+static void nativeDispose(JNIEnv* env, jclass clazz, jint senderPtr) {
+    sp<NativeInputEventSender> sender =
+            reinterpret_cast<NativeInputEventSender*>(senderPtr);
+    sender->dispose();
+    sender->decStrong(gInputEventSenderClassInfo.clazz); // drop reference held by the object
+}
+
+static jboolean nativeSendKeyEvent(JNIEnv* env, jclass clazz, jint senderPtr,
+        jint seq, jobject eventObj) {
+    sp<NativeInputEventSender> sender =
+            reinterpret_cast<NativeInputEventSender*>(senderPtr);
+    KeyEvent event;
+    android_view_KeyEvent_toNative(env, eventObj, &event);
+    status_t status = sender->sendKeyEvent(seq, &event);
+    return !status;
+}
+
+static jboolean nativeSendMotionEvent(JNIEnv* env, jclass clazz, jint senderPtr,
+        jint seq, jobject eventObj) {
+    sp<NativeInputEventSender> sender =
+            reinterpret_cast<NativeInputEventSender*>(senderPtr);
+    MotionEvent* event = android_view_MotionEvent_getNativePtr(env, eventObj);
+    status_t status = sender->sendMotionEvent(seq, event);
+    return !status;
+}
+
+
+static JNINativeMethod gMethods[] = {
+    /* name, signature, funcPtr */
+    { "nativeInit",
+            "(Landroid/view/InputEventSender;Landroid/view/InputChannel;Landroid/os/MessageQueue;)I",
+            (void*)nativeInit },
+    { "nativeDispose", "(I)V",
+            (void*)nativeDispose },
+    { "nativeSendKeyEvent", "(IILandroid/view/KeyEvent;)Z",
+            (void*)nativeSendKeyEvent },
+    { "nativeSendMotionEvent", "(IILandroid/view/MotionEvent;)Z",
+            (void*)nativeSendMotionEvent },
+};
+
+#define FIND_CLASS(var, className) \
+        var = env->FindClass(className); \
+        LOG_FATAL_IF(! var, "Unable to find class " className); \
+        var = jclass(env->NewGlobalRef(var));
+
+#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \
+        var = env->GetMethodID(clazz, methodName, methodDescriptor); \
+        LOG_FATAL_IF(! var, "Unable to find method " methodName);
+
+int register_android_view_InputEventSender(JNIEnv* env) {
+    int res = jniRegisterNativeMethods(env, "android/view/InputEventSender",
+            gMethods, NELEM(gMethods));
+    LOG_FATAL_IF(res < 0, "Unable to register native methods.");
+
+    FIND_CLASS(gInputEventSenderClassInfo.clazz, "android/view/InputEventSender");
+
+    GET_METHOD_ID(gInputEventSenderClassInfo.dispatchInputEventFinished,
+            gInputEventSenderClassInfo.clazz,
+            "dispatchInputEventFinished", "(IZ)V");
+    return 0;
+}
+
+} // namespace android
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index be8f5f4..666d1c6 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -74,6 +74,11 @@
     <protected-broadcast android:name="android.app.action.ENTER_DESK_MODE" />
     <protected-broadcast android:name="android.app.action.EXIT_DESK_MODE" />
 
+    <protected-broadcast android:name="android.appwidget.action.APPWIDGET_UPDATE_OPTIONS" />
+    <protected-broadcast android:name="android.appwidget.action.APPWIDGET_DELETED" />
+    <protected-broadcast android:name="android.appwidget.action.APPWIDGET_DISABLED" />
+    <protected-broadcast android:name="android.appwidget.action.APPWIDGET_ENABLED" />
+
     <protected-broadcast android:name="android.backup.intent.RUN" />
     <protected-broadcast android:name="android.backup.intent.CLEAR" />
     <protected-broadcast android:name="android.backup.intent.INIT" />
@@ -2308,6 +2313,12 @@
             </intent-filter>
         </receiver>
 
+        <receiver android:name="com.android.server.updates.SELinuxPolicyInstallReceiver" >
+            <intent-filter>
+                <action android:name="android.intent.action.UPDATE_SEPOLICY" />
+            </intent-filter>
+        </receiver>
+
         <receiver android:name="com.android.server.MasterClearReceiver"
             android:permission="android.permission.MASTER_CLEAR"
             android:priority="100" >
diff --git a/core/res/res/drawable-ldrtl-hdpi/btn_cab_done_default_holo_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/btn_cab_done_default_holo_dark.9.png
new file mode 100644
index 0000000..45450e4
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/btn_cab_done_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/btn_cab_done_default_holo_light.9.png b/core/res/res/drawable-ldrtl-hdpi/btn_cab_done_default_holo_light.9.png
new file mode 100644
index 0000000..b568989
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/btn_cab_done_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/btn_cab_done_focused_holo_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/btn_cab_done_focused_holo_dark.9.png
new file mode 100644
index 0000000..e043458
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/btn_cab_done_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/btn_cab_done_focused_holo_light.9.png b/core/res/res/drawable-ldrtl-hdpi/btn_cab_done_focused_holo_light.9.png
new file mode 100644
index 0000000..f208c99
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/btn_cab_done_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/btn_cab_done_pressed_holo_dark.9.png b/core/res/res/drawable-ldrtl-hdpi/btn_cab_done_pressed_holo_dark.9.png
new file mode 100644
index 0000000..94eb994
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/btn_cab_done_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-hdpi/btn_cab_done_pressed_holo_light.9.png b/core/res/res/drawable-ldrtl-hdpi/btn_cab_done_pressed_holo_light.9.png
new file mode 100644
index 0000000..1fee149
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-hdpi/btn_cab_done_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/btn_cab_done_default_holo_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/btn_cab_done_default_holo_dark.9.png
new file mode 100644
index 0000000..abffc49
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/btn_cab_done_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/btn_cab_done_default_holo_light.9.png b/core/res/res/drawable-ldrtl-mdpi/btn_cab_done_default_holo_light.9.png
new file mode 100644
index 0000000..a369081
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/btn_cab_done_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/btn_cab_done_focused_holo_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/btn_cab_done_focused_holo_dark.9.png
new file mode 100644
index 0000000..e33e964
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/btn_cab_done_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/btn_cab_done_focused_holo_light.9.png b/core/res/res/drawable-ldrtl-mdpi/btn_cab_done_focused_holo_light.9.png
new file mode 100644
index 0000000..0a845dd
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/btn_cab_done_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/btn_cab_done_pressed_holo_dark.9.png b/core/res/res/drawable-ldrtl-mdpi/btn_cab_done_pressed_holo_dark.9.png
new file mode 100644
index 0000000..74b0352
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/btn_cab_done_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-mdpi/btn_cab_done_pressed_holo_light.9.png b/core/res/res/drawable-ldrtl-mdpi/btn_cab_done_pressed_holo_light.9.png
new file mode 100644
index 0000000..bfb4972
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-mdpi/btn_cab_done_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/btn_cab_done_default_holo_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/btn_cab_done_default_holo_dark.9.png
new file mode 100644
index 0000000..d253dd4
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/btn_cab_done_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/btn_cab_done_default_holo_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/btn_cab_done_default_holo_light.9.png
new file mode 100644
index 0000000..65f9ec1
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/btn_cab_done_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/btn_cab_done_focused_holo_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/btn_cab_done_focused_holo_dark.9.png
new file mode 100644
index 0000000..105da60
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/btn_cab_done_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/btn_cab_done_focused_holo_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/btn_cab_done_focused_holo_light.9.png
new file mode 100644
index 0000000..de53be7
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/btn_cab_done_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/btn_cab_done_pressed_holo_dark.9.png b/core/res/res/drawable-ldrtl-xhdpi/btn_cab_done_pressed_holo_dark.9.png
new file mode 100644
index 0000000..3be0b0c
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/btn_cab_done_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-ldrtl-xhdpi/btn_cab_done_pressed_holo_light.9.png b/core/res/res/drawable-ldrtl-xhdpi/btn_cab_done_pressed_holo_light.9.png
new file mode 100644
index 0000000..878c702
--- /dev/null
+++ b/core/res/res/drawable-ldrtl-xhdpi/btn_cab_done_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/layout-xlarge/activity_list.xml b/core/res/res/layout-xlarge/activity_list.xml
index edf6ee6..bf46af8 100644
--- a/core/res/res/layout-xlarge/activity_list.xml
+++ b/core/res/res/layout-xlarge/activity_list.xml
@@ -52,7 +52,8 @@
                 android:singleLine="true"
                 android:ellipsize="end"
                 android:layout_width="match_parent"
-                android:layout_height="wrap_content" />
+                android:layout_height="wrap_content"
+                android:textAlignment="viewStart" />
         </LinearLayout>
         <View android:id="@+id/titleDivider"
             android:layout_width="match_parent"
diff --git a/core/res/res/layout/alert_dialog.xml b/core/res/res/layout/alert_dialog.xml
index 35552d3..59e56af 100644
--- a/core/res/res/layout/alert_dialog.xml
+++ b/core/res/res/layout/alert_dialog.xml
@@ -55,7 +55,8 @@
                 android:singleLine="true"
                 android:ellipsize="end"
                 android:layout_width="match_parent"
-                android:layout_height="wrap_content" />
+                android:layout_height="wrap_content"
+                android:textAlignment="viewStart" />
         </LinearLayout>
         <ImageView android:id="@+id/titleDivider"
             android:layout_width="match_parent"
diff --git a/core/res/res/layout/alert_dialog_holo.xml b/core/res/res/layout/alert_dialog_holo.xml
index 3f1fa3c..34cb21d 100644
--- a/core/res/res/layout/alert_dialog_holo.xml
+++ b/core/res/res/layout/alert_dialog_holo.xml
@@ -53,7 +53,8 @@
                 android:singleLine="true"
                 android:ellipsize="end"
                 android:layout_width="match_parent"
-                android:layout_height="wrap_content" />
+                android:layout_height="wrap_content"
+                android:textAlignment="viewStart" />
         </LinearLayout>
         <View android:id="@+id/titleDivider"
             android:layout_width="match_parent"
diff --git a/core/res/res/layout/app_not_authorized.xml b/core/res/res/layout/app_not_authorized.xml
new file mode 100644
index 0000000..bd40eeb
--- /dev/null
+++ b/core/res/res/layout/app_not_authorized.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** 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.
+*/
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <!-- Customizable description text -->
+    <TextView android:id="@+id/description"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:layout_gravity="start|center_vertical"
+        android:paddingTop="16dip"
+        android:paddingBottom="16dip"
+        android:paddingStart="16dip"
+        android:paddingEnd="16dip"
+        android:text="@string/app_no_restricted_accounts"
+    />
+
+    <!-- Horizontal divider line -->
+    <View android:layout_height="1dip"
+        android:layout_width="match_parent"
+        android:background="?android:attr/dividerHorizontal" />
+
+    <!-- Alert dialog style buttons along the bottom. -->
+    <LinearLayout android:id="@+id/button_bar"
+        style="?android:attr/buttonBarStyle"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:measureWithLargestChild="true">
+        <Button android:id="@android:id/button1"
+            style="?android:attr/buttonBarButtonStyle"
+            android:layout_width="wrap_content" android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:text="@android:string/yes"
+            android:onClick="onCancelButtonClicked" />
+    </LinearLayout>
+</LinearLayout>
diff --git a/core/res/res/layout/wifi_p2p_dialog_row.xml b/core/res/res/layout/wifi_p2p_dialog_row.xml
index ef810a0..2c88b10 100644
--- a/core/res/res/layout/wifi_p2p_dialog_row.xml
+++ b/core/res/res/layout/wifi_p2p_dialog_row.xml
@@ -23,5 +23,6 @@
     <TextView
             android:id="@+id/value"
             style="@style/wifi_item_content"
-            android:textStyle="bold" />
+            android:textStyle="bold"
+            android:textAlignment="viewStart" />
 </LinearLayout>
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 090c928..8a956d0 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -200,10 +200,8 @@
     <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Direkte toegang tot die mikrofoon om oudio op te neem."</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string>
     <string name="permgroupdesc_camera" msgid="2933667372289567714">"Direkte toegang tot kamera vir die neem van foto of video."</string>
-    <!-- no translation found for permgrouplab_screenlock (8275500173330718168) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) -->
-    <skip />
+    <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Sluitskerm"</string>
+    <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Vermoë om die gedrag van die sluitskerm op jou toestel te beïnvloed."</string>
     <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Jou programme-inligting"</string>
     <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Vermoë om die gedrag van ander programme op jou toestel te beïnvloed."</string>
     <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Muurpapier"</string>
@@ -1050,10 +1048,8 @@
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Teksaksies"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Bergingspasie word min"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Sommige stelselfunksies werk moontlik nie"</string>
-    <!-- no translation found for app_running_notification_title (4625479411505090209) -->
-    <skip />
-    <!-- no translation found for app_running_notification_text (3368349329989620597) -->
-    <skip />
+    <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> loop"</string>
+    <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> loop tans"</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
     <string name="cancel" msgid="6442560571259935130">"Kanselleer"</string>
     <string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1147,7 +1143,8 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Aan:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Voer die vereiste PIN in:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Die foon sal tydelik van Wi-Fi ontkoppel terwyl dit aan <xliff:g id="DEVICE_NAME">%1$s</xliff:g> gekoppel is"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Die tablet sal tydelik van Wi-Fi ontkoppel terwyl dit aan <xliff:g id="DEVICE_NAME">%1$s</xliff:g> gekoppel is"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Die foon sal tydelik van Wi-Fi ontkoppel terwyl dit aan <xliff:g id="DEVICE_NAME">%1$s</xliff:g> gekoppel is"</string>
     <string name="select_character" msgid="3365550120617701745">"Voeg karakter in"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Stuur SMS-boodskappe"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; stuur \'n groot aantal SMS-boodskappe. Wil jy hierdie program toelaat om voort te gaan om boodskappe te stuur?"</string>
@@ -1473,8 +1470,7 @@
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Jy het jou ontsluitpatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer verkeerdelik geteken. Na nog <xliff:g id="NUMBER_1">%d</xliff:g> onsuksesvolle pogings, sal jy gevra word om jou foon te ontsluit deur middel van \'n e-posrekening."\n\n" Probeer weer oor <xliff:g id="NUMBER_2">%d</xliff:g> sekondes."</string>
     <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Verwyder"</string>
-    <!-- no translation found for safe_media_volume_warning (7324161939475478066) -->
-    <skip />
+    <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Verhoog volume bo aanbevole vlak?"\n"Deur vir lang tydperke na hoë volume te luister, kan jou gehoor beskadig word."</string>
     <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Hou aan met twee vingers inhou om toeganklikheid te aktiveer."</string>
     <string name="accessibility_enabled" msgid="1381972048564547685">"Toeganklikheid geaktiveer."</string>
     <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Toeganklikheid gekanselleer."</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 0ee9f29..103f0b7 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"ለ፦"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"የሚፈለገውን ፒን ተይብ፦"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"ፒን፦"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"ስልኩ ከ<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ጋር ተገናኝቶ ባለበት ጊዜ በጊዜያዊነት ከWi-Fi ጋር ያለው ግንኙነት ይቋረጣል"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"ስልኩ ከ<xliff:g id="DEVICE_NAME">%1$s</xliff:g> ጋር ተገናኝቶ ባለበት ጊዜ በጊዜያዊነት ከWi-Fi ጋር ያለው ግንኙነት ይቋረጣል"</string>
     <string name="select_character" msgid="3365550120617701745">"ቁምፊ አስገባ"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"የSMS መልዕክቶች መበላክ ላይ"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ቁጥራቸው ብዙ የሆኑ የኤስ.ኤም.ኤስ. መልዕክቶችን እየላከ ነው። ይሄ መተግበሪያ መልዕክቶችን መላኩን እንዲቀጥል መፍቀድ ትፈልጋለህ?"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 0c5039f..b8fc281 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"إلى:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"اكتب رقم التعريف الشخصي المطلوب:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"رقم التعريف الشخصي:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"سيتم قطع اتصال الهاتف مؤقتًا بشبكة Wi-Fi في الوقت الذي يكون فيه متصلاً بـ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"سيتم قطع اتصال الهاتف مؤقتًا بشبكة Wi-Fi في الوقت الذي يكون فيه متصلاً بـ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"إدراج حرف"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"إرسال رسائل قصيرة SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; يرسل عددًا كبيرًا من الرسائل القصيرة SMS. هل تريد السماح لهذا التطبيق بالاستمرار في إرسال الرسائل؟"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index 3fbd1f8..eb7c6cd 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -200,10 +200,8 @@
     <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Прамы доступ да мікрафону для запісу гуку."</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Камера"</string>
     <string name="permgroupdesc_camera" msgid="2933667372289567714">"Прамы доступ да камеры, каб зрабіць здымак ці зняць відэа."</string>
-    <!-- no translation found for permgrouplab_screenlock (8275500173330718168) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) -->
-    <skip />
+    <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Экран блакіроўкі"</string>
+    <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Магчымасць уплываць на паводзіны экрана блакіроўкi на вашай прыладзе."</string>
     <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Інфармацыя аб вашых прыкладаннях"</string>
     <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Магчымасць уплываць на паводзіны іншых прыкладанняў на вашай прыладзе."</string>
     <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Шпалеры"</string>
@@ -1050,10 +1048,8 @@
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Дзеянні з тэкстам"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Месца для захавання на зыходзе"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Некаторыя сістэмныя функцыі могуць не працаваць"</string>
-    <!-- no translation found for app_running_notification_title (4625479411505090209) -->
-    <skip />
-    <!-- no translation found for app_running_notification_text (3368349329989620597) -->
-    <skip />
+    <string name="app_running_notification_title" msgid="4625479411505090209">"Прыкладанне <xliff:g id="APP_NAME">%1$s</xliff:g> працуе"</string>
+    <string name="app_running_notification_text" msgid="3368349329989620597">"Прыкладанне <xliff:g id="APP_NAME">%1$s</xliff:g> зараз працуе"</string>
     <string name="ok" msgid="5970060430562524910">"ОК"</string>
     <string name="cancel" msgid="6442560571259935130">"Адмяніць"</string>
     <string name="yes" msgid="5362982303337969312">"ОК"</string>
@@ -1147,7 +1143,8 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Каму:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Увядзіце патрэбны PIN-код:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN-код"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Тэлефон будзе часова адключаны ад Wi-Fi, пакуль ён падлучаны да прылады <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Тэлефон будзе часова адключаны ад сеткі Wi-Fi, пакуль ён падлучаны да прылады <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Тэлефон будзе часова адключаны ад Wi-Fi, пакуль ён падлучаны да прылады <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Уставіць сімвал"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Адпраўка SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"Прыкладанне &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; дасылае вялікую колькасць SMS-паведамленняў. Дазволіць гэтаму прыкладанню працягваць адпраўляць паведамленні?"</string>
@@ -1473,8 +1470,7 @@
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Вы няправільна ўвялі графічны ключ разблакiроўкi пэўную колькасць разоў: <xliff:g id="NUMBER_0">%d</xliff:g>. Пасля яшчэ некалькiх няўдалых спроб (<xliff:g id="NUMBER_1">%d</xliff:g>) вам будзе прапанавана разблакiраваць тэлефон, увайшоўшы ў Google."\n\n" Паўтарыце спробу праз <xliff:g id="NUMBER_2">%d</xliff:g> с."</string>
     <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Выдалiць"</string>
-    <!-- no translation found for safe_media_volume_warning (7324161939475478066) -->
-    <skip />
+    <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Павялiчыць гук больш за рэкамендаваны ўзровень?"\n"Доўгае слуханне музыкi на вялiкай гучнасцi можа пашкодзiць ваш слых."</string>
     <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Утрымлiвайце два пальцы, каб уключыць доступ."</string>
     <string name="accessibility_enabled" msgid="1381972048564547685">"Даступнасць уключана."</string>
     <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Даступнасць адменена."</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 66f56b8..4831909 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"До:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Въведете задължителния ПИН:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"ПИН:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Телефонът временно ще прекрати връзката с Wi-Fi, докато е свързан с/ъс <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Телефонът временно ще прекрати връзката с Wi-Fi, докато е свързан с/ъс <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Вмъкване на знак"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Изпращане на SMS съобщения"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; изпраща голям брой SMS съобщения. Искате ли да разрешите на това приложение да продължи да го прави?"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 2faf80d..365186c 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -200,10 +200,8 @@
     <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Accés directe al micròfon per enregistrar àudio."</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Càmera"</string>
     <string name="permgroupdesc_camera" msgid="2933667372289567714">"Accés directe a la càmera per a la captura d\'imatges o de vídeos."</string>
-    <!-- no translation found for permgrouplab_screenlock (8275500173330718168) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) -->
-    <skip />
+    <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Bloqueig de pantalla"</string>
+    <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Capacitat d\'influir en el comportament de la pantalla de bloqueig al dispositiu."</string>
     <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Informació de les aplicacions"</string>
     <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Capacitat d\'afectar el rendiment d\'altres aplicacions del dispositiu."</string>
     <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Fons de pantalla"</string>
@@ -1050,10 +1048,8 @@
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Accions de text"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"S\'està acabant l\'espai d\'emmagatzematge"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"És possible que algunes funcions del sistema no funcionin"</string>
-    <!-- no translation found for app_running_notification_title (4625479411505090209) -->
-    <skip />
-    <!-- no translation found for app_running_notification_text (3368349329989620597) -->
-    <skip />
+    <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> s\'està executant"</string>
+    <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> s\'està executant en aquests moments"</string>
     <string name="ok" msgid="5970060430562524910">"D\'acord"</string>
     <string name="cancel" msgid="6442560571259935130">"Cancel·la"</string>
     <string name="yes" msgid="5362982303337969312">"D\'acord"</string>
@@ -1147,7 +1143,8 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Per a:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Introdueix el PIN sol·licitat:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"El telèfon es desconnectarà temporalment de la Wi-Fi mentre estigui connectat a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"La tauleta es desconnectarà temporalment de la Wi-Fi mentre estigui connectada a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"El telèfon es desconnectarà temporalment de la Wi-Fi mentre estigui connectat a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Insereix un caràcter"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"S\'estan enviant missatges SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; està enviant molts missatges SMS. Vols permetre que aquesta aplicació continuï enviant missatges?"</string>
@@ -1473,8 +1470,7 @@
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Has dibuixat el patró de desbloqueig <xliff:g id="NUMBER_0">%d</xliff:g> vegades de manera incorrecta. Després de <xliff:g id="NUMBER_1">%d</xliff:g> intents incorrectes més, se\'t demanarà que desbloquegis el telèfon amb un compte de correu electrònic."\n\n" Torna-ho a provar d\'aquí a <xliff:g id="NUMBER_2">%d</xliff:g> segons."</string>
     <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Elimina"</string>
-    <!-- no translation found for safe_media_volume_warning (7324161939475478066) -->
-    <skip />
+    <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Vols augmentar el volum per sobre del nivell de seguretat?"\n"Escoltar música a un volum alt durant períodes llargs pot danyar l\'oïda."</string>
     <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Mantén premuts els dos dits per activar l\'accessibilitat."</string>
     <string name="accessibility_enabled" msgid="1381972048564547685">"S\'ha activat l\'accessibilitat."</string>
     <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Accessibilitat cancel·lada."</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 714813c..621067f 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -200,10 +200,8 @@
     <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Přímý přístup k mikrofonu a možnost nahrávání zvuku"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Fotoaparát"</string>
     <string name="permgroupdesc_camera" msgid="2933667372289567714">"Přímý přístup k fotoaparátu a možnost pořizování fotografií a videí"</string>
-    <!-- no translation found for permgrouplab_screenlock (8275500173330718168) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) -->
-    <skip />
+    <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Obrazovka uzamčení"</string>
+    <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Možnost ovlivnit chování obrazovky uzamčení v zařízení."</string>
     <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Informace o vašich aplikacích"</string>
     <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Možnost ovlivnit chování dalších aplikací v zařízení"</string>
     <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Tapeta"</string>
@@ -1050,10 +1048,8 @@
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Operace s textem"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"V úložišti je málo místa"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Některé systémové funkce nemusí fungovat"</string>
-    <!-- no translation found for app_running_notification_title (4625479411505090209) -->
-    <skip />
-    <!-- no translation found for app_running_notification_text (3368349329989620597) -->
-    <skip />
+    <string name="app_running_notification_title" msgid="4625479411505090209">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> je spuštěna"</string>
+    <string name="app_running_notification_text" msgid="3368349329989620597">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> je aktuálně spuštěná"</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
     <string name="cancel" msgid="6442560571259935130">"Zrušit"</string>
     <string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1147,7 +1143,8 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Komu:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Zadejte požadovaný kód PIN:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Telefon se při připojení k zařízení <xliff:g id="DEVICE_NAME">%1$s</xliff:g> dočasně odpojí od sítě Wi-Fi"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Tablet se při připojení k zařízení <xliff:g id="DEVICE_NAME">%1$s</xliff:g> dočasně odpojí od sítě Wi-Fi"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Telefon se při připojení k zařízení <xliff:g id="DEVICE_NAME">%1$s</xliff:g> dočasně odpojí od sítě Wi-Fi"</string>
     <string name="select_character" msgid="3365550120617701745">"Vkládání znaků"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Odesílání zpráv SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"Aplikace &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;odesílá velký počet SMS zpráv. Chcete aplikaci povolit, aby zprávy odesílala i nadále?"</string>
@@ -1473,8 +1470,7 @@
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Již <xliff:g id="NUMBER_0">%d</xliff:g>krát jste nesprávně nakreslili své heslo odemknutí. Po <xliff:g id="NUMBER_1">%d</xliff:g> dalších neúspěšných pokusech budete požádáni o odemčení telefonu pomocí e-mailového účtu."\n\n" Zkuste to znovu za <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
     <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Odebrat"</string>
-    <!-- no translation found for safe_media_volume_warning (7324161939475478066) -->
-    <skip />
+    <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Zvýšit hlasitost nad doporučenou úroveň?"\n"Dlouhodobý poslech hlasitého zvuku může poškodit sluch."</string>
     <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Usnadnění zapnete dlouhým stisknutím dvěma prsty."</string>
     <string name="accessibility_enabled" msgid="1381972048564547685">"Usnadnění přístupu je aktivováno."</string>
     <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Usnadnění zrušeno."</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index ed3aa40..3dcd8f3 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -200,10 +200,8 @@
     <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Direkte adgang til mikrofonen, så der kan optages lyd."</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string>
     <string name="permgroupdesc_camera" msgid="2933667372289567714">"Direkte adgang til kamera, så der kan tages billeder eller optages video."</string>
-    <!-- no translation found for permgrouplab_screenlock (8275500173330718168) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) -->
-    <skip />
+    <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Lås skærm"</string>
+    <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Evne til at påvirke låseskærmens adfærd på enheden."</string>
     <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Oplysninger om dine applikationer"</string>
     <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Evne til at påvirke andre applikationers adfærd på din enhed."</string>
     <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Baggrund"</string>
@@ -1050,10 +1048,8 @@
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Teksthandlinger"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Der er snart ikke mere lagerplads"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Nogle systemfunktioner virker måske ikke"</string>
-    <!-- no translation found for app_running_notification_title (4625479411505090209) -->
-    <skip />
-    <!-- no translation found for app_running_notification_text (3368349329989620597) -->
-    <skip />
+    <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> kører"</string>
+    <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> kører i øjeblikket"</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
     <string name="cancel" msgid="6442560571259935130">"Annuller"</string>
     <string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1147,7 +1143,8 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Til:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Skriv den påkrævede pinkode:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"Pinkode:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Telefonens Wi-Fi-forbindelse vil midlertidigt blive afbrudt, når den er tilsluttet <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Telefonens Wi-Fi-forbindelse vil midlertidigt blive afbrudt, når den er tilsluttet <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Telefonens Wi-Fi-forbindelse vil midlertidigt blive afbrudt, når den er tilsluttet <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Indsæt tegn"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Sender sms-beskeder"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; sender et stort antal sms-beskeder. Vil du tillade, at denne app fortsat sender beskeder?"</string>
@@ -1473,8 +1470,7 @@
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Du har tegnet dit oplåsningsmønster forkert <xliff:g id="NUMBER_0">%d</xliff:g> gange. Efter <xliff:g id="NUMBER_1">%d</xliff:g> yderligere mislykkede forsøg til vil du blive bedt om at låse din telefon op ved hjælp af en e-mailkonto."\n\n" Prøv igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
     <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Fjern"</string>
-    <!-- no translation found for safe_media_volume_warning (7324161939475478066) -->
-    <skip />
+    <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Skal lydstyrken være over det anbefalede niveau?"\n"Du kan skade din hørelse ved at lytte ved høj lydstyrke i længere tid."</string>
     <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Hold fortsat to fingre nede for at aktivere tilgængelighed."</string>
     <string name="accessibility_enabled" msgid="1381972048564547685">"Tilgængelighed aktiveret."</string>
     <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Tilgængelighed er annulleret."</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index 7ebea96..e15a39f 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -200,10 +200,8 @@
     <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Direkter Zugriff auf das Mikrofon zur Audioaufnahme"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string>
     <string name="permgroupdesc_camera" msgid="2933667372289567714">"Direkter Zugriff auf Kamera für Bild- oder Videoaufnahmen"</string>
-    <!-- no translation found for permgrouplab_screenlock (8275500173330718168) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) -->
-    <skip />
+    <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Bildschirm sperren"</string>
+    <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Kann die Bildschirmsperre auf Ihrem Gerät beeinflussen"</string>
     <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Informationen zu Ihren Apps"</string>
     <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Einflussnahme auf das Verhalten anderer Apps auf Ihrem Gerät"</string>
     <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Hintergrund"</string>
@@ -1050,10 +1048,8 @@
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Textaktionen"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Der Speicherplatz wird knapp"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Einige Systemfunktionen funktionieren möglicherweise nicht."</string>
-    <!-- no translation found for app_running_notification_title (4625479411505090209) -->
-    <skip />
-    <!-- no translation found for app_running_notification_text (3368349329989620597) -->
-    <skip />
+    <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> wird ausgeführt."</string>
+    <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> wird gerade ausgeführt."</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
     <string name="cancel" msgid="6442560571259935130">"Abbrechen"</string>
     <string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1147,7 +1143,8 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"An:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Geben Sie die erforderliche PIN ein:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Das Telefon wird vorübergehend vom WLAN getrennt, während eine Verbindung mit <xliff:g id="DEVICE_NAME">%1$s</xliff:g> hergestellt wird."</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Das Tablet wird vorübergehend vom WLAN getrennt, während eine Verbindung mit <xliff:g id="DEVICE_NAME">%1$s</xliff:g> besteht."</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Das Telefon wird vorübergehend vom WLAN getrennt, während eine Verbindung mit <xliff:g id="DEVICE_NAME">%1$s</xliff:g> hergestellt wird."</string>
     <string name="select_character" msgid="3365550120617701745">"Zeichen einfügen"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"SMS werden gesendet"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; sendet eine große Anzahl SMS. Möchten Sie zulassen, dass die App weiterhin Nachrichten sendet?"</string>
@@ -1473,8 +1470,7 @@
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Sie haben Ihr Entsperrungsmuster <xliff:g id="NUMBER_0">%d</xliff:g>-mal falsch gezeichnet. Nach <xliff:g id="NUMBER_1">%d</xliff:g> weiteren erfolglosen Versuchen werden Sie aufgefordert, Ihr Telefon mithilfe eines E-Mail-Kontos zu entsperren."\n\n" Versuchen Sie es in <xliff:g id="NUMBER_2">%d</xliff:g> Sekunden erneut."</string>
     <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Entfernen"</string>
-    <!-- no translation found for safe_media_volume_warning (7324161939475478066) -->
-    <skip />
+    <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Lautstärke über den Schwellenwert anheben?"\n"Wenn Sie über längere Zeiträume hinweg Musik in hoher Lautstärke hören, kann dies Ihr Gehör schädigen."</string>
     <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Drücken Sie mit zwei Fingern, um die Bedienungshilfen zu aktivieren."</string>
     <string name="accessibility_enabled" msgid="1381972048564547685">"Bedienungshilfen aktiviert"</string>
     <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Bedienungshilfen abgebrochen"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 4ac9aab..2dbb82b 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -200,10 +200,8 @@
     <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Άμεση πρόσβαση στο μικρόφωνο για την εγγραφή ήχου."</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Κάμερα"</string>
     <string name="permgroupdesc_camera" msgid="2933667372289567714">"Άμεση πρόσβαση σε κάμερα για λήψη εικόνας ή βίντεο."</string>
-    <!-- no translation found for permgrouplab_screenlock (8275500173330718168) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) -->
-    <skip />
+    <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Κλείδωμα οθόνης"</string>
+    <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Δυνατότητα επίδρασης της συμπεριφοράς της οθόνης κλειδώματος στη συσκευή σας."</string>
     <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Οι πληροφορίες των εφαρμογών σας"</string>
     <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Δυνατότητα επιρροής συμπεριφοράς άλλων εφαρμογών στη συσκευή σας."</string>
     <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Ταπετσαρία"</string>
@@ -1050,10 +1048,8 @@
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Ενέργειες κειμένου"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Ο χώρος αποθήκευσης εξαντλείται"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Ορισμένες λειτουργίες συστήματος ενδέχεται να μην λειτουργούν"</string>
-    <!-- no translation found for app_running_notification_title (4625479411505090209) -->
-    <skip />
-    <!-- no translation found for app_running_notification_text (3368349329989620597) -->
-    <skip />
+    <string name="app_running_notification_title" msgid="4625479411505090209">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> εκτελείται"</string>
+    <string name="app_running_notification_text" msgid="3368349329989620597">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> εκτελείται τώρα."</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
     <string name="cancel" msgid="6442560571259935130">"Ακύρωση"</string>
     <string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1147,7 +1143,8 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Προς:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Πληκτρολογήστε τον απαιτούμενο κωδικό PIN:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Το τηλέφωνο θα αποσυνδεθεί προσωρινά από το δίκτυο Wi-Fi ενώ συνδέεται στη συσκευή <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Το tablet θα αποσυνδεθεί προσωρινά από το δίκτυο Wi-Fi ενώ συνδέεται στη συσκευή <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Το τηλέφωνο θα αποσυνδεθεί προσωρινά από το δίκτυο Wi-Fi ενώ συνδέεται στη συσκευή <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Εισαγωγή χαρακτήρα"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Αποστολή μηνυμάτων SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"Η εφαρμογή &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; στέλνει έναν μεγάλο αριθμό μηνυμάτων SMS. Θέλετε να επιτρέψετε σε αυτήν την εφαρμογή να συνεχίσει να στέλνει μηνύματα;"</string>
@@ -1473,8 +1470,7 @@
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Σχεδιάσατε το μοτίβο ξεκλειδώματος εσφαλμένα <xliff:g id="NUMBER_0">%d</xliff:g> φορές. Μετά από <xliff:g id="NUMBER_1">%d</xliff:g> ανεπιτυχείς προσπάθειες ακόμη, θα σας ζητηθεί να ξεκλειδώσετε το τηλέφωνό σας με τη χρήση ενός λογαριασμού ηλεκτρονικού ταχυδρομείου."\n\n" Δοκιμάστε ξανά σε <xliff:g id="NUMBER_2">%d</xliff:g> δευτερόλεπτα."</string>
     <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Κατάργηση"</string>
-    <!-- no translation found for safe_media_volume_warning (7324161939475478066) -->
-    <skip />
+    <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Αυξάνετε την ένταση ήχου πάνω από το επίπεδο ασφαλείας;"\n"Αν ακούτε μουσική σε υψηλή ένταση για μεγάλο χρονικό διάστημα ενδέχεται να προκληθεί βλάβη στην ακοή σας."</string>
     <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Αγγίξτε παρατεταμένα με δύο δάχτυλα για να ενεργοποιήσετε τη λειτουργία προσβασιμότητας."</string>
     <string name="accessibility_enabled" msgid="1381972048564547685">"Ενεργοποιήθηκε η προσβασιμότητα."</string>
     <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Η λειτουργία προσβασιμότητας ακυρώθηκε."</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index f0390be..9fab86e 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -200,10 +200,8 @@
     <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Direct access to the microphone to record audio."</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Camera"</string>
     <string name="permgroupdesc_camera" msgid="2933667372289567714">"Direct access to camera for image or video capture."</string>
-    <!-- no translation found for permgrouplab_screenlock (8275500173330718168) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) -->
-    <skip />
+    <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Lock screen"</string>
+    <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Ability to affect behaviour of the lock screen on your device."</string>
     <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Your applications information"</string>
     <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Ability to affect behaviour of other applications on your device."</string>
     <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Wallpaper"</string>
@@ -1050,10 +1048,8 @@
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Text actions"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Storage space running out"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Some system functions may not work"</string>
-    <!-- no translation found for app_running_notification_title (4625479411505090209) -->
-    <skip />
-    <!-- no translation found for app_running_notification_text (3368349329989620597) -->
-    <skip />
+    <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> running"</string>
+    <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> is currently running"</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
     <string name="cancel" msgid="6442560571259935130">"Cancel"</string>
     <string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1147,7 +1143,8 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"To:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Type the required PIN:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"The phone will temporarily disconnect from Wi-FI while it\'s connected to <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"The tablet will temporarily disconnect from Wi-Fi while it\'s connected to <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"The phone will temporarily disconnect from Wi-FI while it\'s connected to <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Insert character"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Sending SMS messages"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; is sending a large number of SMS messages. Do you want to allow this app to continue sending messages?"</string>
@@ -1473,8 +1470,7 @@
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"You have incorrectly drawn your unlock pattern <xliff:g id="NUMBER_0">%d</xliff:g> times. After <xliff:g id="NUMBER_1">%d</xliff:g> more unsuccessful attempts, you will be asked to unlock your phone using an email account."\n\n" Try again in <xliff:g id="NUMBER_2">%d</xliff:g> seconds."</string>
     <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Remove"</string>
-    <!-- no translation found for safe_media_volume_warning (7324161939475478066) -->
-    <skip />
+    <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Raise volume above recommended level?"\n"Listening at high volume for long periods may damage your hearing."</string>
     <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Keep holding down two fingers to enable accessibility."</string>
     <string name="accessibility_enabled" msgid="1381972048564547685">"Accessibility enabled."</string>
     <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Accessibility cancelled."</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 3a39920..ee18e9e 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -200,10 +200,8 @@
     <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Acceso directo a micrófono para grabar audio"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Cámara"</string>
     <string name="permgroupdesc_camera" msgid="2933667372289567714">"Acceso directo a cámara para imagen o captura de video"</string>
-    <!-- no translation found for permgrouplab_screenlock (8275500173330718168) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) -->
-    <skip />
+    <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Bloquear pantalla"</string>
+    <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Capacidad para afectar el comportamiento de la pantalla de bloqueo del dispositivo."</string>
     <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Información de tus aplicaciones"</string>
     <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Capacidad para influir en el comportamiento de otras aplicaciones en el dispositivo"</string>
     <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Fondo de pantalla"</string>
@@ -1050,10 +1048,8 @@
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Acciones de texto"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Queda poco espacio de almacenamiento"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Es posible que algunas funciones del sistema no estén disponibles."</string>
-    <!-- no translation found for app_running_notification_title (4625479411505090209) -->
-    <skip />
-    <!-- no translation found for app_running_notification_text (3368349329989620597) -->
-    <skip />
+    <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> en ejecución"</string>
+    <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> se está ejecutando en este momento."</string>
     <string name="ok" msgid="5970060430562524910">"Aceptar"</string>
     <string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
     <string name="yes" msgid="5362982303337969312">"Aceptar"</string>
@@ -1147,7 +1143,8 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Para:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Escribe el PIN solicitado:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"El dispositivo se desconectará temporalmente de la red Wi-Fi mientras esté conectado a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"La tableta se desconectará temporalmente de la red Wi-Fi mientras esté conectada a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"El dispositivo se desconectará temporalmente de la red Wi-Fi mientras esté conectado a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
     <string name="select_character" msgid="3365550120617701745">"Insertar caracteres"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Enviando mensajes SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; está enviando una gran cantidad de mensajes SMS. ¿Quieres permitir que está aplicación siga enviando mensajes?"</string>
@@ -1473,8 +1470,7 @@
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Dibujaste incorrectamente tu patrón de desbloqueo <xliff:g id="NUMBER_0">%d</xliff:g> veces. Luego de <xliff:g id="NUMBER_1">%d</xliff:g> intentos incorrectos más, se te solicitará que desbloquees tu dispositivo mediante el uso de una cuenta de correo."\n\n" Vuelve a intentarlo en <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
     <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Eliminar"</string>
-    <!-- no translation found for safe_media_volume_warning (7324161939475478066) -->
-    <skip />
+    <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"¿Quieres subir el volumen por encima del nivel recomendado?"\n"Si escuchas música con el volumen alto durante períodos prolongados, puedes dañar tu audición."</string>
     <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Mantén presionado con dos dedos para activar la accesibilidad."</string>
     <string name="accessibility_enabled" msgid="1381972048564547685">"Se activó la accesibilidad."</string>
     <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Se canceló la accesibilidad."</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 2217e30..50fb83c 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -200,10 +200,8 @@
     <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Acceder directamente al micrófono para grabar audio"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Cámara"</string>
     <string name="permgroupdesc_camera" msgid="2933667372289567714">"Acceder directamente a la cámara para hacer fotos o grabar vídeos"</string>
-    <!-- no translation found for permgrouplab_screenlock (8275500173330718168) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) -->
-    <skip />
+    <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Pantalla de bloqueo"</string>
+    <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Posibilidad de modificar el comportamiento de la pantalla de bloqueo del dispositivo"</string>
     <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Información de tus aplicaciones"</string>
     <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Posibilidad de influir en el funcionamiento de otras aplicaciones del dispositivo"</string>
     <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Fondo de pantalla"</string>
@@ -1050,10 +1048,8 @@
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Acciones de texto"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Queda poco espacio"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Es posible que algunas funciones del sistema no funcionen."</string>
-    <!-- no translation found for app_running_notification_title (4625479411505090209) -->
-    <skip />
-    <!-- no translation found for app_running_notification_text (3368349329989620597) -->
-    <skip />
+    <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> se está ejecutando"</string>
+    <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> se está ejecutando."</string>
     <string name="ok" msgid="5970060430562524910">"Aceptar"</string>
     <string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
     <string name="yes" msgid="5362982303337969312">"Aceptar"</string>
@@ -1147,7 +1143,8 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Para:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Escribe el PIN solicitado:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"El teléfono se desconectará temporalmente de la red Wi-Fi mientras está conectado a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"El tablet se desconectará temporalmente de la red Wi-Fi mientras esté conectado a  <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"El teléfono se desconectará temporalmente de la red Wi-Fi mientras está conectado a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
     <string name="select_character" msgid="3365550120617701745">"Insertar carácter"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Enviando mensajes SMS..."</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; está enviando un gran número de mensajes SMS. ¿Quieres permitir que está aplicación siga enviando mensajes?"</string>
@@ -1473,8 +1470,7 @@
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Has fallado <xliff:g id="NUMBER_0">%d</xliff:g> veces al dibujar el patrón de desbloqueo. Si fallas otras <xliff:g id="NUMBER_1">%d</xliff:g> veces, deberás usar una cuenta de correo electrónico para desbloquear el teléfono."\n\n" Inténtalo de nuevo en <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
     <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Eliminar"</string>
-    <!-- no translation found for safe_media_volume_warning (7324161939475478066) -->
-    <skip />
+    <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"¿Quieres subir el volumen por encima del nivel recomendado?"\n"Escuchar sonidos a alto volumen durante largos períodos de tiempo puede dañar tus oídos."</string>
     <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Mantén la pantalla pulsada con dos dedos para habilitar las funciones de accesibilidad."</string>
     <string name="accessibility_enabled" msgid="1381972048564547685">"Accesibilidad habilitada"</string>
     <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Accesibilidad cancelada"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index ce8bd81..3abaf62 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Saaja:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Sisestage nõutav PIN-kood:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN-kood:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Telefoni ühendus WiFi-ga katkestatakse ajutiselt, kui see on ühendatud seadmega <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Telefoni ühendus WiFi-ga katkestatakse ajutiselt, kui see on ühendatud seadmega <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Sisesta tähemärk"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"SMS-sõnumite saatmine"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; saadab suurel hulgal SMS-sõnumeid. Kas tahate lubada sellel rakendusel ka edaspidi sõnumeid saata?"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 49325e1..f8c1629 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"به:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"پین لازم را تایپ کنید:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"پین:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"این گوشی به‌طور موقت از Wi-Fi قطع خواهد شد، در حالی که به <xliff:g id="DEVICE_NAME">%1$s</xliff:g> وصل است"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"این گوشی به‌طور موقت از Wi-Fi قطع خواهد شد، در حالی که به <xliff:g id="DEVICE_NAME">%1$s</xliff:g> وصل است"</string>
     <string name="select_character" msgid="3365550120617701745">"درج نویسه"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"ارسال پیامک ها"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; در حال ارسال تعداد زیادی پیامک است. آیا اجازه می‌دهید این برنامه همچنان پیامک ارسال کند؟"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 3711f19..da5c2db 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Kohde:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Kirjoita pyydetty PIN-koodi:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN-koodi:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Puhelimen yhteys wifi-verkkoon katkaistaan väliaikaisesti puhelimen ollessa yhdistettynä laitteeseen <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Puhelimen yhteys wifi-verkkoon katkaistaan väliaikaisesti puhelimen ollessa yhdistettynä laitteeseen <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Lisää merkki"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Tekstiviestien lähettäminen"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; lähettää suuria määriä tekstiviestejä. Annetaanko tämän sovelluksen jatkaa tekstiviestien lähettämistä?"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index dcd6ffd..1bc0fe4 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -200,10 +200,8 @@
     <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Accès direct au microphone pour enregistrer du contenu audio"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Appareil photo"</string>
     <string name="permgroupdesc_camera" msgid="2933667372289567714">"Accès direct à la caméra pour la capture d\'images ou de vidéos"</string>
-    <!-- no translation found for permgrouplab_screenlock (8275500173330718168) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) -->
-    <skip />
+    <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Écran de verrouillage"</string>
+    <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Modifier le comportement de l\'écran de verrouillage sur votre appareil"</string>
     <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Informations relatives à vos applications"</string>
     <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Possibilité de modifier le comportement des autres applications sur votre appareil"</string>
     <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Fond d\'écran"</string>
@@ -906,7 +904,7 @@
     <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Permettre à l\'application titulaire d\'accéder à des fournisseurs de contenu depuis la commande shell. Les applications standards ne devraient jamais avoir recours à cette autorisation."</string>
     <string name="permlab_updateLock" msgid="3527558366616680889">"déconseiller mises à jour auto appareil"</string>
     <string name="permdesc_updateLock" msgid="1655625832166778492">"Permet à l\'application autorisée d\'indiquer au système le moment opportun pour un redémarrage non interactif en vue de la mise à jour de l\'appareil."</string>
-    <string name="save_password_message" msgid="767344687139195790">"Voulez-vous que le navigateur se souvienne de ce mot de passe ?"</string>
+    <string name="save_password_message" msgid="767344687139195790">"Voulez-vous que le navigateur se souvienne de ce mot de passe ?"</string>
     <string name="save_password_notnow" msgid="6389675316706699758">"Pas maintenant"</string>
     <string name="save_password_remember" msgid="6491879678996749466">"Mémoriser"</string>
     <string name="save_password_never" msgid="8274330296785855105">"Jamais"</string>
@@ -1050,10 +1048,8 @@
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Actions sur le texte"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Espace de stockage bientôt saturé"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Il est possible que certaines fonctionnalités du système ne soient pas opérationnelles."</string>
-    <!-- no translation found for app_running_notification_title (4625479411505090209) -->
-    <skip />
-    <!-- no translation found for app_running_notification_text (3368349329989620597) -->
-    <skip />
+    <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> en cours d\'exécution"</string>
+    <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> est en cours d\'exécution."</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
     <string name="cancel" msgid="6442560571259935130">"Annuler"</string>
     <string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1147,7 +1143,8 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"À :"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Saisissez le code PIN requis :"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"Code PIN :"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Le téléphone sera déconnecté du réseau Wi-Fi tant qu\'il sera connecté à l\'appareil <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"La tablette sera déconnectée du réseau Wi-Fi tant qu\'elle sera connectée à l\'appareil \"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>\"."</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Le téléphone sera déconnecté du réseau Wi-Fi tant qu\'il sera connecté à l\'appareil <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
     <string name="select_character" msgid="3365550120617701745">"Insérer un caractère"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Envoi de messages SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; envoie un grand nombre de SMS. Autorisez-vous cette application à poursuivre l\'envoi des messages ?"</string>
@@ -1473,8 +1470,7 @@
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Vous avez dessiné un schéma de déverrouillage incorrect à <xliff:g id="NUMBER_0">%d</xliff:g> reprises. Si vous échouez encore <xliff:g id="NUMBER_1">%d</xliff:g> fois, vous devrez déverrouiller votre téléphone à l\'aide d\'un compte de messagerie électronique."\n\n" Veuillez réessayer dans <xliff:g id="NUMBER_2">%d</xliff:g> secondes."</string>
     <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Supprimer"</string>
-    <!-- no translation found for safe_media_volume_warning (7324161939475478066) -->
-    <skip />
+    <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Augmenter le volume au-dessus du niveau recommandé ?"\n"L\'écoute à un volume élevé pendant des périodes prolongées peut endommager votre audition."</string>
     <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Pour activer l\'accessibilité, appuyez de manière prolongée avec deux doigts."</string>
     <string name="accessibility_enabled" msgid="1381972048564547685">"L\'accessibilité a bien été activée."</string>
     <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Accessibilité annulée."</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index c0bd858..cbec3fe 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"प्रति:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"आवश्‍यक पिन लिखें:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"पिन:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"फ़ोन <xliff:g id="DEVICE_NAME">%1$s</xliff:g> से कनेक्ट रहते समय Wi-Fi से अस्थायी रूप से डिस्कनेक्ट हो जाएगा"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"फ़ोन <xliff:g id="DEVICE_NAME">%1$s</xliff:g> से कनेक्ट रहते समय Wi-Fi से अस्थायी रूप से डिस्कनेक्ट हो जाएगा"</string>
     <string name="select_character" msgid="3365550120617701745">"वर्ण सम्‍मिलित करें"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"SMS संदेश भेज रहा है"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; बड़ी संख्या में SMS संदेश भेज रहा है. क्या आप इस एप्लिकेशन को संदेश भेजना जारी रखने देना चाहते हैं?"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 1564866..c196b59 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -200,10 +200,8 @@
     <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Izravan pristup mikrofonu za snimanje zvuka."</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Fotoaparat"</string>
     <string name="permgroupdesc_camera" msgid="2933667372289567714">"Izravan pristup fotoaparatu za slikanje ili snimanje videozapisa."</string>
-    <!-- no translation found for permgrouplab_screenlock (8275500173330718168) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) -->
-    <skip />
+    <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Zaključan zaslon"</string>
+    <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Mogućnost utjecanja na ponašanje zaključanog zaslona na uređaju."</string>
     <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Informacije o vašoj aplikaciji"</string>
     <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Sposobnost da utječu na postupanje drugih aplikacija na vašem uređaju."</string>
     <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Pozadinska slika"</string>
@@ -1050,10 +1048,8 @@
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Radnje s tekstom"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Ponestaje prostora za pohranu"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Neke sistemske funkcije možda neće raditi"</string>
-    <!-- no translation found for app_running_notification_title (4625479411505090209) -->
-    <skip />
-    <!-- no translation found for app_running_notification_text (3368349329989620597) -->
-    <skip />
+    <string name="app_running_notification_title" msgid="4625479411505090209">"Izvodi se aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="app_running_notification_text" msgid="3368349329989620597">"Trenutačno se izvodi aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="ok" msgid="5970060430562524910">"U redu"</string>
     <string name="cancel" msgid="6442560571259935130">"Odustani"</string>
     <string name="yes" msgid="5362982303337969312">"U redu"</string>
@@ -1147,7 +1143,8 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Prima:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Upišite potreban PIN:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Telefon će se privremeno isključiti s Wi-Fija dok je povezan s uređajem <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Tabletno računalo privremeno će se isključiti s Wi-Fija dok je povezano s uređajem <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Telefon će se privremeno isključiti s Wi-Fija dok je povezan s uređajem <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Umetni znak"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Slanje SMS poruka"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"Aplikacija &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; šalje veliki broj SMS poruka. Želite li dopustiti ovoj aplikaciji da nastavi slati poruke?"</string>
@@ -1473,8 +1470,7 @@
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Netočno ste iscrtali obrazac za otključavanje <xliff:g id="NUMBER_0">%d</xliff:g> puta. Nakon još ovoliko neuspješnih pokušaja: <xliff:g id="NUMBER_1">%d</xliff:g> morat ćete otključati telefon pomoću računa e-pošte."\n\n" Pokušajte ponovo za <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
     <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Ukloni"</string>
-    <!-- no translation found for safe_media_volume_warning (7324161939475478066) -->
-    <skip />
+    <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Želite li pojačati iznad preporučene razine?"\n"Dulje slušanje preglasne glazbe može vam oštetiti sluh."</string>
     <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Nastavite držati s dva prsta kako biste omogućili pristupačnost."</string>
     <string name="accessibility_enabled" msgid="1381972048564547685">"Dostupnost je omogućena."</string>
     <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Pristupačnost otkazana."</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index e7dd28e..ff978a0 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -200,10 +200,8 @@
     <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Közvetlen hozzáférés a mikrofonhoz hangrögzítés céljából"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Fényképezőgép"</string>
     <string name="permgroupdesc_camera" msgid="2933667372289567714">"Közvetlen hozzáférés a fényképezőgéphez kép vagy videó rögzítése céljából"</string>
-    <!-- no translation found for permgrouplab_screenlock (8275500173330718168) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) -->
-    <skip />
+    <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Lezárási képernyő"</string>
+    <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Képes a lezárási képernyő viselkedésének befolyásolására az eszközön."</string>
     <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Az Ön alkalmazásainak információi"</string>
     <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Képes az eszközön a többi alkalmazás viselkedését befolyásolni."</string>
     <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Háttérkép"</string>
@@ -1050,10 +1048,8 @@
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Műveletek szöveggel"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Kevés a szabad terület"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Előfordulhat, hogy néhány rendszerfunkció nem működik."</string>
-    <!-- no translation found for app_running_notification_title (4625479411505090209) -->
-    <skip />
-    <!-- no translation found for app_running_notification_text (3368349329989620597) -->
-    <skip />
+    <string name="app_running_notification_title" msgid="4625479411505090209">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> fut"</string>
+    <string name="app_running_notification_text" msgid="3368349329989620597">"A(z) <xliff:g id="APP_NAME">%1$s</xliff:g> jelenleg fut"</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
     <string name="cancel" msgid="6442560571259935130">"Mégse"</string>
     <string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1147,7 +1143,8 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Címzett:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Adja meg a szükséges PIN kódot:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN kód:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"A telefon ideiglenesen kilép a Wi-Fi hálózatról, míg a(z) <xliff:g id="DEVICE_NAME">%1$s</xliff:g> eszközhöz csatlakozik."</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"A táblagép ideiglenesen lecsatlakozik a Wi-Fi hálózatról, míg a(z) <xliff:g id="DEVICE_NAME">%1$s</xliff:g> eszközhöz csatlakozik"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"A telefon ideiglenesen kilép a Wi-Fi hálózatról, míg a(z) <xliff:g id="DEVICE_NAME">%1$s</xliff:g> eszközhöz csatlakozik."</string>
     <string name="select_character" msgid="3365550120617701745">"Karakter beszúrása"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"SMS-ek küldése"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/ b&gt; nagyszámú SMS üzenetet küld. Engedélyezi, hogy ez az alkalmazás továbbra is üzeneteket küldjön?"</string>
@@ -1473,8 +1470,7 @@
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"<xliff:g id="NUMBER_0">%d</xliff:g> alkalommal helytelenül rajzolta le a feloldási mintát. További <xliff:g id="NUMBER_1">%d</xliff:g> sikertelen kísérlet után egy e-mail fiók használatával kell feloldania a telefonját."\n\n" Kérjük, próbálja újra <xliff:g id="NUMBER_2">%d</xliff:g> másodperc múlva."</string>
     <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Eltávolítás"</string>
-    <!-- no translation found for safe_media_volume_warning (7324161939475478066) -->
-    <skip />
+    <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"A javasolt szint fölé emeli a hangerőt?"\n"Ha hosszú ideig hangosan hallgatja a zenét, az károsíthatja a hallását."</string>
     <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Továbbra is tartsa lenyomva két ujját a hozzáférés engedélyezéséhez."</string>
     <string name="accessibility_enabled" msgid="1381972048564547685">"Hozzáférés engedélyezve"</string>
     <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Hozzáférés megszakítva."</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 630b1a5..fee8fc4 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -200,10 +200,8 @@
     <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Akses langsung ke mikrofon untuk merekam audio."</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string>
     <string name="permgroupdesc_camera" msgid="2933667372289567714">"Akses langsung ke kamera untuk gambar atau tangkapan video."</string>
-    <!-- no translation found for permgrouplab_screenlock (8275500173330718168) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) -->
-    <skip />
+    <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Layar pengunci"</string>
+    <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Kemampuan untuk memengaruhi perilaku layar pengunci di perangkat Anda."</string>
     <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Informasi aplikasi Anda"</string>
     <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Kemampuan untuk memengaruhi perilaku aplikasi lain pada perangkat Anda."</string>
     <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Wallpaper"</string>
@@ -355,7 +353,7 @@
     <string name="permlab_canRequestEnahncedWebAccessibility" msgid="1905232971331801453">"meminta aksesibilitas web yang disempurnakan"</string>
     <string name="permdesc_canRequestEnahncedWebAccessibility" msgid="4500520989321729676">"Memungkinkan pemegang meminta pengaktifan penyempurnaan aksesibilitas web. Contohnya, memasang skrip agar konten aplikasi lebih mudah diakses."</string>
     <string name="permlab_bindTextService" msgid="7358378401915287938">"mengikat ke layanan SMS"</string>
-    <string name="permdesc_bindTextService" msgid="8151968910973998670">"Mengizinkan pemegang mengikat antarmuka tingkat tinggi dari suatu layanan teks (mis. SpellCheckerService). Tidak pernah diperlukan oleh apl normal."</string>
+    <string name="permdesc_bindTextService" msgid="8151968910973998670">"Mengizinkan pemegang mengikat antarmuka tingkat tinggi dari suatu layanan teks (misal: SpellCheckerService). Tidak pernah diperlukan oleh apl normal."</string>
     <string name="permlab_bindVpnService" msgid="4708596021161473255">"mengikat ke layanan VPN"</string>
     <string name="permdesc_bindVpnService" msgid="2067845564581693905">"Mengizinkan pemegang mengikat antarmuka tingkat tinggi dari suatu layanan Vpn. Tidak pernah diperlukan oleh apl normal."</string>
     <string name="permlab_bindWallpaper" msgid="8716400279937856462">"mengikat ke wallpaper"</string>
@@ -1050,10 +1048,8 @@
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Tindakan teks"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Ruang penyimpanan hampir habis"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Beberapa fungsi sistem mungkin tidak dapat bekerja"</string>
-    <!-- no translation found for app_running_notification_title (4625479411505090209) -->
-    <skip />
-    <!-- no translation found for app_running_notification_text (3368349329989620597) -->
-    <skip />
+    <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> berjalan"</string>
+    <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> sedang berjalan"</string>
     <string name="ok" msgid="5970060430562524910">"Oke"</string>
     <string name="cancel" msgid="6442560571259935130">"Batal"</string>
     <string name="yes" msgid="5362982303337969312">"Oke"</string>
@@ -1147,7 +1143,8 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Kepada:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Ketik PIN yang diminta:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Ponsel akan terputus sementara dari Wi-Fi saat tersambung ke <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Sambungan tablet akan diputuskan dari Wi-Fi untuk sementara saat tersambung dengan <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Ponsel akan terputus sementara dari Wi-Fi saat tersambung ke <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Sisipkan huruf"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Mengirim pesan SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; sedang mengirim pesan SMS dalam jumlah besar. Izinkan aplikasi ini untuk melanjutkan pengiriman pesan?"</string>
@@ -1473,8 +1470,7 @@
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Anda telah <xliff:g id="NUMBER_0">%d</xliff:g> kali salah menggambar pola pembuka kunci. Setelah <xliff:g id="NUMBER_1">%d</xliff:g> lagi upaya gagal, Anda akan diminta membuka kunci ponsel menggunakan akun email."\n\n"Coba lagi dalam <xliff:g id="NUMBER_2">%d</xliff:g> detik."</string>
     <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Hapus"</string>
-    <!-- no translation found for safe_media_volume_warning (7324161939475478066) -->
-    <skip />
+    <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Naikkan volume di atas tingkat aman?"\n"Mendengarkan volume tinggi dalam jangka waktu yang lama dapat merusak pendengaran Anda."</string>
     <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Tahan terus dua jari untuk mengaktifkan aksesibilitas."</string>
     <string name="accessibility_enabled" msgid="1381972048564547685">"Aksesibilitas diaktifkan."</string>
     <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Aksesibilitas dibatalkan."</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index fab4740..0cd7322 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -200,10 +200,8 @@
     <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Accesso diretto al microfono per registrare audio."</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Fotocamera"</string>
     <string name="permgroupdesc_camera" msgid="2933667372289567714">"Accesso diretto alla fotocamera per acquisizione di immagini o video."</string>
-    <!-- no translation found for permgrouplab_screenlock (8275500173330718168) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) -->
-    <skip />
+    <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Schermata di blocco"</string>
+    <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Possibilità di influenzare il comportamento della schermata di blocco sul dispositivo."</string>
     <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Informazioni sulle tue applicazioni"</string>
     <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Possibilità di influenzare il comportamento di altre applicazioni sul dispositivo."</string>
     <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Sfondo"</string>
@@ -1050,10 +1048,8 @@
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Azioni testo"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Spazio di archiviazione in esaurimento"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Alcune funzioni di sistema potrebbero non funzionare"</string>
-    <!-- no translation found for app_running_notification_title (4625479411505090209) -->
-    <skip />
-    <!-- no translation found for app_running_notification_text (3368349329989620597) -->
-    <skip />
+    <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> in esecuzione"</string>
+    <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> è attualmente in esecuzione"</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
     <string name="cancel" msgid="6442560571259935130">"Annulla"</string>
     <string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1147,7 +1143,8 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"A:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Inserisci il PIN richiesto:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Il telefono verrà momentaneamente scollegato dalla rete Wi-Fi durante il collegamento a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Il tablet verrà momentaneamente scollegato dalla rete Wi-Fi durante il collegamento a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Il telefono verrà momentaneamente scollegato dalla rete Wi-Fi durante il collegamento a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
     <string name="select_character" msgid="3365550120617701745">"Inserisci carattere"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Invio SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; sta inviando molti SMS. Vuoi consentire all\'applicazione di continuare a inviare messaggi?"</string>
@@ -1473,8 +1470,7 @@
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"<xliff:g id="NUMBER_0">%d</xliff:g> tentativi errati di inserimento della sequenza di sblocco. Dopo altri <xliff:g id="NUMBER_1">%d</xliff:g> tentativi falliti, ti verrà chiesto di sbloccare il telefono con un account email."\n\n" Riprova tra <xliff:g id="NUMBER_2">%d</xliff:g> secondi."</string>
     <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Rimuovi"</string>
-    <!-- no translation found for safe_media_volume_warning (7324161939475478066) -->
-    <skip />
+    <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Aumentare il volume oltre il livello di sicurezza?"\n"Ascoltare musica ad alto volume per lunghi periodi potrebbe danneggiare l\'udito."</string>
     <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Continua a tenere premuto con due dita per attivare l\'accessibilità."</string>
     <string name="accessibility_enabled" msgid="1381972048564547685">"Accessibilità attivata."</string>
     <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Accessibilità annullata."</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 763a105..0139b29 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -200,10 +200,8 @@
     <string name="permgroupdesc_microphone" msgid="7106618286905738408">"גישה ישירה אל המיקרופון להקלטת אודיו."</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"מצלמה"</string>
     <string name="permgroupdesc_camera" msgid="2933667372289567714">"גישה ישירה למצלמה לצילום תמונות או וידאו."</string>
-    <!-- no translation found for permgrouplab_screenlock (8275500173330718168) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) -->
-    <skip />
+    <string name="permgrouplab_screenlock" msgid="8275500173330718168">"מסך נעילה"</string>
+    <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"היכולת להשפיע על אופן ההתנהגות של מסך הנעילה של המכשיר."</string>
     <string name="permgrouplab_appInfo" msgid="8028789762634147725">"מידע על היישומים שלך"</string>
     <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"יכולת להשפיע על התנהגותם של יישומים אחרים במכשיר."</string>
     <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"טפט"</string>
@@ -1050,10 +1048,8 @@
     <string name="editTextMenuTitle" msgid="4909135564941815494">"פעולות טקסט"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"שטח האחסון אוזל"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"ייתכן שפונקציות מערכת מסוימות לא יפעלו"</string>
-    <!-- no translation found for app_running_notification_title (4625479411505090209) -->
-    <skip />
-    <!-- no translation found for app_running_notification_text (3368349329989620597) -->
-    <skip />
+    <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> פועל"</string>
+    <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> פועל כרגע"</string>
     <string name="ok" msgid="5970060430562524910">"אישור"</string>
     <string name="cancel" msgid="6442560571259935130">"ביטול"</string>
     <string name="yes" msgid="5362982303337969312">"אישור"</string>
@@ -1147,7 +1143,8 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"אל:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"הקלד את קוד ה-PIN הנדרש."</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"הטלפון יתנתק מרשת ה-Wi-Fi באופן זמני בשעה שהוא מחובר אל <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"הטאבלט יתנתק מרשת ה-Wi-Fi באופן זמני בשעה שהוא מחובר אל <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"הטלפון יתנתק מרשת ה-Wi-Fi באופן זמני בשעה שהוא מחובר אל <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"הוסף תו"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"שולח הודעות SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt; <xliff:g id="APP_NAME">%1$s</xliff:g> &lt;/ b&gt; שולח מספר רב של הודעות SMS. האם ברצונך לאפשר ליישום זה להמשיך לשלוח הודעות?"</string>
@@ -1473,8 +1470,7 @@
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"שרטטת את קו ביטול הנעילה באופן שגוי <xliff:g id="NUMBER_0">%d</xliff:g> פעמים. לאחר <xliff:g id="NUMBER_1">%d</xliff:g> ניסיונות כושלים נוספים, תתבקש לבטל את נעילת הטלפון באמצעות חשבון דוא\"ל‏."\n\n"נסה שוב בעוד <xliff:g id="NUMBER_2">%d</xliff:g> שניות."</string>
     <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"הסר"</string>
-    <!-- no translation found for safe_media_volume_warning (7324161939475478066) -->
-    <skip />
+    <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"האם להעלות את עוצמת הקול מעל לרמה המומלצת?"\n"האזנה בעוצמת קול גבוהה למשך זמן ארוך עלולה לפגוע בשמיעה."</string>
     <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"המשך לגעת בשתי אצבעות כדי להפעיל נגישות."</string>
     <string name="accessibility_enabled" msgid="1381972048564547685">"נגישות הופעלה."</string>
     <string name="enable_accessibility_canceled" msgid="3833923257966635673">"נגישות בוטלה."</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 83925af..af64842 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -200,10 +200,8 @@
     <string name="permgroupdesc_microphone" msgid="7106618286905738408">"マイクに直接アクセスして音声を記録します。"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"カメラ"</string>
     <string name="permgroupdesc_camera" msgid="2933667372289567714">"カメラに直接アクセスして画像または動画を撮影します。"</string>
-    <!-- no translation found for permgrouplab_screenlock (8275500173330718168) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) -->
-    <skip />
+    <string name="permgrouplab_screenlock" msgid="8275500173330718168">"ロック画面"</string>
+    <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"端末でのロック画面の動作に影響を与えることができます。"</string>
     <string name="permgrouplab_appInfo" msgid="8028789762634147725">"アプリ情報"</string>
     <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"端末上の他のアプリの動作に影響を及ぼします。"</string>
     <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"壁紙"</string>
@@ -1050,10 +1048,8 @@
     <string name="editTextMenuTitle" msgid="4909135564941815494">"テキスト操作"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"空き容量わずか"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"一部のシステム機能が動作しない可能性があります"</string>
-    <!-- no translation found for app_running_notification_title (4625479411505090209) -->
-    <skip />
-    <!-- no translation found for app_running_notification_text (3368349329989620597) -->
-    <skip />
+    <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g>を実行中"</string>
+    <string name="app_running_notification_text" msgid="3368349329989620597">"現在<xliff:g id="APP_NAME">%1$s</xliff:g>を実行しています"</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
     <string name="cancel" msgid="6442560571259935130">"キャンセル"</string>
     <string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1147,7 +1143,8 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"To:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"必要なPINを入力してください:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"携帯端末が<xliff:g id="DEVICE_NAME">%1$s</xliff:g>に接続されている間は一時的にWi-Fi接続が解除されます。"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"タブレットが<xliff:g id="DEVICE_NAME">%1$s</xliff:g>に接続されている間は一時的にWi-Fi接続が切断されます"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"携帯端末が<xliff:g id="DEVICE_NAME">%1$s</xliff:g>に接続されている間は一時的にWi-Fi接続が解除されます。"</string>
     <string name="select_character" msgid="3365550120617701745">"文字を挿入"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"SMSメッセージの送信中"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;が大量のSMSメッセージを送信しています。このアプリにこのままメッセージの送信を許可しますか?"</string>
@@ -1473,8 +1470,7 @@
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"ロック解除パターンの入力を<xliff:g id="NUMBER_0">%d</xliff:g>回間違えました。あと<xliff:g id="NUMBER_1">%d</xliff:g>回間違えると、携帯端末のロック解除にメールアカウントが必要になります。"\n\n"<xliff:g id="NUMBER_2">%d</xliff:g>秒後にもう一度お試しください。"</string>
     <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" - "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"削除"</string>
-    <!-- no translation found for safe_media_volume_warning (7324161939475478066) -->
-    <skip />
+    <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"推奨レベルを超えるまで音量を上げますか?"\n"大音量で長時間聞き続けると、聴力を損なう恐れがあります。"</string>
     <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"ユーザー補助機能を有効にするには2本の指で押し続けてください。"</string>
     <string name="accessibility_enabled" msgid="1381972048564547685">"ユーザー補助が有効になりました。"</string>
     <string name="enable_accessibility_canceled" msgid="3833923257966635673">"ユーザー補助をキャンセルしました。"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index ce657f8..5d663d5 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -200,10 +200,8 @@
     <string name="permgroupdesc_microphone" msgid="7106618286905738408">"오디오를 녹음하기 위해 마이크에 직접 액세스합니다."</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"카메라"</string>
     <string name="permgroupdesc_camera" msgid="2933667372289567714">"이미지 및 동영상을 캡처하기 위해 카메라에 직접 액세스합니다."</string>
-    <!-- no translation found for permgrouplab_screenlock (8275500173330718168) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) -->
-    <skip />
+    <string name="permgrouplab_screenlock" msgid="8275500173330718168">"화면 잠금"</string>
+    <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"기기의 잠금 화면 동작에 영향을 줄 수 있는 기능입니다."</string>
     <string name="permgrouplab_appInfo" msgid="8028789762634147725">"애플리케이션 정보"</string>
     <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"기기의 다른 애플리케이션의 작동에 영향을 줍니다."</string>
     <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"배경화면"</string>
@@ -1050,10 +1048,8 @@
     <string name="editTextMenuTitle" msgid="4909135564941815494">"텍스트 작업"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"저장 공간이 부족함"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"일부 시스템 기능이 작동하지 않을 수 있습니다."</string>
-    <!-- no translation found for app_running_notification_title (4625479411505090209) -->
-    <skip />
-    <!-- no translation found for app_running_notification_text (3368349329989620597) -->
-    <skip />
+    <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> 실행 중"</string>
+    <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g>이(가) 현재 실행 중입니다."</string>
     <string name="ok" msgid="5970060430562524910">"확인"</string>
     <string name="cancel" msgid="6442560571259935130">"취소"</string>
     <string name="yes" msgid="5362982303337969312">"확인"</string>
@@ -1147,7 +1143,8 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"받는사람:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"필수 PIN 입력:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>에 연결되어 있는 동안 일시적으로 휴대전화의 Wi-Fi 연결이 해제됩니다."</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>에 연결되어 있는 동안 일시적으로 태블릿의 Wi-Fi 연결이 해제됩니다."</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g>에 연결되어 있는 동안 일시적으로 휴대전화의 Wi-Fi 연결이 해제됩니다."</string>
     <string name="select_character" msgid="3365550120617701745">"문자 삽입"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"SMS 메시지를 보내는 중"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;이(가) SMS 메시지를 대량으로 보내고 있습니다. 해당 앱이 메시지를 계속 보내도록 하시겠습니까?"</string>
@@ -1473,8 +1470,7 @@
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"잠금해제 패턴을 <xliff:g id="NUMBER_0">%d</xliff:g>회 잘못 그렸습니다. <xliff:g id="NUMBER_1">%d</xliff:g>회 더 실패하면 이메일 계정을 사용하여 휴대전화를 잠금해제해야 합니다."\n\n" <xliff:g id="NUMBER_2">%d</xliff:g>초 후에 다시 시도해 주세요."</string>
     <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"삭제"</string>
-    <!-- no translation found for safe_media_volume_warning (7324161939475478066) -->
-    <skip />
+    <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"안전한 수준 이상으로 볼륨을 높이시겠습니까?"\n"높은 볼륨으로 장시간 청취하면 청력에 손상이 올 수 있습니다."</string>
     <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"두 손가락으로 길게 누르면 접근성을 사용하도록 설정됩니다."</string>
     <string name="accessibility_enabled" msgid="1381972048564547685">"접근성을 사용 설정했습니다."</string>
     <string name="enable_accessibility_canceled" msgid="3833923257966635673">"접근성이 취소되었습니다."</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 9922744..1a48fac 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Skirta:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Įveskite reikiamą PIN kodą:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN kodas:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Telefonas bus laikinai atjungtas nuo „Wi-Fi“, kol bus prijungtas prie „<xliff:g id="DEVICE_NAME">%1$s</xliff:g>“"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Telefonas bus laikinai atjungtas nuo „Wi-Fi“, kol bus prijungtas prie „<xliff:g id="DEVICE_NAME">%1$s</xliff:g>“"</string>
     <string name="select_character" msgid="3365550120617701745">"Įterpti simbolį"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"SMS pranešimų siuntimas"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"Naudojant &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; siunčiama daug SMS pranešimų. Ar norite leisti šiai programai toliau siųsti pranešimus?"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 861f37e..5890f13 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -200,10 +200,8 @@
     <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Tieša piekļuve mikrofonam, lai ierakstītu audio."</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string>
     <string name="permgroupdesc_camera" msgid="2933667372289567714">"Tieša piekļuve kamerai, lai uzņemtu attēlus vai videoklipus."</string>
-    <!-- no translation found for permgrouplab_screenlock (8275500173330718168) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) -->
-    <skip />
+    <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Ekrāna bloķēšana"</string>
+    <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Spēja ietekmēt bloķēšanas ekrāna darbību jūsu ierīcē."</string>
     <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Informācija par jūsu lietojumprogrammām"</string>
     <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Spēja ietekmēt citu ierīcē esošo lietojumprogrammu darbību."</string>
     <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Fona tapete"</string>
@@ -1050,10 +1048,8 @@
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Teksta darbības"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Paliek maz brīvas vietas"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Dažas sistēmas funkcijas var nedarboties."</string>
-    <!-- no translation found for app_running_notification_title (4625479411505090209) -->
-    <skip />
-    <!-- no translation found for app_running_notification_text (3368349329989620597) -->
-    <skip />
+    <string name="app_running_notification_title" msgid="4625479411505090209">"Lietotne <xliff:g id="APP_NAME">%1$s</xliff:g> darbojas"</string>
+    <string name="app_running_notification_text" msgid="3368349329989620597">"Lietotne <xliff:g id="APP_NAME">%1$s</xliff:g> pašlaik darbojas"</string>
     <string name="ok" msgid="5970060430562524910">"Labi"</string>
     <string name="cancel" msgid="6442560571259935130">"Atcelt"</string>
     <string name="yes" msgid="5362982303337969312">"Labi"</string>
@@ -1147,7 +1143,8 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Kam:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Ierakstiet pieprasīto PIN:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Tālrunis tiks īslaicīgi atvienots no Wi-Fi tīkla, kamēr būs izveidots savienojums ar ierīci <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Planšetdators tiks īslaicīgi atvienots no Wi-Fi tīkla, kamēr būs izveidots savienojums ar ierīci <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Tālrunis tiks īslaicīgi atvienots no Wi-Fi tīkla, kamēr būs izveidots savienojums ar ierīci <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
     <string name="select_character" msgid="3365550120617701745">"Ievietojiet rakstzīmi"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Īsziņu sūtīšana"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"Lietotne &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; sūta daudz īsziņu. Vai vēlaties, lai šī lietotne turpinātu sūtīt ziņojumus?"</string>
@@ -1473,8 +1470,7 @@
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Jūs nepareizi norādījāt atbloķēšanas kombināciju <xliff:g id="NUMBER_0">%d</xliff:g> reizes. Pēc vēl <xliff:g id="NUMBER_1">%d</xliff:g> neveiksmīgiem mēģinājumiem tālrunis būs jāatbloķē, izmantojot e-pasta kontu."\n\n"Mēģiniet vēlreiz pēc <xliff:g id="NUMBER_2">%d</xliff:g> sekundēm."</string>
     <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">"  — "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Noņemt"</string>
-    <!-- no translation found for safe_media_volume_warning (7324161939475478066) -->
-    <skip />
+    <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Vai palielināt skaļumu virs ieteicamā līmeņa?"\n"Ilgstoši klausoties skaņu lielā skaļumā, var tikt bojāta dzirde."</string>
     <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Lai iespējotu pieejamību, turiet nospiestus divus pirkstus."</string>
     <string name="accessibility_enabled" msgid="1381972048564547685">"Pieejamības režīms ir iespējots."</string>
     <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Pieejamība ir atcelta."</string>
diff --git a/core/res/res/values-mcc286/config.xml b/core/res/res/values-mcc286/config.xml
old mode 100755
new mode 100644
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index c42697e..be04f9d 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Kepada:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Taipkan PIN yang diperlukan:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Sambungan telefon ke Wi-Fi akan diputuskan buat sementara waktu semasa telefon bersambung ke <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Sambungan telefon ke Wi-Fi akan diputuskan buat sementara waktu semasa telefon bersambung ke <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Masukkan aksara"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Menghantar mesej SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; sedang menghantar banyak mesej SMS. Adakah anda mahu membenarkan apl ini terus menghantar mesej?"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 2d4f9cd..5fd9a42 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -200,10 +200,8 @@
     <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Direkte tilgang til mikrofonen for å ta opp lyd."</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Kameraet"</string>
     <string name="permgroupdesc_camera" msgid="2933667372289567714">"Direkte tilgang til kamera for bilde- eller videoopptak."</string>
-    <!-- no translation found for permgrouplab_screenlock (8275500173330718168) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) -->
-    <skip />
+    <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Låse skjermen"</string>
+    <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Evne til å påvirke atferden til den låste skjermen på enheten din."</string>
     <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Appinformasjonen din"</string>
     <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Ha muligheten til å påvirke andre apper på enheten din."</string>
     <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Bakgrunnen"</string>
@@ -1050,10 +1048,8 @@
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Teksthandlinger"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Lite ledig lagringsplass"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Enkelte systemfunksjoner fungerer muligens ikke slik de skal"</string>
-    <!-- no translation found for app_running_notification_title (4625479411505090209) -->
-    <skip />
-    <!-- no translation found for app_running_notification_text (3368349329989620597) -->
-    <skip />
+    <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> kjører"</string>
+    <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> kjører for øyeblikket"</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
     <string name="cancel" msgid="6442560571259935130">"Avbryt"</string>
     <string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1147,7 +1143,8 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Til:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Skriv inn påkrevd PIN-kode:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Telefonen frakobles Wi-Fi midlertidig mens den er tilkoblet <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Nettbrettet frakobles Wi-Fi midlertidig mens den er tilkoblet <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Telefonen frakobles Wi-Fi midlertidig mens den er tilkoblet <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Sett inn tegn"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Sender SMS-meldinger"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; sender et stort antall SMS. Vil du la appen fortsette å sende ut meldinger?"</string>
@@ -1473,8 +1470,7 @@
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Du har tegnet opplåsningsmønsteret feil <xliff:g id="NUMBER_0">%d</xliff:g> ganger. Etter ytterligere <xliff:g id="NUMBER_1">%d</xliff:g> gale forsøk, blir du bedt om å låse opp telefonen via en e-postkonto."\n\n" Prøv på nytt om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
     <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Fjern"</string>
-    <!-- no translation found for safe_media_volume_warning (7324161939475478066) -->
-    <skip />
+    <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Vil du øke lydnivået over det anbefalte nivået?"\n"Et høyt lydnivå i lengre perioder kan skade hørselen din."</string>
     <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Fortsett å holde nede to fingre for å aktivere tilgjengelighet."</string>
     <string name="accessibility_enabled" msgid="1381972048564547685">"Tilgjengelighet er aktivert."</string>
     <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Tilgjengelighetstjenesten ble avbrutt."</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index 85590ce..f5dd3be 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -200,10 +200,8 @@
     <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Rechtstreeks toegang krijgen tot de microfoon om geluid op te nemen."</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Camera"</string>
     <string name="permgroupdesc_camera" msgid="2933667372289567714">"Rechtstreeks toegang krijgen tot de camera om afbeeldingen of video\'s vast te leggen."</string>
-    <!-- no translation found for permgrouplab_screenlock (8275500173330718168) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) -->
-    <skip />
+    <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Scherm vergrendelen"</string>
+    <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Mogelijkheid om de werking van het vergrendelingsscherm op uw apparaat te beïnvloeden."</string>
     <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Informatie over uw applicaties"</string>
     <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Mogelijkheid om het gedrag van andere applicaties op uw apparaat te beïnvloeden."</string>
     <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Achtergrond"</string>
@@ -1050,10 +1048,8 @@
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Tekstacties"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Opslagruimte is bijna vol"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Bepaalde systeemfuncties werken mogelijk niet"</string>
-    <!-- no translation found for app_running_notification_title (4625479411505090209) -->
-    <skip />
-    <!-- no translation found for app_running_notification_text (3368349329989620597) -->
-    <skip />
+    <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> is actief"</string>
+    <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> is momenteel actief"</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
     <string name="cancel" msgid="6442560571259935130">"Annuleren"</string>
     <string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1147,7 +1143,8 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Naar:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Voer de gewenste pincode in:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"Pincode"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"De verbinding met het wifi-netwerk wordt tijdelijk uitgeschakeld terwijl de telefoon verbonden is met <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"De verbinding met het wifi-netwerk wordt tijdelijk uitgeschakeld terwijl de telefoon is verbonden met <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"De verbinding met het wifi-netwerk wordt tijdelijk uitgeschakeld terwijl de telefoon verbonden is met <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Teken invoegen"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"SMS-berichten verzenden"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; verzendt moment een groot aantal sms-berichten. Wilt u toestaan ​​dat deze app berichten blijft verzenden?"</string>
@@ -1473,8 +1470,7 @@
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"U heeft uw ontgrendelingspatroon <xliff:g id="NUMBER_0">%d</xliff:g> keer onjuist getekend. Na nog eens <xliff:g id="NUMBER_1">%d</xliff:g> mislukte pogingen wordt u gevraagd uw telefoon te ontgrendelen via een e-mailaccount."\n\n" Probeer het over <xliff:g id="NUMBER_2">%d</xliff:g> seconden opnieuw."</string>
     <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Verwijderen"</string>
-    <!-- no translation found for safe_media_volume_warning (7324161939475478066) -->
-    <skip />
+    <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Wilt u het volume verhogen tot boven het aanbevolen geluidsniveau?"\n"Te lang luisteren op een te hoog volume kan leiden tot gehoorbeschadiging."</string>
     <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Blijf het scherm met twee vingers aanraken om toegankelijkheid in te schakelen."</string>
     <string name="accessibility_enabled" msgid="1381972048564547685">"Toegankelijkheid ingeschakeld."</string>
     <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Toegankelijkheid geannuleerd."</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 9bcd359..f6ffb20 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -200,10 +200,8 @@
     <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Bezpośredni dostęp do mikrofonu i nagrywanie dźwięku."</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Aparat"</string>
     <string name="permgroupdesc_camera" msgid="2933667372289567714">"Bezpośredni dostęp do aparatu – robienie zdjęć i nagrywanie filmów."</string>
-    <!-- no translation found for permgrouplab_screenlock (8275500173330718168) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) -->
-    <skip />
+    <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Ekran blokady"</string>
+    <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Możliwość wpływania na zachowanie ekranu blokady urządzenia."</string>
     <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Informacje o aplikacjach"</string>
     <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Możliwość zmiany działania innych aplikacji na urządzeniu."</string>
     <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Tapeta"</string>
@@ -1050,10 +1048,8 @@
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Działania na tekście"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Kończy się miejsce"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Niektóre funkcje systemu mogą nie działać"</string>
-    <!-- no translation found for app_running_notification_title (4625479411505090209) -->
-    <skip />
-    <!-- no translation found for app_running_notification_text (3368349329989620597) -->
-    <skip />
+    <string name="app_running_notification_title" msgid="4625479411505090209">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> uruchomiona"</string>
+    <string name="app_running_notification_text" msgid="3368349329989620597">"Aplikacja <xliff:g id="APP_NAME">%1$s</xliff:g> jest uruchomiona"</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
     <string name="cancel" msgid="6442560571259935130">"Anuluj"</string>
     <string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1147,7 +1143,8 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Do:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Wpisz wymagany kod PIN:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"Kod PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Na czas połączenia z <xliff:g id="DEVICE_NAME">%1$s</xliff:g> telefon zostanie tymczasowo odłączony od Wi-Fi"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Na czas połączenia z <xliff:g id="DEVICE_NAME">%1$s</xliff:g> tablet zostanie tymczasowo odłączony od Wi-Fi"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Na czas połączenia z <xliff:g id="DEVICE_NAME">%1$s</xliff:g> telefon zostanie tymczasowo odłączony od Wi-Fi"</string>
     <string name="select_character" msgid="3365550120617701745">"Wstaw znak"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Wysyłanie wiadomości SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; wysyła wiele SMS-ów. Chcesz pozwolić tej aplikacji dalej wysyłać SMS-y?"</string>
@@ -1473,8 +1470,7 @@
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Po raz <xliff:g id="NUMBER_0">%d</xliff:g> nieprawidłowo narysowałeś wzór odblokowania. Po kolejnych <xliff:g id="NUMBER_1">%d</xliff:g> nieudanych próbach konieczne będzie odblokowanie telefonu przy użyciu danych logowania na konto Google."\n\n" Spróbuj ponownie za <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
     <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Usuń"</string>
-    <!-- no translation found for safe_media_volume_warning (7324161939475478066) -->
-    <skip />
+    <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Chcesz ustawić głośność powyżej bezpiecznego poziomu?"\n"Słuchanie przy dużym poziomie głośności przez dłuższy czas może doprowadzić do uszkodzenia słuchu."</string>
     <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Aby włączyć ułatwienia dostępu, przytrzymaj dwa palce."</string>
     <string name="accessibility_enabled" msgid="1381972048564547685">"Włączono ułatwienia dostępu."</string>
     <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Ułatwienia dostępu zostały anulowane."</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 56a98ed..82a0893 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -200,10 +200,8 @@
     <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Acesso direto ao microfone para gravar áudio."</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Câmara"</string>
     <string name="permgroupdesc_camera" msgid="2933667372289567714">"Acesso direto à câmara para captura de imagens ou vídeos."</string>
-    <!-- no translation found for permgrouplab_screenlock (8275500173330718168) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) -->
-    <skip />
+    <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Bloquear ecrã"</string>
+    <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Capacidade de influenciar o comportamento do ecrã de bloqueio no seu dispositivo."</string>
     <string name="permgrouplab_appInfo" msgid="8028789762634147725">"As informações das suas aplicações"</string>
     <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Capacidade de afetar o comportamento de outras aplicações no seu dispositivo."</string>
     <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Imagem de fundo"</string>
@@ -1050,10 +1048,8 @@
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Acções de texto"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Está quase sem espaço de armazenamento"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Algumas funções do sistema poderão não funcionar"</string>
-    <!-- no translation found for app_running_notification_title (4625479411505090209) -->
-    <skip />
-    <!-- no translation found for app_running_notification_text (3368349329989620597) -->
-    <skip />
+    <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> em execução"</string>
+    <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> está a ser executado"</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
     <string name="cancel" msgid="6442560571259935130">"Cancelar"</string>
     <string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1147,7 +1143,8 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Para:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Introduza o PIN solicitado:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"O telemóvel irá desligar-se temporariamente da rede Wi-Fi enquanto está ligado a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"O tablet sera temporariamente desligado da rede Wi-Fi enquanto estiver ligado a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"O telemóvel irá desligar-se temporariamente da rede Wi-Fi enquanto está ligado a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Introduzir carácter"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"A enviar mensagens SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; está a enviar um grande número de mensagens SMS. Pretende autorizar que a aplicação continue a enviar mensagens?"</string>
@@ -1473,8 +1470,7 @@
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Desenhou a sequência de desbloqueio incorretamente <xliff:g id="NUMBER_0">%d</xliff:g> vezes. Depois de mais <xliff:g id="NUMBER_1">%d</xliff:g> tentativas sem sucesso, ser-lhe-á pedido para desbloquear o telemóvel através de uma conta de email."\n\n" Tente novamente dentro de <xliff:g id="NUMBER_2">%d</xliff:g> segundos."</string>
     <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" - "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Remover"</string>
-    <!-- no translation found for safe_media_volume_warning (7324161939475478066) -->
-    <skip />
+    <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Aumentar o volume acima do nível recomendado?"\n"Ouvir em volume alto durante longos períodos de tempo poderá prejudicar a sua audição."</string>
     <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Mantenha os dois dedos para ativar a acessibilidade."</string>
     <string name="accessibility_enabled" msgid="1381972048564547685">"Acessibilidade ativada."</string>
     <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Acessibilidade cancelada."</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 64e02f0..873c9db 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Para:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Digite o PIN obrigatório:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"O telefone desconectará temporariamente da rede Wi-Fi enquanto estiver conectado a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"O telefone desconectará temporariamente da rede Wi-Fi enquanto estiver conectado a <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Inserir caractere"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Enviando mensagens SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; envia uma grande quantidade de mensagens SMS. Deseja permitir que este aplicativo continue enviando mensagens?"</string>
diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml
index c081d7f..e934a08 100644
--- a/core/res/res/values-rm/strings.xml
+++ b/core/res/res/values-rm/strings.xml
@@ -1785,6 +1785,8 @@
     <skip />
     <!-- no translation found for wifi_p2p_show_pin_message (8530563323880921094) -->
     <skip />
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
     <!-- no translation found for wifi_p2p_frequency_conflict_message (7363907213787469151) -->
     <skip />
     <string name="select_character" msgid="3365550120617701745">"Inserir in caracter"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index f711741..fdeeb93 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -200,10 +200,8 @@
     <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Acces direct la microfon pentru înregistrări audio."</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Camera foto"</string>
     <string name="permgroupdesc_camera" msgid="2933667372289567714">"Acces direct la camera foto pentru a realiza fotografii şi videoclipuri."</string>
-    <!-- no translation found for permgrouplab_screenlock (8275500173330718168) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) -->
-    <skip />
+    <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Blocare ecran"</string>
+    <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Capacitatea de a afecta comportamentul ecranului de blocare pe dispozitivul dvs."</string>
     <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Informaţiile despre aplicaţiile dvs."</string>
     <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Capacitatea de a influenţa comportamentul altor aplicaţii de pe dispozitiv."</string>
     <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Imaginea de fundal"</string>
@@ -1050,10 +1048,8 @@
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Acţiuni pentru text"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Spaţiul de stocare aproape ocupat"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Este posibil ca unele funcţii de sistem să nu funcţioneze"</string>
-    <!-- no translation found for app_running_notification_title (4625479411505090209) -->
-    <skip />
-    <!-- no translation found for app_running_notification_text (3368349329989620597) -->
-    <skip />
+    <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> rulează"</string>
+    <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> rulează acum"</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
     <string name="cancel" msgid="6442560571259935130">"Anulaţi"</string>
     <string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1147,7 +1143,8 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Către:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Introduceţi codul PIN necesar:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"Cod PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Telefonul se va deconecta temporar de la reţeaua Wi-Fi cât timp este conectat la <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Tableta se va deconecta temporar de la rețeaua Wi-Fi cât timp este conectată la <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Telefonul se va deconecta temporar de la reţeaua Wi-Fi cât timp este conectat la <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Introduceţi caracterul"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Se trimit mesaje SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; trimite un număr mare de mesaje SMS. Permiteţi acestei aplicaţii să trimită în continuare mesaje?"</string>
@@ -1473,8 +1470,7 @@
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Aţi desenat incorect modelul pentru deblocare de <xliff:g id="NUMBER_0">%d</xliff:g> ori. După încă <xliff:g id="NUMBER_1">%d</xliff:g> încercări nereuşite, vi se va solicita să deblocaţi telefonul cu ajutorul unui cont de e-mail."\n\n" Încercaţi din nou peste <xliff:g id="NUMBER_2">%d</xliff:g> (de) secunde."</string>
     <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Eliminaţi"</string>
-    <!-- no translation found for safe_media_volume_warning (7324161939475478066) -->
-    <skip />
+    <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Ridicați volumul mai sus de nivelul recomandat?"\n"Ascultarea la volum ridicat pe perioade lungi de timp vă poate afecta auzul."</string>
     <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Menţineţi două degete pe ecran pentru a activa accesibilitatea."</string>
     <string name="accessibility_enabled" msgid="1381972048564547685">"S-a activat accesibilitatea."</string>
     <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Accesibilitatea a fost anulată"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index d230f8a..da1f312 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -200,10 +200,8 @@
     <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Прямой доступ к микрофону для записи звука."</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Камера"</string>
     <string name="permgroupdesc_camera" msgid="2933667372289567714">"Прямой доступ к камере для фото- и видеосъемки."</string>
-    <!-- no translation found for permgrouplab_screenlock (8275500173330718168) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) -->
-    <skip />
+    <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Блокировка экрана"</string>
+    <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Возможность управлять блокировкой экрана на устройстве."</string>
     <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Информация о приложениях"</string>
     <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Возможность влиять на поведение других приложений на устройстве."</string>
     <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Обои"</string>
@@ -1050,10 +1048,8 @@
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Операции с текстом"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Заканчивается свободное место"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Некоторые системные функции могут не работать"</string>
-    <!-- no translation found for app_running_notification_title (4625479411505090209) -->
-    <skip />
-    <!-- no translation found for app_running_notification_text (3368349329989620597) -->
-    <skip />
+    <string name="app_running_notification_title" msgid="4625479411505090209">"Приложение <xliff:g id="APP_NAME">%1$s</xliff:g> выполняется"</string>
+    <string name="app_running_notification_text" msgid="3368349329989620597">"Приложение <xliff:g id="APP_NAME">%1$s</xliff:g> выполняется в данный момент"</string>
     <string name="ok" msgid="5970060430562524910">"ОК"</string>
     <string name="cancel" msgid="6442560571259935130">"Отмена"</string>
     <string name="yes" msgid="5362982303337969312">"ОК"</string>
@@ -1147,7 +1143,8 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Кому:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Введите PIN-код:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN-код:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"При подключении к устройству <xliff:g id="DEVICE_NAME">%1$s</xliff:g> телефон будет временно отключаться от сети Wi-Fi"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"При подключении к устройству <xliff:g id="DEVICE_NAME">%1$s</xliff:g> планшетный ПК будет временно отключаться от сети Wi-Fi"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"При подключении к устройству <xliff:g id="DEVICE_NAME">%1$s</xliff:g> телефон будет временно отключаться от сети Wi-Fi"</string>
     <string name="select_character" msgid="3365550120617701745">"Введите символ"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Отправка SMS-сообщений"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; отправляет большое количество SMS. Разрешить приложению и дальше отправлять сообщения?"</string>
@@ -1473,8 +1470,7 @@
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Вы <xliff:g id="NUMBER_0">%d</xliff:g> раз неверно указали графический ключ. После <xliff:g id="NUMBER_1">%d</xliff:g> неверных попыток для разблокировки телефона потребуется войти в аккаунт Google."\n\n"Повтор через <xliff:g id="NUMBER_2">%d</xliff:g> сек."</string>
     <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Удалить"</string>
-    <!-- no translation found for safe_media_volume_warning (7324161939475478066) -->
-    <skip />
+    <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Увеличить громкость до небезопасного уровня?"\n"Долговременное прослушивание на такой громкости может повредить слух."</string>
     <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Чтобы включить специальные возможности, удерживайте пальцы на экране."</string>
     <string name="accessibility_enabled" msgid="1381972048564547685">"Специальные возможности включены."</string>
     <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Специальные возможности не будут включены."</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index f37ebf0..94fdd7b 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -200,10 +200,8 @@
     <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Priamy prístup k mikrofónu na záznam zvuku."</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Fotoaparát"</string>
     <string name="permgroupdesc_camera" msgid="2933667372289567714">"Priamy prístup k fotoaparátu na nasnímanie fotografií alebo natočenie videí."</string>
-    <!-- no translation found for permgrouplab_screenlock (8275500173330718168) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) -->
-    <skip />
+    <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Uzamknúť obrazovku"</string>
+    <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Možnosť ovplyvniť správanie zámky obrazovky na vašom zariadení."</string>
     <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Informácie o vašich aplikáciách"</string>
     <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Schopnosť ovplyvniť správanie ďalších aplikácií na vašom zariadení."</string>
     <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Tapeta"</string>
@@ -1050,10 +1048,8 @@
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Operácie s textom"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Nedostatok ukladacieho priestoru"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Niektoré systémové funkcie nemusia fungovať"</string>
-    <!-- no translation found for app_running_notification_title (4625479411505090209) -->
-    <skip />
-    <!-- no translation found for app_running_notification_text (3368349329989620597) -->
-    <skip />
+    <string name="app_running_notification_title" msgid="4625479411505090209">"Aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> je spustená"</string>
+    <string name="app_running_notification_text" msgid="3368349329989620597">"Aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> je práve spustená"</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
     <string name="cancel" msgid="6442560571259935130">"Zrušiť"</string>
     <string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1147,7 +1143,8 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Komu:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Zadajte požadovaný kód PIN:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Telefón bude počas pripojenia k zariadeniu <xliff:g id="DEVICE_NAME">%1$s</xliff:g> od siete Wi-Fi dočasne odpojený."</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Tablet bude počas pripojenia k zariadeniu <xliff:g id="DEVICE_NAME">%1$s</xliff:g> od siete Wi-Fi dočasne odpojený."</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Telefón bude počas pripojenia k zariadeniu <xliff:g id="DEVICE_NAME">%1$s</xliff:g> od siete Wi-Fi dočasne odpojený."</string>
     <string name="select_character" msgid="3365550120617701745">"Vkladanie znakov"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Odosielanie správ SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"Aplikácia &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; posiela veľký počet správ SMS. Chcete tejto aplikácií povoliť, aby aj naďalej posielala správy?"</string>
@@ -1473,8 +1470,7 @@
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"<xliff:g id="NUMBER_0">%d</xliff:g>-krát ste nesprávne nakreslili svoj bezpečnostný vzor. Po <xliff:g id="NUMBER_1">%d</xliff:g> ďalších neúspešných pokusoch sa zobrazí výzva na odomknutie telefónu pomocou e-mailového účtu."\n\n" Skúste to znova o <xliff:g id="NUMBER_2">%d</xliff:g> s."</string>
     <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Odstrániť"</string>
-    <!-- no translation found for safe_media_volume_warning (7324161939475478066) -->
-    <skip />
+    <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Zvýšiť hlasitosť nad odporúčanú úroveň?"\n"Dlhodobé počúvanie pri vysokej hlasitosti môže poškodiť váš sluch."</string>
     <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Zjednodušenie ovládania povolíte dlhým stlačením dvoma prstami."</string>
     <string name="accessibility_enabled" msgid="1381972048564547685">"Zjednodušenie ovládania je povolené."</string>
     <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Zjednodušenie ovládania bolo zrušené."</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index d865c7c..1486341 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Za:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Vnesite zahtevano kodo PIN:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Telefon bo začasno prekinil povezavo z Wi-Fi-jem, ko je povezan z napravo <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Telefon bo začasno prekinil povezavo z Wi-Fi-jem, ko je povezan z napravo <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Vstavljanje znaka"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Pošiljanje sporočil SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; pošilja veliko SMS-ov. Ali želite dovoliti, da jih še naprej pošilja?"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 8bc5879..1ec58d4 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -200,10 +200,8 @@
     <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Директан приступ микрофону за снимање звука."</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Камера"</string>
     <string name="permgroupdesc_camera" msgid="2933667372289567714">"Директан приступ камери за снимање слика или видео снимака."</string>
-    <!-- no translation found for permgrouplab_screenlock (8275500173330718168) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) -->
-    <skip />
+    <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Закључавање екрана"</string>
+    <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Могућност да утиче на понашање закључаног екрана на уређају."</string>
     <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Информације о апликацијама"</string>
     <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Способност да се утиче на понашање других апликација на уређају."</string>
     <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Позадина"</string>
@@ -1050,10 +1048,8 @@
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Радње у вези са текстом"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Простор за складиштење је на измаку"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Неке системске функције можда не функционишу"</string>
-    <!-- no translation found for app_running_notification_title (4625479411505090209) -->
-    <skip />
-    <!-- no translation found for app_running_notification_text (3368349329989620597) -->
-    <skip />
+    <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> је покренута"</string>
+    <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> је тренутно покренута"</string>
     <string name="ok" msgid="5970060430562524910">"Потврди"</string>
     <string name="cancel" msgid="6442560571259935130">"Откажи"</string>
     <string name="yes" msgid="5362982303337969312">"Потврди"</string>
@@ -1147,7 +1143,8 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Коме:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Унесите потребни PIN:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Телефон ће привремено прекинути везу са Wi-Fi-јем док је повезан са уређајем <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Таблет ће привремено прекинути везу са Wi-Fi-јем док је повезан са уређајем <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Телефон ће привремено прекинути везу са Wi-Fi-јем док је повезан са уређајем <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Уметање знака"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Слање SMS порука"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; шаље велики број SMS порука. Желите ли да дозволите овој апликацији да настави са слањем порука?"</string>
@@ -1473,8 +1470,7 @@
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Нацртали сте шаблон за откључавање неисправно <xliff:g id="NUMBER_0">%d</xliff:g> пута. После још <xliff:g id="NUMBER_1">%d</xliff:g> неуспешна(их) покушаја, од вас ће бити затражено да откључате телефон помоћу налога е-поште."\n\n"Покушајте поново за <xliff:g id="NUMBER_2">%d</xliff:g> секунде(и)."</string>
     <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Уклони"</string>
-    <!-- no translation found for safe_media_volume_warning (7324161939475478066) -->
-    <skip />
+    <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Желите ли да појачате звук преко препорученог нивоа?"\n"Ако слушате гласну музику током дужег периода, може да дође до оштећења слуха."</string>
     <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Држите са два прста да бисте омогућили приступачност."</string>
     <string name="accessibility_enabled" msgid="1381972048564547685">"Приступачност је омогућена."</string>
     <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Приступачност је отказана."</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 46f2d6e..6a51baf 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -200,10 +200,8 @@
     <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Direktåtkomst till mikrofonen för att spela in ljud."</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string>
     <string name="permgroupdesc_camera" msgid="2933667372289567714">"Direktåtkomst till kamera för att ta bilder eller spela in video."</string>
-    <!-- no translation found for permgrouplab_screenlock (8275500173330718168) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) -->
-    <skip />
+    <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Låsa skärmen"</string>
+    <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Möjlighet att påverka funktionen för enhetens låsskärm."</string>
     <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Information i dina appar"</string>
     <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Kan påverka beteendet hos andra appar på enheten."</string>
     <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Bakgrund"</string>
@@ -1050,10 +1048,8 @@
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Textåtgärder"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Lagringsutrymmet börjar ta slut"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Det kan hända att vissa systemfunktioner inte fungerar"</string>
-    <!-- no translation found for app_running_notification_title (4625479411505090209) -->
-    <skip />
-    <!-- no translation found for app_running_notification_text (3368349329989620597) -->
-    <skip />
+    <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> körs"</string>
+    <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> körs"</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
     <string name="cancel" msgid="6442560571259935130">"Avbryt"</string>
     <string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1147,7 +1143,8 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Till:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Ange den obligatoriska PIN-koden:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN-kod:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Mobilen kommer tillfälligt att kopplas från Wi-Fi när den är ansluten till <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Surfplattans Wi-Fi-anslutning kommer tillfälligt att avbrytas när den är ansluten till <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Mobilen kommer tillfälligt att kopplas från Wi-Fi när den är ansluten till <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Infoga tecken"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Skickar SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; skickar ett stort antal SMS. Vill du tillåta att appen fortsätter att skicka meddelanden?"</string>
@@ -1473,8 +1470,7 @@
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Du har ritat ditt grafiska lösenord fel <xliff:g id="NUMBER_0">%d</xliff:g> gånger. Efter ytterligare <xliff:g id="NUMBER_1">%d</xliff:g> försök ombeds du låsa upp mobilen med hjälp av ett e-postkonto."\n\n" Försök igen om <xliff:g id="NUMBER_2">%d</xliff:g> sekunder."</string>
     <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Ta bort"</string>
-    <!-- no translation found for safe_media_volume_warning (7324161939475478066) -->
-    <skip />
+    <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Vill du höja volymen över den rekommenderade nivån?"\n"Om du lyssnar på hög volym under långa perioder kan din hörsel skadas."</string>
     <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Fortsätt trycka med två fingrar om du vill aktivera tillgänglighetsläget."</string>
     <string name="accessibility_enabled" msgid="1381972048564547685">"Tillgänglighetsläget har aktiverats."</string>
     <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Byte till tillgänglighetsläge avbrutet."</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 58e98a3..d1e4270 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -641,7 +641,7 @@
     <string name="policydesc_expirePassword" msgid="1729725226314691591">"Dhibiti ni mara ngapi nenosiri la kufunga skrini linafaa libadilishwe."</string>
     <string name="policylab_encryptedStorage" msgid="8901326199909132915">"Weka msimbo fiche wa hifadhi"</string>
     <string name="policydesc_encryptedStorage" msgid="2637732115325316992">"Inahitaji kwamba data ya programu iliyohifadhiwa iwe na msimbo fiche."</string>
-    <string name="policylab_disableCamera" msgid="6395301023152297826">"Lemaza kamera"</string>
+    <string name="policylab_disableCamera" msgid="6395301023152297826">"Zima kamera"</string>
     <string name="policydesc_disableCamera" msgid="2306349042834754597">"Zuia matumizi yote ya kamera za kifaa."</string>
     <string name="policylab_disableKeyguardFeatures" msgid="266329104542638802">"Lemaza vipengele kwenye kilinzi cha kitufe."</string>
     <string name="policydesc_disableKeyguardFeatures" msgid="3467082272186534614">"Inazuia matumizi ya baadhi ya vipengele kwenye kilinzi cha kitufe."</string>
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Kwa:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Charaza PIN inayohitajika:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Simu itaukata muunganisho kwa muda kutoka kwenye Wi-Fi inapokuwa imeunganishwa kwenye <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Simu itaukata muunganisho kwa muda kutoka kwenye Wi-Fi inapokuwa imeunganishwa kwenye <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Ingiza kibambo"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Inatuma ujumbe wa SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; inatuma idadi kubwa ya SMS. Je, unataka kuruhusu programu hii kuendelea kutuma SMS?"</string>
@@ -1437,7 +1439,7 @@
     <string name="wifi_display_notification_disconnect" msgid="6183754463561153372">"Tenganisha"</string>
     <string name="kg_emergency_call_label" msgid="684946192523830531">"Simu ya dharura"</string>
     <string name="kg_forgot_pattern_button_text" msgid="8852021467868220608">"Umesahau Ruwaza"</string>
-    <string name="kg_wrong_pattern" msgid="1850806070801358830">"Ruwaza Isiyo sahihi"</string>
+    <string name="kg_wrong_pattern" msgid="1850806070801358830">"Mchoro Usio sahihi"</string>
     <string name="kg_wrong_password" msgid="2333281762128113157">"Nenosiri Lisilo sahihi"</string>
     <string name="kg_wrong_pin" msgid="1131306510833563801">"PIN isiyo sahihi"</string>
     <string name="kg_too_many_failed_attempts_countdown" msgid="6358110221603297548">"Jaribu tena baada ya sekunde <xliff:g id="NUMBER">%d</xliff:g>."</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 9055408..4bca1b1 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"ถึง:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"พิมพ์ PIN ที่ต้องการ:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"โทรศัพท์จะยกเลิกการเชื่อมต่อกับ Wi-Fi ชั่วคราวในขณะที่เชื่อมต่อกับ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"โทรศัพท์จะยกเลิกการเชื่อมต่อกับ Wi-Fi ชั่วคราวในขณะที่เชื่อมต่อกับ <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"ใส่อักขระ"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"กำลังส่งข้อความ SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; กำลังส่งข้อความ SMS จำนวนมาก คุณต้องการอนุญาตให้แอปพลิเคชันนี้ส่งข้อความต่อหรือไม่"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 59a91c2..5354a54 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -200,10 +200,8 @@
     <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Direktang access sa mikropono upang mag-record ng audio."</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Camera"</string>
     <string name="permgroupdesc_camera" msgid="2933667372289567714">"Direktang access sa camera para sa pagkuha ng larawan o video."</string>
-    <!-- no translation found for permgrouplab_screenlock (8275500173330718168) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) -->
-    <skip />
+    <string name="permgrouplab_screenlock" msgid="8275500173330718168">"I-lock ang screen"</string>
+    <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Kakayahang maapektuhan ang pagkilos ng lock screen sa iyong device."</string>
     <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Ang impormasyon ng iyong mga application"</string>
     <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Kakayahang maapektuhan ang pag-uugali ng iba pang mga application sa iyong device."</string>
     <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Wallpaper"</string>
@@ -1050,10 +1048,8 @@
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Pagkilos ng teksto"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Nauubusan na ang puwang ng storage"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Maaaring hindi gumana nang tama ang ilang paggana ng system"</string>
-    <!-- no translation found for app_running_notification_title (4625479411505090209) -->
-    <skip />
-    <!-- no translation found for app_running_notification_text (3368349329989620597) -->
-    <skip />
+    <string name="app_running_notification_title" msgid="4625479411505090209">"Tumatakbo ang <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
+    <string name="app_running_notification_text" msgid="3368349329989620597">"Kasalukuyang tumatakbo ang <xliff:g id="APP_NAME">%1$s</xliff:g>"</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
     <string name="cancel" msgid="6442560571259935130">"Kanselahin"</string>
     <string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1147,7 +1143,8 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Kay:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"I-type ang kinakailangang PIN:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Pansamantalang madidiskoneta ang telepono sa Wi-Fi habang nakakonekta ito sa <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Pansamantalang madidiskoneta ang tablet sa Wi-Fi habang nakakonekta ito sa <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Pansamantalang madidiskoneta ang telepono sa Wi-Fi habang nakakonekta ito sa <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Magpasok ng character"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Nagpapadala ng mga SMS na mensahe"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"Ang &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ay nagpapadala ng maraming mensaheng SMS. Gusto mo bang payagan ang app na ito na magpatuloy sa pagpapadala ng mga mensahe?"</string>
@@ -1473,8 +1470,7 @@
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Naguhit mo nang hindi tama ang iyong pattern sa pag-unlock nang <xliff:g id="NUMBER_0">%d</xliff:g> (na) beses. Pagkatapos ng <xliff:g id="NUMBER_1">%d</xliff:g> pang hindi matagumpay na pagtatangka, hihilingin sa iyong i-unlock ang telepono mo gamit ang isang email account."\n\n" Subukang muli sa loob ng <xliff:g id="NUMBER_2">%d</xliff:g> (na) segundo."</string>
     <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Alisin"</string>
-    <!-- no translation found for safe_media_volume_warning (7324161939475478066) -->
-    <skip />
+    <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Lakasan ang volume nang lagpas sa ligtas na antas?"\n"Maaaring mapinsala ng pakikinig sa malakas na volume sa loob ng mahahabang panahon ang iyong pandinig."</string>
     <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Panatilihing nakapindot nang matagal ang iyong dalawang daliri upang paganahin ang pagiging naa-access."</string>
     <string name="accessibility_enabled" msgid="1381972048564547685">"Pinagana ang accessibility."</string>
     <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Nakansela ang pagiging naa-access."</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index b95998f..c5ce4cd 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -200,10 +200,8 @@
     <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Ses kaydetmek için mikrofona doğrudan erişim."</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Kamera"</string>
     <string name="permgroupdesc_camera" msgid="2933667372289567714">"Resim ve video kaydı için kameraya doğrudan erişim."</string>
-    <!-- no translation found for permgrouplab_screenlock (8275500173330718168) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) -->
-    <skip />
+    <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Kilit ekranı"</string>
+    <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Cihazınızdaki kilit ekranının çalışma biçimini etkileyebilme özelliği."</string>
     <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Uygulama bilgileriniz"</string>
     <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Cihazınızdaki diğer uygulamaların davranışlarını etkileyebilme."</string>
     <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Duvar Kağıdı"</string>
@@ -1050,10 +1048,8 @@
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Metin eylemleri"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Depolama alanı bitiyor"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Bazı sistem işlevleri çalışmayabilir"</string>
-    <!-- no translation found for app_running_notification_title (4625479411505090209) -->
-    <skip />
-    <!-- no translation found for app_running_notification_text (3368349329989620597) -->
-    <skip />
+    <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> çalışıyor"</string>
+    <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> şu anda çalışıyor"</string>
     <string name="ok" msgid="5970060430562524910">"Tamam"</string>
     <string name="cancel" msgid="6442560571259935130">"İptal"</string>
     <string name="yes" msgid="5362982303337969312">"Tamam"</string>
@@ -1147,7 +1143,8 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Alıcı:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Gerekli PIN\'i yazın:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Telefon <xliff:g id="DEVICE_NAME">%1$s</xliff:g> adlı cihaza bağlıyken Kablosuz ağ bağlantısı geçici olarak kesilecektir"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Tablet <xliff:g id="DEVICE_NAME">%1$s</xliff:g> adlı cihaza bağlıyken Kablosuz ağ bağlantısı geçici olarak kesilecektir"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Telefon <xliff:g id="DEVICE_NAME">%1$s</xliff:g> adlı cihaza bağlıyken Kablosuz ağ bağlantısı geçici olarak kesilecektir"</string>
     <string name="select_character" msgid="3365550120617701745">"Karakter ekle"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"SMS mesajları gönderiliyor"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; çok sayıda SMS mesajı gönderiyor. Bu uygulamanın mesaj göndermeye devam etmesine izin veriyor musunuz?"</string>
@@ -1473,8 +1470,7 @@
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Kilit açma deseninizi <xliff:g id="NUMBER_0">%d</xliff:g> kez yanlış çizdiniz. <xliff:g id="NUMBER_1">%d</xliff:g> başarısız denemeden sonra telefonunuzu bir e-posta hesabı kullanarak açmanız istenir."\n\n" <xliff:g id="NUMBER_2">%d</xliff:g> saniye içinde tekrar deneyin."</string>
     <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Kaldır"</string>
-    <!-- no translation found for safe_media_volume_warning (7324161939475478066) -->
-    <skip />
+    <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Ses düzeyi önerilen seviyenin üzerine çıkarılsın mı?"\n"Uzun süre yüksek sesle dinlemek işitme duyunuza zarar verebilir."</string>
     <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Erişilebilirliği etkinleştirmek için iki parmağınızı basılı tutmaya devam edin."</string>
     <string name="accessibility_enabled" msgid="1381972048564547685">"Erişilebilirlik etkinleştirildi."</string>
     <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Erişilebilirlik iptal edildi."</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 994e8a1..7c8f192 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -200,10 +200,8 @@
     <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Безпосередній доступ до мікрофона для запису звуку."</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Камера"</string>
     <string name="permgroupdesc_camera" msgid="2933667372289567714">"Безпосередній доступ до камери для здійснення фото- чи відеозйомки."</string>
-    <!-- no translation found for permgrouplab_screenlock (8275500173330718168) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) -->
-    <skip />
+    <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Екран блокування"</string>
+    <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Можливість впливати на поведінку екрана блокування вашого пристрою."</string>
     <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Інформація про програми"</string>
     <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Здатність впливати на роботу інших програм на пристрої."</string>
     <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Фоновий малюнок"</string>
@@ -1050,10 +1048,8 @@
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Дії з текстом"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Закінчується пам’ять"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Деякі системні функції можуть не працювати"</string>
-    <!-- no translation found for app_running_notification_title (4625479411505090209) -->
-    <skip />
-    <!-- no translation found for app_running_notification_text (3368349329989620597) -->
-    <skip />
+    <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> працює"</string>
+    <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> зараз працює"</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
     <string name="cancel" msgid="6442560571259935130">"Скасувати"</string>
     <string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1147,7 +1143,8 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Кому:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Введіть потрібний PIN-код:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN-код:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Під час з’єднання з пристроєм <xliff:g id="DEVICE_NAME">%1$s</xliff:g> телефон тимчасово від’єднається від мережі Wi-Fi"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Під час з’єднання з пристроєм <xliff:g id="DEVICE_NAME">%1$s</xliff:g> планшетний ПК тимчасово від’єднається від мережі Wi-Fi"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Під час з’єднання з пристроєм <xliff:g id="DEVICE_NAME">%1$s</xliff:g> телефон тимчасово від’єднається від мережі Wi-Fi"</string>
     <string name="select_character" msgid="3365550120617701745">"Вставл-ня символу"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Надсил. SMS повідомлень"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"Програма &lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; надсилає велику кількість SMS-повідомлень. Дозволити цій програмі й надалі надсилати повідомлення?"</string>
@@ -1473,8 +1470,7 @@
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Ключ розблокування неправильно намальовано стільки разів: <xliff:g id="NUMBER_0">%d</xliff:g>. У вас є ще стільки спроб: <xliff:g id="NUMBER_1">%d</xliff:g>. У разі невдачі з’явиться запит розблокувати телефон за допомогою облікового запису електронної пошти."\n\n" Повторіть спробу через <xliff:g id="NUMBER_2">%d</xliff:g> сек."</string>
     <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" – "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Вилучити"</string>
-    <!-- no translation found for safe_media_volume_warning (7324161939475478066) -->
-    <skip />
+    <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Збільшити гучність понад рекомендований рівень?"\n"Якщо слухати надто гучну музику тривалий час, можна пошкодити слух."</string>
     <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Утримуйте двома пальцями, щоб увімкнути доступність."</string>
     <string name="accessibility_enabled" msgid="1381972048564547685">"Доступність увімкнено."</string>
     <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Доступність скасовано."</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 0e61026..e9ccd36 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -200,10 +200,8 @@
     <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Truy cập trực tiếp vào micrô để ghi âm."</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Máy ảnh"</string>
     <string name="permgroupdesc_camera" msgid="2933667372289567714">"Truy cập trực tiếp vào máy ảnh để chụp ảnh hoặc quay video."</string>
-    <!-- no translation found for permgrouplab_screenlock (8275500173330718168) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) -->
-    <skip />
+    <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Khóa màn hình"</string>
+    <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Khả năng ảnh hưởng tới trạng thái màn hình khóa trên thiết bị của bạn."</string>
     <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Thông tin về các ứng dụng của bạn"</string>
     <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Khả năng ảnh hưởng tới hoạt động của các ứng dụng khác trên thiết bị của bạn."</string>
     <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Hình nền"</string>
@@ -1050,10 +1048,8 @@
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Tác vụ văn bản"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Sắp hết dung lượng lưu trữ"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Một số chức năng hệ thống có thể không hoạt động"</string>
-    <!-- no translation found for app_running_notification_title (4625479411505090209) -->
-    <skip />
-    <!-- no translation found for app_running_notification_text (3368349329989620597) -->
-    <skip />
+    <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g> đang chạy"</string>
+    <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g> hiện đang chạy"</string>
     <string name="ok" msgid="5970060430562524910">"OK"</string>
     <string name="cancel" msgid="6442560571259935130">"Hủy"</string>
     <string name="yes" msgid="5362982303337969312">"OK"</string>
@@ -1147,7 +1143,8 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Người nhận:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Nhập PIN bắt buộc:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"Mã PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Điện thoại sẽ tạm thời ngắt kết nối khỏi Wi-Fi trong khi điện thoại được kết nối với <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Máy tính bảng sẽ tạm thời ngắt kết nối khỏi Wi-Fi trong khi máy tính bảng được kết nối với <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Điện thoại sẽ tạm thời ngắt kết nối khỏi Wi-Fi trong khi điện thoại được kết nối với <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Chèn ký tự"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Đang gửi tin nhắn SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; đang gửi rất nhiều tin nhắn SMS. Bạn có muốn cho phép ứng dụng này tiếp tục gửi tin nhắn không?"</string>
@@ -1473,8 +1470,7 @@
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Bạn đã <xliff:g id="NUMBER_0">%d</xliff:g> lần vẽ không chính xác hình mở khóa của mình. Sau <xliff:g id="NUMBER_1">%d</xliff:g> lần thử không thành công nữa, bạn sẽ được yêu cầu mở khóa điện thoại bằng tài khoản email."\n\n" Vui lòng thử lại sau <xliff:g id="NUMBER_2">%d</xliff:g> giây."</string>
     <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Xóa"</string>
-    <!-- no translation found for safe_media_volume_warning (7324161939475478066) -->
-    <skip />
+    <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Tăng âm lượng trên mức được đề xuất?"\n"Nghe ở âm lượng cao trong thời gian dài có thể gây hại cho thính giác của bạn."</string>
     <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Tiếp tục giữ hai ngón tay để bật trợ năng."</string>
     <string name="accessibility_enabled" msgid="1381972048564547685">"Trợ năng đã được bật."</string>
     <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Đã hủy trợ năng."</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index ec33b27..c768ec8 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -200,10 +200,8 @@
     <string name="permgroupdesc_microphone" msgid="7106618286905738408">"直接使用麦克风以录制音频。"</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"相机"</string>
     <string name="permgroupdesc_camera" msgid="2933667372289567714">"直接使用相机以拍摄图片或视频。"</string>
-    <!-- no translation found for permgrouplab_screenlock (8275500173330718168) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) -->
-    <skip />
+    <string name="permgrouplab_screenlock" msgid="8275500173330718168">"锁定屏幕"</string>
+    <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"能够影响设备的锁定屏幕的行为。"</string>
     <string name="permgrouplab_appInfo" msgid="8028789762634147725">"您的应用信息"</string>
     <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"能够影响设备上其他应用的行为。"</string>
     <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"壁纸"</string>
@@ -1050,10 +1048,8 @@
     <string name="editTextMenuTitle" msgid="4909135564941815494">"文字操作"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"存储空间不足"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"某些系统功能可能无法正常使用"</string>
-    <!-- no translation found for app_running_notification_title (4625479411505090209) -->
-    <skip />
-    <!-- no translation found for app_running_notification_text (3368349329989620597) -->
-    <skip />
+    <string name="app_running_notification_title" msgid="4625479411505090209">"<xliff:g id="APP_NAME">%1$s</xliff:g>正在运行"</string>
+    <string name="app_running_notification_text" msgid="3368349329989620597">"<xliff:g id="APP_NAME">%1$s</xliff:g>目前正在运行"</string>
     <string name="ok" msgid="5970060430562524910">"确定"</string>
     <string name="cancel" msgid="6442560571259935130">"取消"</string>
     <string name="yes" msgid="5362982303337969312">"确定"</string>
@@ -1147,7 +1143,8 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"收件人:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"键入所需的 PIN:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"手机连接到<xliff:g id="DEVICE_NAME">%1$s</xliff:g>时会暂时断开与 Wi-Fi 的连接。"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"平板电脑连接到<xliff:g id="DEVICE_NAME">%1$s</xliff:g>时会暂时断开与 Wi-Fi 的连接"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"手机连接到<xliff:g id="DEVICE_NAME">%1$s</xliff:g>时会暂时断开与 Wi-Fi 的连接。"</string>
     <string name="select_character" msgid="3365550120617701745">"插入字符"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"正在发送短信"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt;在发送大量短信。是否允许该应用继续发送短信?"</string>
@@ -1473,8 +1470,7 @@
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"您已经 <xliff:g id="NUMBER_0">%d</xliff:g> 次错误地绘制了解锁图案。如果再尝试 <xliff:g id="NUMBER_1">%d</xliff:g> 次后仍不成功,系统就会要求您使用自己的电子邮件帐户解锁手机。"\n\n"请在 <xliff:g id="NUMBER_2">%d</xliff:g> 秒后重试。"</string>
     <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"删除"</string>
-    <!-- no translation found for safe_media_volume_warning (7324161939475478066) -->
-    <skip />
+    <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"将音量调高到推荐级别以上?"\n"长时间使用高音量可能会损伤听力。"</string>
     <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"持续按住双指即可启用辅助功能。"</string>
     <string name="accessibility_enabled" msgid="1381972048564547685">"辅助功能已启用。"</string>
     <string name="enable_accessibility_canceled" msgid="3833923257966635673">"已取消辅助功能。"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 905cdfb..5cb2f9d 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -510,7 +510,7 @@
     <string name="permlab_locationUpdates" msgid="7785408253364335740">"控制位置更新通知"</string>
     <string name="permdesc_locationUpdates" msgid="1120741557891438876">"允許應用程式啟用/停用來自無線電的位置更新通知 (不建議一般應用程式使用)。"</string>
     <string name="permlab_checkinProperties" msgid="7855259461268734914">"存取登機選項"</string>
-    <string name="permdesc_checkinProperties" msgid="4024526968630194128">"允許讀取/寫入由簽入服務上載的內容 (不建議一般應用程式使用)。"</string>
+    <string name="permdesc_checkinProperties" msgid="4024526968630194128">"允許讀取/寫入由簽入服務上傳的內容 (不建議一般應用程式使用)。"</string>
     <string name="permlab_bindGadget" msgid="776905339015863471">"選擇小工具"</string>
     <string name="permdesc_bindGadget" msgid="8261326938599049290">"允許應用程式告知系統哪個應用程式可以使用哪些小工具。啟用這項權限後,應用程式即會讓其他應用程式使用個人資料 (不建議一般應用程式使用)。"</string>
     <string name="permlab_modifyPhoneState" msgid="8423923777659292228">"修改手機狀態"</string>
@@ -1147,7 +1147,9 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"收件者:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"請輸入必要的 PIN:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"手機與 <xliff:g id="DEVICE_NAME">%1$s</xliff:g> 連線期間將暫時中斷 WiFi 連線"</string>
+    <!-- no translation found for wifi_p2p_frequency_conflict_message (8012981257742232475) -->
+    <skip />
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"手機與 <xliff:g id="DEVICE_NAME">%1$s</xliff:g> 連線期間將暫時中斷 WiFi 連線"</string>
     <string name="select_character" msgid="3365550120617701745">"插入字元"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"傳送 SMS 簡訊"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"&lt;b&gt;&lt;/b&gt;「<xliff:g id="APP_NAME">%1$s</xliff:g>」正在傳送大量簡訊。您要允許這個應用程式繼續傳送簡訊嗎?"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 52cb1b9..d9d0913 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -200,10 +200,8 @@
     <string name="permgroupdesc_microphone" msgid="7106618286905738408">"Ukufinyelela okuqondile ku-microphone ukuze uqophe umsindo."</string>
     <string name="permgrouplab_camera" msgid="4820372495894586615">"Ikhamela"</string>
     <string name="permgroupdesc_camera" msgid="2933667372289567714">"Ukufinyelela okuqondile kukhamera ekuthwebuleni isithombe noma ividiyo."</string>
-    <!-- no translation found for permgrouplab_screenlock (8275500173330718168) -->
-    <skip />
-    <!-- no translation found for permgroupdesc_screenlock (7067497128925499401) -->
-    <skip />
+    <string name="permgrouplab_screenlock" msgid="8275500173330718168">"Khiya isikrini"</string>
+    <string name="permgroupdesc_screenlock" msgid="7067497128925499401">"Ikhono lokwazi ukuthinta ukuziphatha kwesikrini sokukhiya kudivayisi yakho."</string>
     <string name="permgrouplab_appInfo" msgid="8028789762634147725">"Ulwazi lezinhlelo zakho zokusebenza"</string>
     <string name="permgroupdesc_appInfo" msgid="3950378538049625907">"Amandla okuthinta ukuziphatha kwezinhlelo zokusebenza kudivayisi yakho."</string>
     <string name="permgrouplab_wallpaper" msgid="3850280158041175998">"Isithombe sangemuva"</string>
@@ -1050,10 +1048,8 @@
     <string name="editTextMenuTitle" msgid="4909135564941815494">"Izenzo zombhalo"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"Isikhala sokulondoloza siyaphela"</string>
     <string name="low_internal_storage_view_text" msgid="6640505817617414371">"Eminye imisebenzi yohlelo ingahle ingasebenzi"</string>
-    <!-- no translation found for app_running_notification_title (4625479411505090209) -->
-    <skip />
-    <!-- no translation found for app_running_notification_text (3368349329989620597) -->
-    <skip />
+    <string name="app_running_notification_title" msgid="4625479411505090209">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> iyasebenza"</string>
+    <string name="app_running_notification_text" msgid="3368349329989620597">"I-<xliff:g id="APP_NAME">%1$s</xliff:g> isebenza manje"</string>
     <string name="ok" msgid="5970060430562524910">"KULUNGILE"</string>
     <string name="cancel" msgid="6442560571259935130">"Khansela"</string>
     <string name="yes" msgid="5362982303337969312">"KULUNGILE"</string>
@@ -1147,7 +1143,8 @@
     <string name="wifi_p2p_to_message" msgid="248968974522044099">"Ku:"</string>
     <string name="wifi_p2p_enter_pin_message" msgid="5920929550367828970">"Faka i-PIN edingekayo:"</string>
     <string name="wifi_p2p_show_pin_message" msgid="8530563323880921094">"PIN:"</string>
-    <string name="wifi_p2p_frequency_conflict_message" msgid="7363907213787469151">"Ifoni izonqamuka okwesikhashana ku-Wi-Fi ngenkathi ixhumeke ku-<xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="tablet" msgid="8012981257742232475">"Ithebulethi izonqamuka okwesikhashana ku-Wi-Fi ngenkathi ixhumeke ku-<xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
+    <string name="wifi_p2p_frequency_conflict_message" product="default" msgid="7363907213787469151">"Ifoni izonqamuka okwesikhashana ku-Wi-Fi ngenkathi ixhumeke ku-<xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
     <string name="select_character" msgid="3365550120617701745">"Faka uhlamvu"</string>
     <string name="sms_control_title" msgid="7296612781128917719">"Ithumela imiyalezo ye-SMS"</string>
     <string name="sms_control_message" msgid="3867899169651496433">"I-&lt;b&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/b&gt; ithumela inombolo enkulu yemilayezo ye-SMS. Ufuna ukuvumela lolu hlelo lokusebenza ukuqhubeka ukuthumela imilayezo?"</string>
@@ -1473,8 +1470,7 @@
     <string name="kg_failed_attempts_almost_at_login" product="default" msgid="1437638152015574839">"Ukulayisha ungenisa iphathini yakho yokuvula ngendlela engalungile izikhathi ezi-<xliff:g id="NUMBER_0">%d</xliff:g> Emva kweminye imizamo engu-<xliff:g id="NUMBER_1">%d</xliff:g>, uzocelwa ukuvula ifoni yakho usebenzisa ukungena ngemvume ku-Google"\n\n" Zame futhi emumva kwengu- <xliff:g id="NUMBER_2">%d</xliff:g> imizuzwana."</string>
     <string name="kg_text_message_separator" product="default" msgid="4160700433287233771">" — "</string>
     <string name="kg_reordering_delete_drop_target_text" msgid="7899202978204438708">"Susa"</string>
-    <!-- no translation found for safe_media_volume_warning (7324161939475478066) -->
-    <skip />
+    <string name="safe_media_volume_warning" product="default" msgid="7324161939475478066">"Khulisa ivolomu ngaphezu kwezinga elinconyiwe?"\n"Ukulalela ngevolomu ephezulu izikhathi ezinde kungalimaza ukuzwa kwakho."</string>
     <string name="continue_to_enable_accessibility" msgid="1626427372316070258">"Gcina ucindezele iminwe yakho emibili ukuze unike amandla ukufinyelela."</string>
     <string name="accessibility_enabled" msgid="1381972048564547685">"Ukufinyelela kunikwe amandla."</string>
     <string name="enable_accessibility_canceled" msgid="3833923257966635673">"Ukufinyelela kukhanseliwe."</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index badd3d7..8c7a374 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2092,11 +2092,11 @@
             <!-- Don't use a layer. -->
             <enum name="none" value="0" />
             <!-- Use a software layer. Refer to
-                 {@link android.view.View#setLayerType(int, android.graphics.Paint) for
+                 {@link android.view.View#setLayerType(int, android.graphics.Paint)} for
                  more information. -->
             <enum name="software" value="1" />
             <!-- Use a hardware layer. Refer to
-                 {@link android.view.View#setLayerType(int, android.graphics.Paint) for
+                 {@link android.view.View#setLayerType(int, android.graphics.Paint)} for
                  more information. -->
             <enum name="hardware" value="2" />
         </attr>
@@ -2709,6 +2709,16 @@
              below and above child items.) The height of this will be the same as
              the height of the normal list item divider. -->
         <attr name="childDivider" format="reference|color" />
+        <!-- The start bound for an item's indicator. To specify a start bound specific to children,
+             use childIndicatorStart. -->
+        <attr name="indicatorStart" format="dimension" />
+        <!-- The end bound for an item's indicator. To specify a right bound specific to children,
+             use childIndicatorEnd. -->
+        <attr name="indicatorEnd" format="dimension" />
+        <!-- The start bound for a child's indicator. -->
+        <attr name="childIndicatorStart" format="dimension" />
+        <!-- The end bound for a child's indicator. -->
+        <attr name="childIndicatorEnd" format="dimension" />
     </declare-styleable>
     <declare-styleable name="Gallery">
         <attr name="gravity" />
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 6f59817..0afe4c1 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -889,6 +889,11 @@
         <!-- Declare that your application will be able to deal with RTL (right to left) layouts.
              If set to  false (default value), your application will not care about RTL layouts. -->
         <attr name="supportsRtl" format="boolean" />
+        <!-- Declare that this application requires access to restricted accounts of a certain
+             type. The default value is null and restricted accounts won\'t be visible to this
+             application. The type should correspond to the account authenticator type, such as
+             "com.google" -->
+        <attr name="restrictedAccountType" format="string"/>
     </declare-styleable>
     
     <!-- The <code>permission</code> tag declares a security permission that can be
@@ -991,9 +996,8 @@
               permission, and it must always be granted when it is installed.
               If you set this to false, then in some cases the application may
               be installed with it being granted the permission, and it will
-              need to request the permission later if it needs it.
+              need to request the permission later if it needs it. -->
         <attr name="required" format="boolean" />
-        -->
     </declare-styleable>
 
     <!-- The <code>uses-configuration</code> tag specifies
@@ -1036,7 +1040,7 @@
               don't support it.  If you set this to false, then this will
               not impose a restriction on where the application can be
               installed. -->
-        <attr name="required" format="boolean" />
+        <attr name="required" />
     </declare-styleable>
 
     <!-- The <code>uses-sdk</code> tag describes the SDK features that the
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 7a6a1e9..cc50d8a 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1043,4 +1043,9 @@
 
     <!-- Apps that are authorized to access shared accounts, overridden by product overlays -->
     <string name="config_appsAuthorizedForSharedAccounts"></string>
+
+    <!-- Flag indicating that the media framework should not allow changes or mute on any
+         stream or master volumes. -->
+    <bool name="config_useFixedVolume">false</bool>
+
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 9f810af..42d692f 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2037,6 +2037,12 @@
   <public type="attr" name="mirrorForRtl" />
   <public type="attr" name="windowOverscan" />
   <public type="attr" name="requiredForAllUsers" />
+  <public type="attr" name="indicatorStart"  />
+  <public type="attr" name="indicatorEnd" />
+  <public type="attr" name="childIndicatorStart" />
+  <public type="attr" name="childIndicatorEnd" />
+  <public type="attr" name="restrictedAccountType" />
+
   <public type="style" name="Theme.NoTitleBar.Overscan" />
   <public type="style" name="Theme.Light.NoTitleBar.Overscan" />
   <public type="style" name="Theme.Black.NoTitleBar.Overscan" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index a2d4570..4a15967 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3202,7 +3202,8 @@
     <string name="wifi_p2p_enter_pin_message">Type the required PIN: </string>
     <string name="wifi_p2p_show_pin_message">PIN: </string>
 
-    <string name="wifi_p2p_frequency_conflict_message">The phone will temporarily disconnect from Wi-Fi while it\'s connected to <xliff:g id="device_name">%1$s</xliff:g></string>
+    <string name="wifi_p2p_frequency_conflict_message" product="tablet">The tablet will temporarily disconnect from Wi-Fi while it\'s connected to <xliff:g id="device_name">%1$s</xliff:g></string>
+    <string name="wifi_p2p_frequency_conflict_message" product="default">The phone will temporarily disconnect from Wi-Fi while it\'s connected to <xliff:g id="device_name">%1$s</xliff:g></string>
 
     <!-- Name of the dialog that lets the user choose an accented character to insert -->
     <string name="select_character">Insert character</string>
@@ -4062,5 +4063,10 @@
     <string name="user_switched">Current user <xliff:g id="name" example="Bob">%1$s</xliff:g>.</string>
     <!-- Default name of the owner user [CHAR LIMIT=20] -->
     <string name="owner_name" msgid="3879126011135546571">Owner</string>
-
+    <!-- Error message title [CHAR LIMIT=35] -->
+    <string name="error_message_title">Error</string>
+    <!-- Message informing user that app is not permitted to access accounts. [CHAR LIMIT=none] -->
+    <string name="app_no_restricted_accounts">This application does not support accounts for limited users</string>
+    <!-- Message informing user that the requested activity could not be found [CHAR LIMIT=none] -->
+    <string name="app_not_found">No application found to handle this action</string>
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 80e77dd..757bbc8 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -277,6 +277,7 @@
   <java-symbol type="bool" name="config_camera_sound_forced" />
   <java-symbol type="bool" name="config_dontPreferApn" />
   <java-symbol type="bool" name="config_speed_up_audio_on_mt_calls" />
+  <java-symbol type="bool" name="config_useFixedVolume" />
 
   <java-symbol type="integer" name="config_cursorWindowSize" />
   <java-symbol type="integer" name="config_longPressOnPowerBehavior" />
@@ -868,7 +869,7 @@
   <java-symbol type="string" name="config_chooseAccountActivity" />
   <java-symbol type="string" name="config_chooseTypeAndAccountActivity" />
   <java-symbol type="string" name="config_appsAuthorizedForSharedAccounts" />
-
+  <java-symbol type="string" name="error_message_title" />
 
   <java-symbol type="plurals" name="abbrev_in_num_days" />
   <java-symbol type="plurals" name="abbrev_in_num_hours" />
@@ -1120,6 +1121,7 @@
   <java-symbol type="layout" name="sms_short_code_confirmation_dialog" />
   <java-symbol type="layout" name="keyguard_add_widget" />
   <java-symbol type="layout" name="action_bar_up_container" />
+  <java-symbol type="layout" name="app_not_authorized" />
 
   <java-symbol type="anim" name="slide_in_child_bottom" />
   <java-symbol type="anim" name="slide_in_right" />
diff --git a/core/res/res/xml/kg_password_kbd_numeric.xml b/core/res/res/xml/kg_password_kbd_numeric.xml
old mode 100755
new mode 100644
diff --git a/core/tests/benchmarks/src/com/android/internal/util/IndentingPrintWriterBenchmark.java b/core/tests/benchmarks/src/com/android/internal/util/IndentingPrintWriterBenchmark.java
new file mode 100644
index 0000000..34c73e8
--- /dev/null
+++ b/core/tests/benchmarks/src/com/android/internal/util/IndentingPrintWriterBenchmark.java
@@ -0,0 +1,152 @@
+/*
+ * 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.internal.util;
+
+import com.google.android.collect.Lists;
+import com.google.caliper.SimpleBenchmark;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+
+public class IndentingPrintWriterBenchmark extends SimpleBenchmark {
+
+    private PrintWriter mDirect;
+    private IndentingPrintWriter mIndenting;
+
+    private Node mSimple;
+    private Node mComplex;
+
+    @Override
+    protected void setUp() throws IOException {
+        final FileOutputStream os = new FileOutputStream(new File("/dev/null"));
+        mDirect = new PrintWriter(os);
+        mIndenting = new IndentingPrintWriter(mDirect, "  ");
+
+        final Node manyChildren = Node.build("ManyChildren", Node.build("1"), Node.build("2"),
+                Node.build("3"), Node.build("4"), Node.build("5"), Node.build("6"), Node.build("7"),
+                Node.build("8"), Node.build("9"), Node.build("10"));
+
+        mSimple = Node.build("RED");
+        mComplex = Node.build("PARENT", Node.build("RED"), Node.build("GREEN",
+                Node.build("BLUE", manyChildren, manyChildren), manyChildren, manyChildren),
+                manyChildren);
+    }
+
+    @Override
+    protected void tearDown() {
+        mIndenting.close();
+        mIndenting = null;
+        mDirect = null;
+    }
+
+    public void timeSimpleDirect(int reps) {
+        for (int i = 0; i < reps; i++) {
+            mSimple.dumpDirect(mDirect, 0);
+        }
+    }
+
+    public void timeSimpleIndenting(int reps) {
+        for (int i = 0; i < reps; i++) {
+            mSimple.dumpIndenting(mIndenting);
+        }
+    }
+
+    public void timeComplexDirect(int reps) {
+        for (int i = 0; i < reps; i++) {
+            mComplex.dumpDirect(mDirect, 0);
+        }
+    }
+
+    public void timeComplexIndenting(int reps) {
+        for (int i = 0; i < reps; i++) {
+            mComplex.dumpIndenting(mIndenting);
+        }
+    }
+
+    public void timePairRaw(int reps) {
+        final int value = 1024;
+        for (int i = 0; i < reps; i++) {
+            mDirect.print("key=");
+            mDirect.print(value);
+            mDirect.print(" ");
+        }
+    }
+
+    public void timePairIndenting(int reps) {
+        final int value = 1024;
+        for (int i = 0; i < reps; i++) {
+            mIndenting.printPair("key", value);
+        }
+    }
+
+    private static class Node {
+        public String name;
+        public ArrayList<Node> children;
+
+        private static String[] sIndents = new String[] { "", "  ", "    ", "      ", "        " };
+
+        public static Node build(String name, Node... children) {
+            Node node = new Node();
+            node.name = name;
+            if (children != null && children.length > 0) {
+                node.children = Lists.newArrayList(children);
+            }
+            return node;
+        }
+
+        private void dumpSelf(PrintWriter pw) {
+            pw.print("Node ");
+            pw.print(name);
+            pw.print(" first ");
+            pw.print(512);
+            pw.print(" second ");
+            pw.print(1024);
+            pw.print(" third ");
+            pw.println(2048);
+        }
+
+        public void dumpDirect(PrintWriter pw, int depth) {
+            pw.print(sIndents[depth]);
+            dumpSelf(pw);
+
+            if (children != null) {
+                for (Node child : children) {
+                    child.dumpDirect(pw, depth + 1);
+                }
+            }
+
+            pw.println();
+        }
+
+        public void dumpIndenting(IndentingPrintWriter pw) {
+            dumpSelf(pw);
+
+            if (children != null) {
+                pw.increaseIndent();
+                for (Node child : children) {
+                    child.dumpIndenting(pw);
+                }
+                pw.decreaseIndent();
+            }
+
+            pw.println();
+        }
+    }
+}
diff --git a/core/tests/coretests/src/android/net/RouteInfoTest.java b/core/tests/coretests/src/android/net/RouteInfoTest.java
index 59eb601..55d6592 100644
--- a/core/tests/coretests/src/android/net/RouteInfoTest.java
+++ b/core/tests/coretests/src/android/net/RouteInfoTest.java
@@ -149,6 +149,40 @@
         assertAreNotEqual(r1, r3);
     }
 
+    public void testHostRoute() {
+      RouteInfo r;
+
+      r = new RouteInfo(Prefix("0.0.0.0/0"), Address("0.0.0.0"), "wlan0");
+      assertFalse(r.isHostRoute());
+
+      r = new RouteInfo(Prefix("::/0"), Address("::"), "wlan0");
+      assertFalse(r.isHostRoute());
+
+      r = new RouteInfo(Prefix("192.0.2.0/24"), null, "wlan0");
+      assertFalse(r.isHostRoute());
+
+      r = new RouteInfo(Prefix("2001:db8::/48"), null, "wlan0");
+      assertFalse(r.isHostRoute());
+
+      r = new RouteInfo(Prefix("192.0.2.0/32"), Address("0.0.0.0"), "wlan0");
+      assertTrue(r.isHostRoute());
+
+      r = new RouteInfo(Prefix("2001:db8::/128"), Address("::"), "wlan0");
+      assertTrue(r.isHostRoute());
+
+      r = new RouteInfo(Prefix("192.0.2.0/32"), null, "wlan0");
+      assertTrue(r.isHostRoute());
+
+      r = new RouteInfo(Prefix("2001:db8::/128"), null, "wlan0");
+      assertTrue(r.isHostRoute());
+
+      r = new RouteInfo(Prefix("::/128"), Address("fe80::"), "wlan0");
+      assertTrue(r.isHostRoute());
+
+      r = new RouteInfo(Prefix("0.0.0.0/32"), Address("192.0.2.1"), "wlan0");
+      assertTrue(r.isHostRoute());
+    }
+
     public RouteInfo passThroughParcel(RouteInfo r) {
         Parcel p = Parcel.obtain();
         RouteInfo r2 = null;
diff --git a/core/tests/coretests/src/android/net/UriMatcherTest.java b/core/tests/coretests/src/android/net/UriMatcherTest.java
index 2872144..a728d4f 100644
--- a/core/tests/coretests/src/android/net/UriMatcherTest.java
+++ b/core/tests/coretests/src/android/net/UriMatcherTest.java
@@ -19,10 +19,11 @@
 import android.content.UriMatcher;
 import android.net.Uri;
 import android.test.suitebuilder.annotation.SmallTest;
+
 import junit.framework.TestCase;
 
-public class UriMatcherTest extends TestCase
-{
+public class UriMatcherTest extends TestCase {
+
     static final int ROOT = 0;
     static final int PEOPLE = 1;
     static final int PEOPLE_ID = 2;
@@ -37,54 +38,76 @@
     static final int CALLERID = 11;
     static final int CALLERID_TEXT = 12;
     static final int FILTERRECENT = 13;
-    
+    static final int ANOTHER_PATH_SEGMENT = 13;
+
     @SmallTest
     public void testContentUris() {
-        check("content://asdf", UriMatcher.NO_MATCH);
-        check("content://people", PEOPLE);
-        check("content://people/1", PEOPLE_ID);
-        check("content://people/asdf", UriMatcher.NO_MATCH);
-        check("content://people/2/phones", PEOPLE_PHONES); 
-        check("content://people/2/phones/3", PEOPLE_PHONES_ID); 
-        check("content://people/2/phones/asdf", UriMatcher.NO_MATCH);
-        check("content://people/2/addresses", PEOPLE_ADDRESSES); 
-        check("content://people/2/addresses/3", PEOPLE_ADDRESSES_ID); 
-        check("content://people/2/addresses/asdf", UriMatcher.NO_MATCH);
-        check("content://people/2/contact-methods", PEOPLE_CONTACTMETH); 
-        check("content://people/2/contact-methods/3", PEOPLE_CONTACTMETH_ID); 
-        check("content://people/2/contact-methods/asdf", UriMatcher.NO_MATCH);
-        check("content://calls", CALLS);
-        check("content://calls/1", CALLS_ID);
-        check("content://calls/asdf", UriMatcher.NO_MATCH);
-        check("content://caller-id", CALLERID);
-        check("content://caller-id/asdf", CALLERID_TEXT);
-        check("content://caller-id/1", CALLERID_TEXT);
-        check("content://filter-recent", FILTERRECENT);
+        UriMatcher matcher = new UriMatcher(ROOT);
+        matcher.addURI("people", null, PEOPLE);
+        matcher.addURI("people", "#", PEOPLE_ID);
+        matcher.addURI("people", "#/phones", PEOPLE_PHONES);
+        matcher.addURI("people", "#/phones/blah", PEOPLE_PHONES_ID);
+        matcher.addURI("people", "#/phones/#", PEOPLE_PHONES_ID);
+        matcher.addURI("people", "#/addresses", PEOPLE_ADDRESSES);
+        matcher.addURI("people", "#/addresses/#", PEOPLE_ADDRESSES_ID);
+        matcher.addURI("people", "#/contact-methods", PEOPLE_CONTACTMETH);
+        matcher.addURI("people", "#/contact-methods/#", PEOPLE_CONTACTMETH_ID);
+        matcher.addURI("calls", null, CALLS);
+        matcher.addURI("calls", "#", CALLS_ID);
+        matcher.addURI("caller-id", null, CALLERID);
+        matcher.addURI("caller-id", "*", CALLERID_TEXT);
+        matcher.addURI("filter-recent", null, FILTERRECENT);
+        matcher.addURI("auth", "another/path/segment", ANOTHER_PATH_SEGMENT);
+        checkAll(matcher);
     }
 
-    private static final UriMatcher mURLMatcher = new UriMatcher(ROOT);
-
-    static
-    {
-        mURLMatcher.addURI("people", null, PEOPLE);
-        mURLMatcher.addURI("people", "#", PEOPLE_ID);
-        mURLMatcher.addURI("people", "#/phones", PEOPLE_PHONES);
-        mURLMatcher.addURI("people", "#/phones/blah", PEOPLE_PHONES_ID);
-        mURLMatcher.addURI("people", "#/phones/#", PEOPLE_PHONES_ID);
-        mURLMatcher.addURI("people", "#/addresses", PEOPLE_ADDRESSES);
-        mURLMatcher.addURI("people", "#/addresses/#", PEOPLE_ADDRESSES_ID);
-        mURLMatcher.addURI("people", "#/contact-methods", PEOPLE_CONTACTMETH);
-        mURLMatcher.addURI("people", "#/contact-methods/#", PEOPLE_CONTACTMETH_ID);
-        mURLMatcher.addURI("calls", null, CALLS);
-        mURLMatcher.addURI("calls", "#", CALLS_ID);
-        mURLMatcher.addURI("caller-id", null, CALLERID);
-        mURLMatcher.addURI("caller-id", "*", CALLERID_TEXT);
-        mURLMatcher.addURI("filter-recent", null, FILTERRECENT);
+    @SmallTest
+    public void testContentUrisWithLeadingSlash() {
+        UriMatcher matcher = new UriMatcher(ROOT);
+        matcher.addURI("people", null, PEOPLE);
+        matcher.addURI("people", "/#", PEOPLE_ID);
+        matcher.addURI("people", "/#/phones", PEOPLE_PHONES);
+        matcher.addURI("people", "/#/phones/blah", PEOPLE_PHONES_ID);
+        matcher.addURI("people", "/#/phones/#", PEOPLE_PHONES_ID);
+        matcher.addURI("people", "/#/addresses", PEOPLE_ADDRESSES);
+        matcher.addURI("people", "/#/addresses/#", PEOPLE_ADDRESSES_ID);
+        matcher.addURI("people", "/#/contact-methods", PEOPLE_CONTACTMETH);
+        matcher.addURI("people", "/#/contact-methods/#", PEOPLE_CONTACTMETH_ID);
+        matcher.addURI("calls", null, CALLS);
+        matcher.addURI("calls", "/#", CALLS_ID);
+        matcher.addURI("caller-id", null, CALLERID);
+        matcher.addURI("caller-id", "/*", CALLERID_TEXT);
+        matcher.addURI("filter-recent", null, FILTERRECENT);
+        matcher.addURI("auth", "/another/path/segment", ANOTHER_PATH_SEGMENT);
+        checkAll(matcher);
     }
 
-    void check(String uri, int expected)
-    {
-        int result = mURLMatcher.match(Uri.parse(uri));
+    private void checkAll(UriMatcher matcher) {
+        check("content://asdf", UriMatcher.NO_MATCH, matcher);
+        check("content://people", PEOPLE, matcher);
+        check("content://people/1", PEOPLE_ID, matcher);
+        check("content://people/asdf", UriMatcher.NO_MATCH, matcher);
+        check("content://people/2/phones", PEOPLE_PHONES, matcher);
+        check("content://people/2/phones/3", PEOPLE_PHONES_ID, matcher);
+        check("content://people/2/phones/asdf", UriMatcher.NO_MATCH, matcher);
+        check("content://people/2/addresses", PEOPLE_ADDRESSES, matcher);
+        check("content://people/2/addresses/3", PEOPLE_ADDRESSES_ID, matcher);
+        check("content://people/2/addresses/asdf", UriMatcher.NO_MATCH, matcher);
+        check("content://people/2/contact-methods", PEOPLE_CONTACTMETH, matcher);
+        check("content://people/2/contact-methods/3", PEOPLE_CONTACTMETH_ID, matcher);
+        check("content://people/2/contact-methods/asdf", UriMatcher.NO_MATCH, matcher);
+        check("content://calls", CALLS, matcher);
+        check("content://calls/1", CALLS_ID, matcher);
+        check("content://calls/asdf", UriMatcher.NO_MATCH, matcher);
+        check("content://caller-id", CALLERID, matcher);
+        check("content://caller-id/asdf", CALLERID_TEXT, matcher);
+        check("content://caller-id/1", CALLERID_TEXT, matcher);
+        check("content://filter-recent", FILTERRECENT, matcher);
+        check("content://auth/another/path/segment", ANOTHER_PATH_SEGMENT, matcher);
+    }
+
+    private void check(String uri, int expected, UriMatcher matcher) {
+        int result = matcher.match(Uri.parse(uri));
         if (result != expected) {
             String msg = "failed on " + uri;
             msg += " expected " + expected + " got " + result;
diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk
index e02e95a..7f8d5f0 100644
--- a/data/fonts/Android.mk
+++ b/data/fonts/Android.mk
@@ -152,7 +152,7 @@
     RobotoCondensed-Italic.ttf \
     RobotoCondensed-BoldItalic.ttf \
     DroidNaskh-Regular.ttf \
-    DroidNaskh-Regular-SystemUI.ttf \
+    DroidNaskhUI-Regular.ttf \
     DroidSansDevanagari-Regular.ttf \
     DroidSansHebrew-Regular.ttf \
     DroidSansHebrew-Bold.ttf \
diff --git a/data/fonts/DroidNaskh-Regular-SystemUI.ttf b/data/fonts/DroidNaskhUI-Regular.ttf
similarity index 100%
rename from data/fonts/DroidNaskh-Regular-SystemUI.ttf
rename to data/fonts/DroidNaskhUI-Regular.ttf
Binary files differ
diff --git a/data/fonts/fallback_fonts.xml b/data/fonts/fallback_fonts.xml
index 999ddc4..16d760c 100644
--- a/data/fonts/fallback_fonts.xml
+++ b/data/fonts/fallback_fonts.xml
@@ -36,7 +36,7 @@
     </family>
     <family>
         <fileset>
-            <file variant="compact">DroidNaskh-Regular-SystemUI.ttf</file>
+            <file variant="compact">DroidNaskhUI-Regular.ttf</file>
         </fileset>
     </family>
     <family>
@@ -78,17 +78,50 @@
     </family>
     <family>
         <fileset>
-            <file>AnjaliNewLipi-light.ttf</file>
+            <file variant="elegant">NotoSansMalayalam-Regular.ttf</file>
+            <file variant="elegant">NotoSansMalayalam-Bold.ttf</file>
         </fileset>
     </family>
     <family>
         <fileset>
-            <file>Lohit-Bengali.ttf</file>
+            <file variant="compact">NotoSansMalayalamUI-Regular.ttf</file>
+            <file variant="compact">NotoSansMalayalamUI-Bold.ttf</file>
         </fileset>
     </family>
     <family>
         <fileset>
-            <file>Lohit-Kannada.ttf</file>
+            <file variant="elegant">NotoSansBengali-Regular.ttf</file>
+            <file variant="elegant">NotoSansBengali-Bold.ttf</file>
+        </fileset>
+    </family>
+    <family>
+        <fileset>
+            <file variant="compact">NotoSansBengaliUI-Regular.ttf</file>
+            <file variant="compact">NotoSansBengaliUI-Bold.ttf</file>
+        </fileset>
+    </family>
+    <family>
+        <fileset>
+            <file variant="elegant">NotoSansTelugu-Regular.ttf</file>
+            <file variant="elegant">NotoSansTelugu-Bold.ttf</file>
+        </fileset>
+    </family>
+    <family>
+        <fileset>
+            <file variant="compact">NotoSansTeluguUI-Regular.ttf</file>
+            <file variant="compact">NotoSansTeluguUI-Bold.ttf</file>
+        </fileset>
+    </family>
+    <family>
+        <fileset>
+            <file variant="elegant">NotoSansKannada-Regular.ttf</file>
+            <file variant="elegant">NotoSansKannada-Bold.ttf</file>
+        </fileset>
+    </family>
+    <family>
+        <fileset>
+            <file variant="compact">NotoSansKannadaUI-Regular.ttf</file>
+            <file variant="compact">NotoSansKannadaUI-Bold.ttf</file>
         </fileset>
     </family>
     <family>
@@ -103,11 +136,6 @@
     </family>
     <family>
         <fileset>
-            <file>Lohit-Telugu.ttf</file>
-        </fileset>
-    </family>
-    <family>
-        <fileset>
             <file>DroidSansFallback.ttf</file>
         </fileset>
     </family>
diff --git a/data/fonts/fonts.mk b/data/fonts/fonts.mk
index 875795a..7c2f955 100644
--- a/data/fonts/fonts.mk
+++ b/data/fonts/fonts.mk
@@ -33,7 +33,7 @@
     RobotoCondensed-Italic.ttf \
     RobotoCondensed-BoldItalic.ttf \
     DroidNaskh-Regular.ttf \
-    DroidNaskh-Regular-SystemUI.ttf \
+    DroidNaskhUI-Regular.ttf \
     DroidSansDevanagari-Regular.ttf \
     DroidSansHebrew-Regular.ttf \
     DroidSansHebrew-Bold.ttf \
diff --git a/docs/html/google/gcm/gcm.jd b/docs/html/google/gcm/gcm.jd
index f8ba7b4..089baf5 100644
--- a/docs/html/google/gcm/gcm.jd
+++ b/docs/html/google/gcm/gcm.jd
@@ -777,10 +777,11 @@
 
 <p class="note"><strong>Note:</strong> If your organization has a firewall 
 that restricts the traffic to or 
-from the Internet, you need to configure it to allow connectivity with GCM. 
+from the Internet, you need to configure it to allow connectivity with GCM in order for
+your Android devices to receive messages. 
 The ports to open are: 5228, 5229, and 5230. GCM typically only uses 5228, but 
 it sometimes uses 5229 and 5230. GCM doesn't provide specific IPs, so you should 
-allow your server to accept incoming connections from all IP addresses 
+allow your firewall to accept incoming connections from all IP addresses 
 contained in the IP blocks listed in Google's ASN of 15169.</p>
 
 
diff --git a/docs/html/google/play/billing/billing_integrate.jd b/docs/html/google/play/billing/billing_integrate.jd
index 3365cfc..57227a8 100644
--- a/docs/html/google/play/billing/billing_integrate.jd
+++ b/docs/html/google/play/billing/billing_integrate.jd
@@ -19,6 +19,7 @@
        <li><a href="#Subs">Implementing Subscriptions</a><li>
        </ol>
     </li>
+    <li><a href="#billing-security">Securing Your App</a>
   </ol>
   <h2>Reference</h2>
   <ol>
@@ -361,6 +362,34 @@
 the user. Once a subscription expires without renewal, it will no longer appear 
 in the returned {@code Bundle}.</p>
 
+<h2 id="billing-security">Securing Your Application</h2>
+
+<p>To help ensure the integrity of the transaction information that is sent to 
+your application, Google Play signs the JSON string that contains the response 
+data for a purchase order. Google Play uses the private key that is associated 
+with your application in the Developer Console to create this signature. The 
+Developer Console generates an RSA key pair for each application.<p>
+
+<p class="note"><strong>Note:</strong>To find the public key portion of this key 
+pair, open your application's details in the Developer Console, then click on 
+<strong>Services & APIs</strong>, and look at the field titled 
+<strong>Your License Key for This Application</strong>.</p>
+
+<p>The Base64-encoded RSA public key generated by Google Play is in binary 
+encoded, X.509 subjectPublicKeyInfo DER SEQUENCE format. It is the same public 
+key that is used with Google Play licensing.</p>
+
+<p>When your application receives this signed response you can 
+use the public key portion of your RSA key pair to verify the signature. 
+By performing signature verification you can detect responses that have 
+been tampered with or that have been spoofed. You can perform this signature 
+verification step in your application; however, if your application connects 
+to a secure remote server then we recommend that you perform the signature 
+verification on that server.</p>
+
+<p>For more information about best practices for security and design, see <a
+href="{@docRoot}google/play/billing/billing_best_practices.html">Security and Design</a>.</p>
+
 
 
 
diff --git a/docs/html/google/play/billing/billing_reference.jd b/docs/html/google/play/billing/billing_reference.jd
index 1410e65..e168d70 100644
--- a/docs/html/google/play/billing/billing_reference.jd
+++ b/docs/html/google/play/billing/billing_reference.jd
@@ -143,7 +143,9 @@
   </tr>
   <tr>
     <td>{@code INAPP_DATA_SIGNATURE}</td>
-    <td>String containing the signature of the purchase data that was signed with the private key of the developer.</td>
+    <td>String containing the signature of the purchase data that was signed 
+with the private key of the developer. The data signature uses the 
+RSASSA-PKCS1-v1_5 scheme.</td>
   </tr>
 </table>
 </p>
diff --git a/docs/html/google/play/billing/v2/billing_integrate.jd b/docs/html/google/play/billing/v2/billing_integrate.jd
old mode 100755
new mode 100644
diff --git a/docs/html/google/play/licensing/adding-licensing.jd b/docs/html/google/play/licensing/adding-licensing.jd
index 3f2460f..93561f6 100644
--- a/docs/html/google/play/licensing/adding-licensing.jd
+++ b/docs/html/google/play/licensing/adding-licensing.jd
@@ -853,37 +853,39 @@
 
 <h3 id="account-key">Embed your public key for licensing</h3>
 
-<p>For each publisher account, the Google Play service automatically
-generates a  2048-bit RSA public/private key pair that is used exclusively for
-licensing. The key pair is uniquely associated with the publisher account and is
-shared across all applications that are published through the account. Although
-associated with a publisher account, the key pair is <em>not</em> the same as
-the key that you use to sign your applications (or derived from it).</p>
+<p>For each application, the Google Play service automatically
+generates a  2048-bit RSA public/private key pair that is used for 
+licensing and in-app billing. The key pair is uniquely associated with the 
+application. Although associated with the application, the key pair is 
+<em>not</em> the same as the key that you use to sign your applications (or derived from it).</p>
 
 <p>The Google Play Developer Console exposes the public key for licensing to any
-developer signed in to the publisher account, but it keeps the private key
+developer signed in to the Developer Console, but it keeps the private key
 hidden from all users in a secure location. When an application requests a
 license check for an application published in your account, the licensing server
-signs the license response using the private key of your account's key pair.
+signs the license response using the private key of your application's key pair.
 When the LVL receives the response, it uses the public key provided by the
 application to verify the signature of the license response. </p>
 
-<p>To add licensing to an application, you must obtain your publisher account's
+<p>To add licensing to an application, you must obtain your application's
 public key for licensing and copy it into your application. Here's how to find
-your account's public key for licensing:</p>
+your application's public key for licensing:</p>
 
 <ol>
 <li>Go to the Google Play <a
 href="http://play.google.com/apps/publish">Developer Console</a> and sign in.
 Make sure that you sign in to the account from which the application you are
 licensing is published (or will be published). </li>
-<li>In the account home page, locate the "Edit profile" link and click it. </li>
-<li>In the Edit Profile page, locate the "Licensing" pane, shown below. Your
-public key for licensing is given in the "Public key" text box. </li>
+<li>In the application details page, locate the <strong>Services & APIs</strong> 
+link and click it. </li>
+<li>In the <strong>Services & APIs</strong> page, locate the 
+<strong>Licensing & In-App Billing</strong> section. Your public key for 
+licensing is given in the 
+<strong>Your License Key For This Application</strong> field. </li>
 </ol>
 
 <p>To add the public key to your application, simply copy/paste the key string
-from the text box into your application as the value of the String variable
+from the field into your application as the value of the String variable
 <code>BASE64_PUBLIC_KEY</code>. When you are copying, make sure that you have
 selected the entire key string, without omitting any characters. </p>
 
@@ -965,16 +967,6 @@
 </ul>
 </div>
 
-
-
-
-
-
-
-
-
-
-
 <h2 id="app-obfuscation">Obfuscating Your Code</h2>
 
 <p>To ensure the security of your application, particularly for a paid
diff --git a/docs/html/google/play/licensing/index.jd b/docs/html/google/play/licensing/index.jd
index a13be10..6632fc0 100644
--- a/docs/html/google/play/licensing/index.jd
+++ b/docs/html/google/play/licensing/index.jd
@@ -16,7 +16,7 @@
 
 <p>The licensing service is a secure means of controlling access to your applications. When an
 application checks the licensing status, the Google Play server signs the licensing status
-response using a key pair that is uniquely associated with the publisher account. Your application
+response using a key pair that is uniquely associated with the application. Your application
 stores the public key in its compiled <code>.apk</code> file and uses it to verify the licensing
 status response.</p>
 
diff --git a/docs/html/google/play/licensing/licensing-reference.jd b/docs/html/google/play/licensing/licensing-reference.jd
index 4240097..7bfa61a 100644
--- a/docs/html/google/play/licensing/licensing-reference.jd
+++ b/docs/html/google/play/licensing/licensing-reference.jd
@@ -186,7 +186,7 @@
 </tr>
 <tr>
 <td>{@code ERROR_SERVER_FAILURE}</td>
-<td>Server error &mdash; the server could not load the publisher account's key
+<td>Server error &mdash; the server could not load the application's key
 pair for licensing.</td>
 <td>No</td>
 <td></td>
diff --git a/docs/html/google/play/licensing/overview.jd b/docs/html/google/play/licensing/overview.jd
index 2434a4c..4e1a9c9 100644
--- a/docs/html/google/play/licensing/overview.jd
+++ b/docs/html/google/play/licensing/overview.jd
@@ -38,13 +38,13 @@
 the result to your application, which can allow or disallow further use of the
 application as needed.</p>
 
-<p class="note"><strong>Note:</strong> If a paid application has been uploaded to Google Play but
-saved only as a draft application (the app is unpublished), the licensing server considers all users
-to be licensed users of the application (because it's not even possible to purchase the app).
-This exception is necessary in order for you to perform testing of your licensing
+<p class="note"><strong>Note:</strong> If a paid application has been uploaded 
+to Google Play, but saved only as a draft application (the app is 
+unpublished), the licensing server considers all users to be licensed users of 
+the application (because it's not even possible to purchase the app). This 
+exception is necessary in order for you to perform testing of your licensing 
 implementation.</p>
 
-
 <div class="figure" style="width:469px">
 <img src="{@docRoot}images/licensing_arch.png" alt=""/>
 <p class="img-caption"><strong>Figure 1.</strong> Your application initiates a
@@ -102,10 +102,11 @@
 server and you.</p>
 
 <p>The licensing service generates a single licensing key pair for each
-publisher account and exposes the public key in your account's profile page. You must copy the
-public key from the web site and embed it in your application source code. The server retains the
-private key internally and uses it to sign license responses for the applications you
-publish with that account.</p>
+application and exposes the public key in your application's 
+<strong>Services & APIs</strong> page in the Developer Console. You must copy 
+the public key from the Developer Console and embed it in your application 
+source code. The server retains the private key internally and uses it to sign 
+license responses for the applications you publish with that account.</p>
 
 <p>When your application receives a signed response, it uses the embedded public
 key to verify the data. The use of public key cryptography in the licensing
@@ -221,7 +222,7 @@
 <p>Licensing lets you move to a license-based model that is enforceable on
 all devices that have access to Google Play. Access is not bound to the
 characteristics of the host device, but to your
-publisher account on Google Play (through the app's public key) and the
+application on Google Play (through the app's public key) and the
 licensing policy that you define. Your application can be installed and
 managed on any device on any storage, including SD card.</p>
 
diff --git a/docs/html/guide/practices/index.jd b/docs/html/guide/practices/index.jd
index 04a43c5..48a849a 100644
--- a/docs/html/guide/practices/index.jd
+++ b/docs/html/guide/practices/index.jd
@@ -19,7 +19,7 @@
     
     <a href="http://android-developers.blogspot.com/2012/01/say-goodbye-to-menu-button.html">
       <h4>Say Goodbye to the Menu Button</h4>
-      <p>As Ice Cream Sandwich rolls out to more devices, it?s important that you begin to migrate
+      <p>As Ice Cream Sandwich rolls out to more devices, it's important that you begin to migrate
 your designs to the action bar in order to promote a consistent Android user experience.</p>
     </a>
     
@@ -49,4 +49,4 @@
   </div>
 
 
-</div>
\ No newline at end of file
+</div>
diff --git a/docs/html/guide/topics/providers/calendar-provider.jd b/docs/html/guide/topics/providers/calendar-provider.jd
index f53b062..5adc68c 100644
--- a/docs/html/guide/topics/providers/calendar-provider.jd
+++ b/docs/html/guide/topics/providers/calendar-provider.jd
@@ -605,7 +605,7 @@
 Uri updateUri = null;
 // The new title for the event
 values.put(Events.TITLE, &quot;Kickboxing&quot;); 
-myUri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
+updateUri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
 int rows = getContentResolver().update(updateUri, values, null, null);
 Log.i(DEBUG_TAG, &quot;Rows updated: &quot; + rows);  </pre>
 
diff --git a/docs/html/images/home/io-extended-2013.png b/docs/html/images/home/io-extended-2013.png
new file mode 100644
index 0000000..93989d4
--- /dev/null
+++ b/docs/html/images/home/io-extended-2013.png
Binary files differ
diff --git a/docs/html/images/ui/notifications/custom_message.png b/docs/html/images/ui/notifications/custom_message.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/docs/html/images/ui/notifications/notifications_window.png b/docs/html/images/ui/notifications/notifications_window.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/docs/html/images/ui/notifications/status_bar.png b/docs/html/images/ui/notifications/status_bar.png
old mode 100755
new mode 100644
Binary files differ
diff --git a/docs/html/index.jd b/docs/html/index.jd
index ec0469c..29d6a8f 100644
--- a/docs/html/index.jd
+++ b/docs/html/index.jd
@@ -14,16 +14,16 @@
             <ul>
                 <li class="item carousel-home">
                     <div class="content-left col-10">
-                    <img src="{@docRoot}images/home/io-logo-2013.png" style="margin:40px 0 0">
+                    <img src="{@docRoot}images/home/io-extended-2013.png" style="margin:90px 0 0">
                     </div>
                     <div class="content-right col-5">
-                    <h1>Google I/O 2013</h1>
-                    <p>Android will be at Google I/O on May 15-17, 2013, with sessions covering a variety of topics
-                    such as design, performance, and how to extend your app with the latest Android features.</p>
-                    <p>For more information about event details and planned sessions,
-                    stay tuned to <a
-                    href="http://google.com/+GoogleDevelopers">+Google Developers</a>.</p>
-                    <p><a href="https://developers.google.com/events/io/" class="button">Learn more</a></p>
+                    <h1>Google I/O Extended</h1>
+                    <p>Android will be at Google I/O on May 15-17, 2013, with sessions covering topics
+                    such as design, performance, and how to enhance your app with the latest Android features.</p>
+                    <p>Even if you can't make it there, you can experience the excitement and innovation of
+                    Google I/O remotely with Google I/O Extended.</p>
+                    <p><a href="https://developers.google.com/events/io/io-extended/?utm_source=site&utm_medium=emb&utm_campaign=extended-android-site"
+                    >Organize or attend an event near you &raquo;</a></p>
                     </div>
                 </li>
                 <li class="item carousel-home">
diff --git a/docs/html/tools/devices/emulator.jd b/docs/html/tools/devices/emulator.jd
index cee6473..bae3985 100644
--- a/docs/html/tools/devices/emulator.jd
+++ b/docs/html/tools/devices/emulator.jd
@@ -1254,7 +1254,7 @@
 <td>&nbsp;</td>
 </tr>
 <tr>
-  <td><code>power health &lt;percent&gt;</code></td>
+  <td><code>capacity &lt;percent&gt;</code></td>
   <td>Set remaining battery capacity state (0-100).</td>
 <td>&nbsp;</td>
 </tr>
diff --git a/docs/html/tools/testing/testing_otheride.jd b/docs/html/tools/testing/testing_otheride.jd
index 0678f52..9484158 100644
--- a/docs/html/tools/testing/testing_otheride.jd
+++ b/docs/html/tools/testing/testing_otheride.jd
@@ -75,9 +75,9 @@
 <p>
     You use the <code>android</code> tool to create test projects.
     You also use <code>android</code> to convert existing test code into an Android test project,
-    or to add the <code>run-tests</code> Ant target to an existing Android test project.
+    or to add the <code>test</code> Ant target to an existing Android test project.
     These operations are described in more detail in the section <a href="#UpdateTestProject">
-    Updating a test project</a>. The <code>run-tests</code> target is described in
+    Updating a test project</a>. The <code>test</code> target is described in
     <a href="#RunTestsAnt">Quick build and run with Ant</a>.
 </p>
 <h3 id="CreateTestProject">Creating a test project</h3>
@@ -300,7 +300,7 @@
 <h3 id="RunTestsAnt">Quick build and run with Ant</h3>
 <p>
     You can use Ant to run all the tests in your test project, using the target
-    <code>run-tests</code>, which is created automatically when you create a test project with
+    <code>test</code>, which is created automatically when you create a test project with
     the <code>android</code> tool.
 </p>
 <p>
diff --git a/docs/html/training/animation/anim_card_flip.mp4 b/docs/html/training/animation/anim_card_flip.mp4
old mode 100755
new mode 100644
Binary files differ
diff --git a/docs/html/training/animation/anim_card_flip.ogv b/docs/html/training/animation/anim_card_flip.ogv
old mode 100755
new mode 100644
Binary files differ
diff --git a/docs/html/training/animation/anim_card_flip.webm b/docs/html/training/animation/anim_card_flip.webm
old mode 100755
new mode 100644
Binary files differ
diff --git a/docs/html/training/animation/anim_screenslide.mp4 b/docs/html/training/animation/anim_screenslide.mp4
old mode 100755
new mode 100644
Binary files differ
diff --git a/docs/html/training/animation/anim_screenslide.ogv b/docs/html/training/animation/anim_screenslide.ogv
old mode 100755
new mode 100644
Binary files differ
diff --git a/docs/html/training/animation/anim_screenslide.webm b/docs/html/training/animation/anim_screenslide.webm
old mode 100755
new mode 100644
Binary files differ
diff --git a/docs/html/training/basics/data-storage/databases.jd b/docs/html/training/basics/data-storage/databases.jd
index 9976bb1..61fb758 100644
--- a/docs/html/training/basics/data-storage/databases.jd
+++ b/docs/html/training/basics/data-storage/databases.jd
@@ -284,7 +284,7 @@
 // Define 'where' part of query.
 String selection = FeedReaderContract.FeedEntry.COLUMN_NAME_ENTRY_ID + &quot; LIKE ?&quot;;
 // Specify arguments in placeholder order.
-String[] selelectionArgs = { String.valueOf(rowId) };
+String[] selectionArgs = { String.valueOf(rowId) };
 // Issue SQL statement.
 db.delete(table_name, selection, selectionArgs);
 </pre>
@@ -309,7 +309,7 @@
 
 // Which row to update, based on the ID
 String selection = FeedReaderContract.FeedEntry.COLUMN_NAME_ENTRY_ID + &quot; LIKE ?&quot;;
-String[] selelectionArgs = { String.valueOf(rowId) };
+String[] selectionArgs = { String.valueOf(rowId) };
 
 int count = db.update(
     FeedReaderDbHelper.FeedEntry.TABLE_NAME,
diff --git a/docs/html/training/basics/intents/result.jd b/docs/html/training/basics/intents/result.jd
index 0086913..24ecc46 100644
--- a/docs/html/training/basics/intents/result.jd
+++ b/docs/html/training/basics/intents/result.jd
@@ -62,7 +62,7 @@
 static final int PICK_CONTACT_REQUEST = 1;  // The request code
 ...
 private void pickContact() {
-    Intent pickContactIntent = new Intent(Intent.ACTION_PICK, new Uri("content://contacts"));
+    Intent pickContactIntent = new Intent(Intent.ACTION_PICK, Uri.parse("content://contacts"));
     pickContactIntent.setType(Phone.CONTENT_TYPE); // Show user only contacts w/ phone numbers
     startActivityForResult(pickContactIntent, PICK_CONTACT_REQUEST);
 }
diff --git a/docs/html/training/contacts-provider/ContactsList.zip b/docs/html/training/contacts-provider/ContactsList.zip
new file mode 100644
index 0000000..d2a5cfb
--- /dev/null
+++ b/docs/html/training/contacts-provider/ContactsList.zip
Binary files differ
diff --git a/docs/html/training/contacts-provider/display-contact-badge.jd b/docs/html/training/contacts-provider/display-contact-badge.jd
new file mode 100644
index 0000000..f08935d
--- /dev/null
+++ b/docs/html/training/contacts-provider/display-contact-badge.jd
@@ -0,0 +1,635 @@
+page.title=Displaying the Quick Contact Badge
+
+trainingnavtop=true
+@jd:body
+
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<!-- table of contents -->
+<h2>This lesson teaches you to</h2>
+<ol>
+    <li>
+        <a href="#AddView">Add a QuickContactBadge View</a>
+    </li>
+    <li>
+        <a href="#SetURIThumbnail">Set the Contact URI and Thumbnail</a>
+    </li>
+    <li>
+        <a href="#ListView">
+            Add a QuickContactBadge to a ListView
+        </a>
+    </li>
+</ol>
+
+<!-- other docs (NOT javadocs) -->
+<h2>You should also read</h2>
+<ul>
+    <li>
+        <a href="{@docRoot}guide/topics/providers/content-provider-basics.html">
+        Content Provider Basics
+        </a>
+    </li>
+    <li>
+        <a href="{@docRoot}guide/topics/providers/contacts-provider.html">
+        Contacts Provider
+        </a>
+    </li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+    <a href="http://developer.android.com/shareables/training/ContactsList.zip" class="button">
+    Download the sample
+    </a>
+ <p class="filename">ContactsList.zip</p>
+</div>
+
+</div>
+</div>
+<p>
+    This lesson shows you how to add a {@link android.widget.QuickContactBadge} to your UI
+    and how to bind data to it. A {@link android.widget.QuickContactBadge} is a widget that
+    initially appears as a thumbnail image. Although you can use any {@link android.graphics.Bitmap}
+    for the thumbnail image, you usually use a {@link android.graphics.Bitmap} decoded from the
+    contact's photo thumbnail image.
+</p>
+<p>
+    The small image acts as a control; when users click on the image, the
+    {@link android.widget.QuickContactBadge} expands into a dialog containing the following:
+</p>
+<dl>
+    <dt>A large image</dt>
+    <dd>
+        The large image associated with the contact, or no image is available, a placeholder
+        graphic.
+    </dd>
+    <dt>
+        App icons
+    </dt>
+    <dd>
+        An app icon for each piece of detail data that can be handled by a built-in app. For
+        example, if the contact's details include one or more email addresses, an email icon
+        appears. When users click the icon, all of the contact's email addresses appear. When users
+        click one of the addresses, the email app displays a screen for composing a message to the
+        selected email address.
+    </dd>
+</dl>
+<p>
+    The {@link android.widget.QuickContactBadge} view provides instant access to a contact's
+    details, as well as a fast way of communicating with the contact. Users don't have to look up
+    a contact, find and copy information, and then paste it into the appropriate app. Instead, they
+    can click on the {@link android.widget.QuickContactBadge}, choose the communication method they
+    want to use, and send the information for that method directly to the appropriate app.
+</p>
+<h2 id="AddView">Add a QuickContactBadge View</h2>
+<p>
+    To add a {@link android.widget.QuickContactBadge}, insert a
+    <code>&lt;QuickContactBadge&gt;</code> element in your layout. For example:
+</p>
+<pre>
+&lt;RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"&gt;
+...
+    &lt;QuickContactBadge
+               android:id=&#64;+id/quickbadge
+               android:layout_height="wrap_content"
+               android:layout_width="wrap_content"
+               android:scaleType="centerCrop"/&gt;
+    ...
+&lt;/RelativeLayout&gt;
+</pre>
+<h2 id="">Retrieve provider data</h2>
+<p>
+    To display a contact in the {@link android.widget.QuickContactBadge}, you need a content URI
+    for the contact and a {@link android.graphics.Bitmap} for the small image. You generate
+    both the content URI and the {@link android.graphics.Bitmap} from columns retrieved from the
+    Contacts Provider. Specify these columns as part of the projection you use to load data into
+    your {@link android.database.Cursor}.
+</p>
+<p>
+    For Android 3.0 (API level 11) and later, include the following columns in your projection:</p>
+<ul>
+    <li>{@link android.provider.ContactsContract.Contacts#_ID Contacts._ID}</li>
+    <li>{@link android.provider.ContactsContract.Contacts#LOOKUP_KEY Contacts.LOOKUP_KEY}</li>
+    <li>
+        {@link android.provider.ContactsContract.Contacts#PHOTO_THUMBNAIL_URI
+        Contacts.PHOTO_THUMBNAIL_URI}
+    </li>
+</ul>
+<p>
+    For Android 2.3.3 (API level 10) and earlier, use the following columns:
+</p>
+<ul>
+    <li>{@link android.provider.ContactsContract.Contacts#_ID Contacts._ID}</li>
+    <li>{@link android.provider.ContactsContract.Contacts#LOOKUP_KEY Contacts.LOOKUP_KEY}</li>
+</ul>
+<p>
+    The remainder of this lesson assumes that you've already loaded a
+    {@link android.database.Cursor} that contains these columns as well as others you may have
+    chosen. To learn how to retrieve this columns in a {@link android.database.Cursor}, read the
+    lesson <a href="retrieve-names.html">Retrieving a List of Contacts</a>.
+</p>
+<h2 id="SetURIThumbnail">Set the Contact URI and Thumbnail</h2>
+<p>
+    Once you have the necessary columns, you can bind data to the
+    {@link android.widget.QuickContactBadge}.
+</p>
+<h3>Set the Contact URI</h3>
+<p>
+    To set the content URI for the contact, call
+    {@link android.provider.ContactsContract.Contacts#getLookupUri getLookupUri(id,lookupKey)} to
+    get a {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}, then
+    call {@link android.widget.QuickContactBadge#assignContactUri assignContactUri()} to set the
+    contact. For example:
+</p>
+<pre>
+    // The Cursor that contains contact rows
+    Cursor mCursor;
+    // The index of the _ID column in the Cursor
+    int mIdColumn;
+    // The index of the LOOKUP_KEY column in the Cursor
+    int mLookupKeyColumn;
+    // A content URI for the desired contact
+    Uri mContactUri;
+    // A handle to the QuickContactBadge view
+    QuickContactBadge mBadge;
+    ...
+    mBadge = (QuickContactBadge) findViewById(R.id.quickbadge);
+    /*
+     * Insert code here to move to the desired cursor row
+     */
+    // Gets the _ID column index
+    mIdColumn = mCursor.getColumnIndex(Contacts._ID);
+    // Gets the LOOKUP_KEY index
+    mLookupKeyColumn = mCursor.getColumnIndex(Contacts.LOOKUP_KEY);
+    // Gets a content URI for the contact
+    mContactUri =
+            Contacts.getLookupUri(
+                Cursor.getLong(mIdColumn),
+                Cursor.getString(mLookupKeyColumn)
+            );
+    mBadge.assignContactUri(mContactUri);
+</pre>
+<p>
+    When users click the {@link android.widget.QuickContactBadge} icon, the contact's
+    details automatically appear in the dialog.
+</p>
+<h3>Set the photo thumbnail</h3>
+<p>
+    Setting the contact URI for the {@link android.widget.QuickContactBadge} does not automatically
+    load the contact's thumbnail photo. To load the photo, get a URI for the photo from the
+    contact's {@link android.database.Cursor} row, use it to open the file containing the compressed
+    thumbnail photo, and read the file into a {@link android.graphics.Bitmap}.
+</p>
+<p class="note">
+    <strong>Note:</strong> The
+    {@link android.provider.ContactsContract.Contacts#PHOTO_THUMBNAIL_URI} column isn't available
+    in platform versions prior to 3.0. For those versions, you must retrieve the URI
+    from the {@link android.provider.ContactsContract.Contacts.Photo Contacts.Photo} subtable.
+</p>
+<p>
+    First, set up variables for accessing the {@link android.database.Cursor} containing the
+    {@link android.provider.ContactsContract.Contacts#_ID Contacts._ID} and
+    {@link android.provider.ContactsContract.Contacts#LOOKUP_KEY Contacts.LOOKUP_KEY} columns, as
+    described previously:
+</p>
+<pre>
+    // The column in which to find the thumbnail ID
+    int mThumbnailColumn;
+    /*
+     * The thumbnail URI, expressed as a String.
+     * Contacts Provider stores URIs as String values.
+     */
+    String mThumbnailUri;
+    ...
+    /*
+     * Gets the photo thumbnail column index if
+     * platform version &gt;= Honeycomb
+     */
+    if (Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.HONEYCOMB) {
+        mThumbnailColumn =
+                mCursor.getColumnIndex(Contacts.PHOTO_THUMBNAIL_URI);
+    // Otherwise, sets the thumbnail column to the _ID column
+    } else {
+        mThumbnailColumn = mIdColumn;
+    }
+    /*
+     * Assuming the current Cursor position is the contact you want,
+     * gets the thumbnail ID
+     */
+    mThumbnailUri = Cursor.getString(mThumbnailColumn);
+    ...
+</pre>
+<p>
+    Define a method that takes photo-related data for the contact and dimensions for the
+    destination view,  and returns the properly-sized thumbnail in a
+    {@link android.graphics.Bitmap}. Start by constructing a URI that points to the
+    thumbnail:
+<p>
+<pre>
+    /**
+     * Load a contact photo thumbnail and return it as a Bitmap,
+     * resizing the image to the provided image dimensions as needed.
+     * @param photoData photo ID Prior to Honeycomb, the contact's _ID value.
+     * For Honeycomb and later, the value of PHOTO_THUMBNAIL_URI.
+     * @return A thumbnail Bitmap, sized to the provided width and height.
+     * Returns null if the thumbnail is not found.
+     */
+    private Bitmap loadContactPhotoThumbnail(String photoData) {
+        // Creates an asset file descriptor for the thumbnail file.
+        AssetFileDescriptor afd = null;
+        // try-catch block for file not found
+        try {
+            // Creates a holder for the URI.
+            Uri thumbUri;
+            // If Android 3.0 or later
+            if (Build.VERSION.SDK_INT
+                    &gt;=
+                Build.VERSION_CODES.HONEYCOMB) {
+                // Sets the URI from the incoming PHOTO_THUMBNAIL_URI
+                thumbUri = Uri.parse(photoData);
+            } else {
+            // Prior to Android 3.0, constructs a photo Uri using _ID
+                /*
+                 * Creates a contact URI from the Contacts content URI
+                 * incoming photoData (_ID)
+                 */
+                final Uri contactUri = Uri.withAppendedPath(
+                        Contacts.CONTENT_URI, photoData);
+                /*
+                 * Creates a photo URI by appending the content URI of
+                 * Contacts.Photo.
+                 */
+                thumbUri =
+                        Uri.withAppendedPath(
+                                contactUri, Photo.CONTENT_DIRECTORY);
+            }
+    
+        /*
+         * Retrieves an AssetFileDescriptor object for the thumbnail
+         * URI
+         * using ContentResolver.openAssetFileDescriptor
+         */
+        afd = getActivity().getContentResolver().
+                openAssetFileDescriptor(thumbUri, "r");
+        /*
+         * Gets a file descriptor from the asset file descriptor.
+         * This object can be used across processes.
+         */
+        FileDescriptor fileDescriptor = afd.getFileDescriptor();
+        // Decode the photo file and return the result as a Bitmap
+        // If the file descriptor is valid
+        if (fileDescriptor != null) {
+            // Decodes the bitmap
+            return BitmapFactory.decodeFileDescriptor(
+                    fileDescriptor, null, null);
+            }
+        // If the file isn't found
+        } catch (FileNotFoundException e) {
+            /*
+             * Handle file not found errors
+             */
+        }
+        // In all cases, close the asset file descriptor
+        } finally {
+            if (afd != null) {
+                try {
+                    afd.close();
+                } catch (IOException e) {}
+            }
+        }
+        return null;
+    }
+</pre>
+<p>
+    Call the <code>loadContactPhotoThumbnail()</code> method in your code to get the
+    thumbnail {@link android.graphics.Bitmap}, and use the result to set the photo thumbnail in
+    your {@link android.widget.QuickContactBadge}:
+</p>
+<pre>
+    ...
+    /*
+     * Decodes the thumbnail file to a Bitmap.
+     */
+    Bitmap mThumbnail =
+            loadContactPhotoThumbnail(mThumbnailUri);
+    /*
+     * Sets the image in the QuickContactBadge
+     * QuickContactBadge inherits from ImageView, so
+     */
+    mBadge.setImageBitmap(mThumbnail);
+</pre>
+<h2 id="ListView">Add a QuickContactBadge to a ListView</h2>
+<p>
+    A {@link android.widget.QuickContactBadge} is a useful addition to a
+    {@link android.widget.ListView} that displays a list of contacts. Use the
+    {@link android.widget.QuickContactBadge} to display a thumbnail photo for each contact; when
+    users click the thumbnail, the {@link android.widget.QuickContactBadge} dialog appears.
+</p>
+<h3>Add the QuickContactBadge element</h3>
+<p>
+    To start, add a {@link android.widget.QuickContactBadge} view element to your item layout
+    For example, if you want to display a {@link android.widget.QuickContactBadge} and a name for
+    each contact you retrieve, put the following XML into a layout file:
+</p>
+<pre>
+&lt;RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"&gt;
+    &lt;QuickContactBadge
+        android:id="&#64;+id/quickcontact"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:scaleType="centerCrop"/&gt;
+    &lt;TextView android:id="&#64;+id/displayname"
+              android:layout_width="match_parent"
+              android:layout_height="wrap_content"
+              android:layout_toRightOf="&#64;+id/quickcontact"
+              android:gravity="center_vertical"
+              android:layout_alignParentRight="true"
+              android:layout_alignParentTop="true"/&gt;
+&lt;/RelativeLayout&gt;
+</pre>
+<p>
+    In the following sections, this file is referred to as <code>contact_item_layout.xml</code>.
+</p>
+<h3>Set up a custom CursorAdapter</h3>
+<p>
+    To bind a {@link android.support.v4.widget.CursorAdapter} to a {@link android.widget.ListView}
+    containing a {@link android.widget.QuickContactBadge}, define a custom adapter that
+    extends {@link android.support.v4.widget.CursorAdapter}. This approach allows you to process the
+    data in the {@link android.database.Cursor} before you bind it to the
+    {@link android.widget.QuickContactBadge}. This approach also allows you to bind multiple
+    {@link android.database.Cursor} columns to the {@link android.widget.QuickContactBadge}. Neither
+    of these operations is possible in a regular {@link android.support.v4.widget.CursorAdapter}.
+</p>
+<p>
+    The subclass of {@link android.support.v4.widget.CursorAdapter} that you define must
+    override the following methods:
+</p>
+<dl>
+    <dt>{@link android.support.v4.widget.CursorAdapter#newView CursorAdapter.newView()}</dt>
+    <dd>
+        Inflates a new {@link android.view.View} object to hold the item layout. In the override
+        of this method, store handles to the child {@link android.view.View} objects of the layout,
+        including the child {@link android.widget.QuickContactBadge}. By taking this approach, you
+        avoid having to get handles to the child {@link android.view.View} objects each time you
+        inflate a new layout.
+        <p>
+            You must override this method so you can get handles to the individual child
+            {@link android.view.View} objects. This technique allows you to control their binding in
+            {@link android.support.v4.widget.CursorAdapter#bindView CursorAdapter.bindView()}.
+        </p>
+    </dd>
+    <dt>{@link android.support.v4.widget.CursorAdapter#bindView CursorAdapter.bindView()}</dt>
+    <dd>
+        Moves data from the current {@link android.database.Cursor} row to the child
+        {@link android.view.View} objects of the item layout. You must override this method so
+        you can bind both the contact's URI and thumbnail to the
+        {@link android.widget.QuickContactBadge}. The default implementation only allows a 1-to-1
+        mapping between a column and a {@link android.view.View}
+    </dd>
+</dl>
+<p>
+    The following code snippet contains an example of a custom subclass of
+    {@link android.support.v4.widget.CursorAdapter}:
+</p>
+<h3>Define the custom list adapter</h3>
+<p>
+    Define the subclass of {@link android.support.v4.widget.CursorAdapter} including its
+    constructor, and override
+    {@link android.support.v4.widget.CursorAdapter#newView newView()} and
+    {@link android.support.v4.widget.CursorAdapter#bindView bindView()}:
+</p>
+<pre>
+    /**
+     *
+     *
+     */
+    private class ContactsAdapter extends CursorAdapter {
+        private LayoutInflater mInflater;
+        ...
+        public ContactsAdapter(Context context) {
+            super(context, null, 0);
+
+            /*
+             * Gets an inflater that can instantiate
+             * the ListView layout from the file.
+             */
+            mInflater = LayoutInflater.from(context);
+            ...
+        }
+        ...
+        /**
+         * Defines a class that hold resource IDs of each item layout
+         * row to prevent having to look them up each time data is
+         * bound to a row.
+         */
+        private class ViewHolder {
+            TextView displayname;
+            QuickContactBadge quickcontact;
+        }
+        ..
+        &#64;Override
+        public View newView(
+                Context context,
+                Cursor cursor,
+                ViewGroup viewGroup) {
+            /* Inflates the item layout. Stores resource IDs in a
+             * in a ViewHolder class to prevent having to look
+             * them up each time bindView() is called.
+             */
+            final View itemView =
+                    mInflater.inflate(
+                            R.layout.contact_list_layout,
+                            viewGroup,
+                            false
+                    );
+            final ViewHolder holder = new ViewHolder();
+            holder.displayname =
+                    (TextView) view.findViewById(R.id.displayname);
+            holder.quickcontact =
+                    (QuickContactBadge)
+                            view.findViewById(R.id.quickcontact);
+            view.setTag(holder);
+            return view;
+        }
+        ...
+        &#64;Override
+        public void bindView(
+                View view,
+                Context context,
+                Cursor cursor) {
+            final ViewHolder holder = (ViewHolder) view.getTag();
+            final String photoData =
+                    cursor.getString(mPhotoDataIndex);
+            final String displayName =
+                    cursor.getString(mDisplayNameIndex);
+            ...
+            // Sets the display name in the layout
+            holder.displayname = cursor.getString(mDisplayNameIndex);
+            ...
+            /*
+             * Generates a contact URI for the QuickContactBadge.
+             */
+            final Uri contactUri = Contacts.getLookupUri(
+                    cursor.getLong(mIdIndex),
+                    cursor.getString(mLookupKeyIndex));
+            holder.quickcontact.assignContactUri(contactUri);
+            String photoData = cursor.getString(mPhotoDataIndex);
+            /*
+             * Decodes the thumbnail file to a Bitmap.
+             * The method loadContactPhotoThumbnail() is defined
+             * in the section "Set the Contact URI and Thumbnail"
+             */
+            Bitmap thumbnailBitmap =
+                    loadContactPhotoThumbnail(photoData);
+            /*
+             * Sets the image in the QuickContactBadge
+             * QuickContactBadge inherits from ImageView
+             */
+            holder.quickcontact.setImageBitmap(thumbnailBitmap);
+    }
+</pre>
+
+<h3>Set up variables</h3>
+<p>
+    In your code, set up variables, including a {@link android.database.Cursor} projection that
+    includes the necessary columns.
+</p>
+<p class="note">
+    <strong>Note:</strong> The following code snippets use the method
+    <code>loadContactPhotoThumbnail()</code>, which is defined in the section
+    <a href="#SetURIThumbnail">Set the Contact URI and Thumbnail</a>
+</p>
+<p>
+    For example:
+</p>
+<pre>
+public class ContactsFragment extends Fragment implements
+        LoaderManager.LoaderCallbacks&lt;Cursor&gt; {
+...
+    // Defines a ListView
+    private ListView mListView;
+    // Defines a ContactsAdapter
+    private ContactsAdapter mAdapter;
+    ...
+    // Defines a Cursor to contain the retrieved data
+    private Cursor mCursor;
+    /*
+     * Defines a projection based on platform version. This ensures
+     * that you retrieve the correct columns.
+     */
+    private static final String[] PROJECTION =
+            {
+                Contacts._ID,
+                Contacts.LOOKUP_KEY,
+                (Build.VERSION.SDK_INT &gt;=
+                 Build.VERSION_CODES.HONEYCOMB) ?
+                        Contacts.DISPLAY_NAME_PRIMARY :
+                        Contacts.DISPLAY_NAME
+                (Build.VERSION.SDK_INT &gt;=
+                 Build.VERSION_CODES.HONEYCOMB) ?
+                        Contacts.PHOTO_THUMBNAIL_ID :
+                        /*
+                         * Although it's not necessary to include the
+                         * column twice, this keeps the number of
+                         * columns the same regardless of version
+                         */
+                        Contacts_ID
+                ...
+            };
+    /*
+     * As a shortcut, defines constants for the
+     * column indexes in the Cursor. The index is
+     * 0-based and always matches the column order
+     * in the projection.
+     */
+    // Column index of the _ID column
+    private int mIdIndex = 0;
+    // Column index of the LOOKUP_KEY column
+    private int mLookupKeyIndex = 1;
+    // Column index of the display name column
+    private int mDisplayNameIndex = 3;
+    /*
+     * Column index of the photo data column.
+     * It's PHOTO_THUMBNAIL_URI for Honeycomb and later,
+     * and _ID for previous versions.
+     */
+    private int mPhotoDataIndex =
+            Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.HONEYCOMB ?
+            3 :
+            0;
+    ...
+</pre>
+<h3>Set up the ListView</h3>
+<p>
+    In {@link android.support.v4.app.Fragment#onCreate Fragment.onCreate()}, instantiate the custom
+    cursor adapter and get a handle to the {@link android.widget.ListView}:
+</p>
+<pre>
+    &#64;Override
+    public void onCreate(Bundle savedInstanceState) {
+        ...
+        /*
+         * Instantiates the subclass of
+         * CursorAdapter
+         */
+        ContactsAdapter mContactsAdapter =
+                new ContactsAdapter(getActivity());
+        /*
+         * Gets a handle to the ListView in the file
+         * contact_list_layout.xml
+         */
+        mListView = (ListView) findViewById(R.layout.contact_list_layout);
+        ...
+    }
+    ...
+</pre>
+<p>
+    In {@link android.support.v4.app.Fragment#onActivityCreated onActivityCreated()}, bind the
+    <code>ContactsAdapter</code> to the {@link android.widget.ListView}:
+</p>
+<pre>
+    &#64;Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        ...
+        // Sets up the adapter for the ListView
+        mListView.setAdapter(mAdapter);
+        ...
+    }
+    ...
+</pre>
+<p>
+    When you get back a {@link android.database.Cursor} containing the contacts data, usually in
+    {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()},
+    call {@link android.support.v4.widget.CursorAdapter#swapCursor swapCursor()} to move the
+    {@link android.database.Cursor} data to the {@link android.widget.ListView}. This displays the
+    {@link android.widget.QuickContactBadge} for each entry in the list of contacts:
+</p>
+<pre>
+    public void onLoadFinished(Loader&lt;Cursor&gt; loader, Cursor cursor) {
+        // When the loader has completed, swap the cursor into the adapter.
+        mContactsAdapter.swapCursor(cursor);
+    }
+</pre>
+<p>
+    When you bind a {@link android.database.Cursor} to a
+    {@link android.widget.ListView} with a {@link android.support.v4.widget.CursorAdapter}
+    (or subclass), and you use a {@link android.support.v4.content.CursorLoader} to load the
+    {@link android.database.Cursor}, always clear references to the {@link android.database.Cursor}
+    in your implementation of
+    {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoaderReset onLoaderReset()}.
+    For example:
+</p>
+<pre>
+    &#64;Override
+    public void onLoaderReset(Loader&lt;Cursor&gt; loader) {
+        // Removes remaining reference to the previous Cursor
+        mContactsAdapter.swapCursor(null);
+    }
+</pre>
diff --git a/docs/html/training/contacts-provider/index.jd b/docs/html/training/contacts-provider/index.jd
new file mode 100644
index 0000000..f380d95
--- /dev/null
+++ b/docs/html/training/contacts-provider/index.jd
@@ -0,0 +1,97 @@
+page.title=Accessing Contacts Data
+
+trainingnavtop=true
+startpage=true
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<!-- Required platform, tools, add-ons, devices, knowledge, etc. -->
+<h2>Dependencies and prerequisites</h2>
+<ul>
+  <li>Android 2.0 (API Level 5) or higher</li>
+  <li>Experience in using {@link android.content.Intent} objects</li>
+  <li>Experience in using content providers</li>
+</ul>
+
+<!-- related docs (NOT javadocs) -->
+<h2>You should also read</h2>
+<ul>
+    <li>
+        <a href="{@docRoot}guide/topics/providers/content-provider-basics.html">
+        Content Provider Basics</a>
+    </li>
+    <li>
+        <a href="{@docRoot}guide/topics/providers/contacts-provider.html">
+        Contacts Provider</a>
+    </li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+    <a href="http://developer.android.com/shareables/training/ContactsList.zip" class="button">
+    Download the sample
+    </a>
+ <p class="filename">ContactsList.zip</p>
+</div>
+
+</div>
+</div>
+
+<p>
+    The <a href="{@docRoot}guide/topics/providers/contacts-provider.html">Contacts Provider</a> is
+    the central repository of the user's contacts information, including data from contacts apps and
+    social networking apps. In your apps, you can access Contacts Provider information directly by
+    calling {@link android.content.ContentResolver} methods or by sending intents to a contacts app.
+</p>
+<p>
+    This class focuses on retrieving lists of contacts, displaying the details for a particular
+    contact, and modifying contacts using intents. The basic techniques described
+    here can be extended to perform more complex tasks. In addition, this class helps you
+    understand the overall structure and operation of the
+    <a href="{@docRoot}guide/topics/providers/contacts-provider.html">Contacts Provider</a>.
+</p>
+<h2>Lessons</h2>
+ 
+<dl>
+    <dt>
+        <b><a href="retrieve-names.html">Retrieving a List of Contacts</a></b>
+    </dt>
+    <dd>
+        Learn how to retrieve a list of contacts for which the data matches all or part of a search
+        string, using the following techniques:
+        <ul>
+            <li>Match by contact name</li>
+            <li>Match any type of contact data</li>
+            <li>Match a specific type of contact data, such as a phone number</li>
+        </ul>
+    </dd>
+    <dt>
+        <b><a href="retrieve-details.html">Retrieving Details for a Contact</a></b>
+    </dt>
+    <dd>
+        Learn how to retrieve the details for a single contact. A contact's details are data
+        such as phone numbers and email addresses. You can retrieve all details, or you can
+        retrieve details of a specific type, such as all email addresses.
+    </dd>
+    <dt>
+        <b><a href="modify-data.html">Modifying Contacts Using Intents</a></b>
+    </dt>
+    <dd>
+        Learn how to modify a contact by sending an intent to the People app.
+    </dd>
+    <dt>
+        <b>
+            <a href="display-contact-badge.html">Displaying the Quick Contact Badge</a>
+        </b>
+    </dt>
+    <dd>
+        Learn how to display the {@link android.widget.QuickContactBadge} widget. When the user
+        clicks the contact badge widget, a dialog opens that displays the contact's details and
+        action buttons for apps that can handle the details. For example, if the contact has an
+        email address, the dialog displays an action button for the default email app.
+    </dd>
+</dl>
diff --git a/docs/html/training/contacts-provider/modify-data.jd b/docs/html/training/contacts-provider/modify-data.jd
new file mode 100644
index 0000000..64853ef
--- /dev/null
+++ b/docs/html/training/contacts-provider/modify-data.jd
@@ -0,0 +1,305 @@
+page.title=Modifying Contacts Using Intents
+trainingnavtop=true
+@jd:body
+<div id="tb-wrapper">
+<div id="tb">
+
+<!-- table of contents -->
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li><a href="#InsertContact">Insert a New Contact Using an Intent</a></li>
+  <li><a href="#EditContact">Edit an Existing Contact Using an Intent</a></li>
+  <li><a href="#InsertEdit">Let Users Choose to Insert or Edit Using an Intent</a>
+</ol>
+<h2>You should also read</h2>
+<ul>
+    <li>
+        <a href="{@docRoot}guide/topics/providers/content-provider-basics.html">
+        Content Provider Basics
+        </a>
+    </li>
+    <li>
+        <a href="{@docRoot}guide/topics/providers/contacts-provider.html">
+        Contacts Provider
+        </a>
+    </li>
+    <li>
+        <a href="{@docRoot}guide/components/intents-filters.html">Intents and Intent Filters</a>
+    </li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+    <a href="http://developer.android.com/shareables/training/ContactsList.zip" class="button">
+    Download the sample
+    </a>
+ <p class="filename">ContactsList.zip</p>
+</div>
+
+</div>
+</div>
+<p>
+    This lesson shows you how to use an {@link android.content.Intent} to insert a new contact or
+    modify a contact's data. Instead of accessing the Contacts Provider directly, an
+    {@link android.content.Intent} starts the contacts app, which runs the appropriate
+    {@link android.app.Activity}. For the modification actions described in this lesson,
+    if you send extended data in the {@link android.content.Intent} it's entered into the UI of the
+    {@link android.app.Activity} that is started.
+</p>
+<p>
+    Using an {@link android.content.Intent} to insert or update a single contact is the preferred
+    way of modifying the Contacts Provider, for the following reasons:
+</p>
+<ul>
+    <li>It saves you the time and and effort of developing your own UI and code.</li>
+    <li>
+        It avoids introducing errors caused by modifications that don't follow the
+        Contacts Provider's rules.
+    </li>
+    <li>
+        It reduces the number of permissions you need to request. Your app doesn't need permission
+        to write to the Contacts Provider, because it delegates modifications to the contacts app,
+        which already has that permission.
+    </li>
+</ul>
+<h2 id="InsertContact">Insert a New Contact Using an Intent</h2>
+<p>
+    You often want to allow the user to insert a new contact when your app receives new data. For
+    example, a restaurant review app can allow users to add the restaurant as a contact as they're
+    reviewing it. To do this using an intent, create the intent using as much data as you have
+    available, and then send the intent to the contacts app.
+</p>
+<p>
+    Inserting a contact using the contacts app inserts a new <em>raw</em> contact into the Contacts
+    Provider's {@link android.provider.ContactsContract.RawContacts} table.  If necessary,
+    the contacts app prompts users for the account type and account to use when creating the raw
+    contact. The contacts app also notifies users if the raw contact already exists. Users then have
+    option of canceling the insertion, in which case no contact is created. To learn
+    more about raw contacts, see the
+    <a href="{@docRoot}guide/topics/providers/contacts-provider.html">Contacts Provider</a>
+    API guide.
+</p>
+
+<h3>Create an Intent</h3>
+<p>
+    To start, create a new {@link android.content.Intent} object with the action
+    {@link android.provider.ContactsContract.Intents.Insert#ACTION Intents.Insert.ACTION}.
+    Set the MIME type to {@link android.provider.ContactsContract.RawContacts#CONTENT_TYPE
+    RawContacts.CONTENT_TYPE}. For example:
+</p>
+<pre>
+...
+// Creates a new Intent to insert a contact
+Intent intent = new Intent(Intents.Insert.ACTION);
+// Sets the MIME type to match the Contacts Provider
+intent.setType(ContactsContract.RawContacts.CONTENT_TYPE);
+</pre>
+<p>
+    If you already have details for the contact, such as a phone number or email address, you can
+    insert them into the intent as extended data. For a key value, use the appropriate constant from
+    {@link android.provider.ContactsContract.Intents.Insert Intents.Insert}. The contacts app
+    displays the data in its insert screen, allowing users to make further edits and additions.
+</p>
+<pre>
+/* Assumes EditText fields in your UI contain an email address
+ * and a phone number.
+ *
+ */
+private EditText mEmailAddress = (EditText) findViewById(R.id.email);
+private EditText mPhoneNumber = (EditText) findViewById(R.id.phone);
+...
+/*
+ * Inserts new data into the Intent. This data is passed to the
+ * contacts app's Insert screen
+ */
+// Inserts an email address
+intent.putExtra(Intents.Insert.EMAIL, mEmailAddress.getText())
+/*
+ * In this example, sets the email type to be a work email.
+ * You can set other email types as necessary.
+ */
+      .putExtra(Intents.Insert.EMAIL_TYPE, CommonDataKinds.Email.TYPE_WORK)
+// Inserts a phone number
+      .putExtra(Intents.Insert.PHONE, mPhoneNumber.getText())
+/*
+ * In this example, sets the phone type to be a work phone.
+ * You can set other phone types as necessary.
+ */
+      .putExtra(Intents.Insert.PHONE_TYPE, Phone.TYPE_WORK);
+
+</pre>
+<p>
+    Once you've created the {@link android.content.Intent}, send it by calling
+    {@link android.support.v4.app.Fragment#startActivity startActivity()}.
+</p>
+<pre>
+    /* Sends the Intent
+     */
+    startActivity(intent);
+</pre>
+<p>
+    This call opens a screen in the contacts app that allows users to enter a new contact. The
+    account type and account name for the contact is listed at the top of the screen. Once users
+    enter the data and click <i>Done</i>, the contacts app's contact list appears. Users return to
+    your app by clicking <i>Back</i>.
+</p>
+<h2 id="EditContact">Edit an Existing Contact Using an Intent</h2>
+<p>
+    Editing an existing contact using an {@link android.content.Intent} is useful if the user
+    has already chosen a contact of interest. For example, an app that finds contacts that have
+    postal addresses but lack a postal code could give users the option of looking up the code and
+    then adding it to the contact.
+</p>
+<p>
+    To edit an existing contact using an intent, use a procedure similar to
+    inserting a contact. Create an intent as described in the section
+    <a href="#InsertContact">Insert a New Contact Using an Intent</a>, but add the contact's
+    {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI
+    Contacts.CONTENT_LOOKUP_URI} and the MIME type
+    {@link android.provider.ContactsContract.Contacts#CONTENT_ITEM_TYPE
+    Contacts.CONTENT_ITEM_TYPE} to the intent. If you want to edit the contact with details you
+    already have, you can put them in the intent's extended data. Notice that some
+    name columns can't be edited using an intent; these columns are listed in the summary
+    section of the API reference for the class {@link android.provider.ContactsContract.Contacts}
+    under the heading "Update".
+</p>
+<p>
+    Finally, send the intent. In response, the contacts app displays an edit screen. When the user
+    finishes editing and saves the edits, the contacts app displays a contact list. When the user
+    clicks <i>Back</i>, your app is displayed.
+</p>
+<div class="sidebox-wrapper">
+<div class="sidebox">
+    <h2>Contacts Lookup Key</h2>
+    <p>
+        A contact's {@link android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY} value is
+        the identifier that you should use to retrieve a contact. It remains constant,
+        even if the provider changes the contact's row ID to handle internal operations.
+    </p>
+</div>
+</div>
+<h3>Create the Intent</h3>
+<p>
+    To edit a contact, call {@link android.content.Intent#Intent Intent(action)} to
+    create an intent with the action {@link android.content.Intent#ACTION_EDIT}. Call
+    {@link android.content.Intent#setDataAndType setDataAndType()} to set the data value for the
+    intent to the contact's {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI
+    Contacts.CONTENT_LOOKUP_URI} and the MIME type to
+    {@link android.provider.ContactsContract.Contacts#CONTENT_ITEM_TYPE
+    Contacts.CONTENT_ITEM_TYPE} MIME type; because a call to
+    {@link android.content.Intent#setType setType()} overwrites the current data value for the
+    {@link android.content.Intent}, you must set the data and the MIME type at the same time.
+</p>
+<p>
+    To get a contact's {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI
+    Contacts.CONTENT_LOOKUP_URI}, call
+    {@link android.provider.ContactsContract.Contacts#getLookupUri
+    Contacts.getLookupUri(id, lookupkey)} with the contact's
+    {@link android.provider.ContactsContract.Contacts#_ID Contacts._ID} and
+    {@link android.provider.ContactsContract.Contacts#LOOKUP_KEY Contacts.LOOKUP_KEY} values as
+    arguments.
+</p>
+<p>
+    The following snippet shows you how to create an intent:
+</p>
+<pre>
+    // The Cursor that contains the Contact row
+    public Cursor mCursor;
+    // The index of the lookup key column in the cursor
+    public int mLookupKeyIndex;
+    // The index of the contact's _ID value
+    public int mIdIndex;
+    // The lookup key from the Cursor
+    public String mCurrentLookupKey;
+    // The _ID value from the Cursor
+    public long mCurrentId;
+    // A content URI pointing to the contact
+    Uri mSelectedContactUri;
+    ...
+    /*
+     * Once the user has selected a contact to edit,
+     * this gets the contact's lookup key and _ID values from the
+     * cursor and creates the necessary URI.
+     */
+    // Gets the lookup key column index
+    mLookupKeyIndex = mCursor.getColumnIndex(Contacts.LOOKUP_KEY);
+    // Gets the lookup key value
+    mCurrentLookupKey = mCursor.getString(mLookupKeyIndex);
+    // Gets the _ID column index
+    mIdIndex = mCursor.getColumnIndex(Contacts._ID);
+    mCurrentId = mCursor.getLong(mIdIndex);
+    mSelectedContactUri =
+            Contacts.getLookupUri(mCurrentId, mCurrentLookupKey);
+    ...
+    // Creates a new Intent to edit a contact
+    Intent editIntent = new Intent(Intent.ACTION_EDIT);
+    /*
+     * Sets the contact URI to edit, and the data type that the
+     * Intent must match
+     */
+    editIntent.setDataAndType(mSelectedContactUri,Contacts.CONTENT_ITEM_TYPE);
+</pre>
+<h3>Add the navigation flag</h3>
+<p>
+    In Android 4.0 (API version 14) and later, a problem in the contacts app causes incorrect
+    navigation. When your app sends an edit intent to the contacts app, and users edit and save a
+    contact, when they click <i>Back</i> they see the contacts list screen. To navigate back to
+    your app, they have to click <i>Recents</i> and choose your app.
+</p>
+<p>
+    To work around this problem in Android 4.0.3 (API version 15) and later, add the extended
+    data key {@code finishActivityOnSaveCompleted} to the intent, with a value of {@code true}.
+    Android versions prior to Android 4.0 accept this key, but it has no effect. To set the
+    extended data, do the following:
+</p>
+<pre>
+    // Sets the special extended data for navigation
+    editIntent.putExtra("finishActivityOnSaveCompleted", true);
+</pre>
+<h3>Add other extended data</h3>
+<p>
+    To add additional extended data to the {@link android.content.Intent}, call
+    {@link android.content.Intent#putExtra putExtra()} as desired.
+    You can add extended data for common contact fields by using the key values specified in
+    {@link android.provider.ContactsContract.Intents.Insert Intents.Insert}. Remember that some
+    columns in the {@link android.provider.ContactsContract.Contacts} table can't be modified.
+    These columns are listed in the summary section of the API reference for the class
+    {@link android.provider.ContactsContract.Contacts} under the heading "Update".
+</p>
+
+<h3>Send the Intent</h3>
+<p>
+    Finally, send the intent you've constructed. For example:
+</p>
+<pre>
+    // Sends the Intent
+    startActivity(editIntent);
+</pre>
+<h2 id="InsertEdit">Let Users Choose to Insert or Edit Using an Intent</h2>
+<p>
+    You can allow users to choose whether to insert a contact or edit an existing one by sending
+    an {@link android.content.Intent} with the action
+    {@link android.content.Intent#ACTION_INSERT_OR_EDIT}. For example, an email client app could
+    allow users to add an incoming email address to a new contact, or add it as an additional
+    address for an existing contact. Set the MIME type for this intent to
+    {@link android.provider.ContactsContract.Contacts#CONTENT_ITEM_TYPE Contacts.CONTENT_ITEM_TYPE},
+    but don't set the data URI.
+</p>
+<p>
+    When you send this intent, the contacts app displays a list of contacts.
+    Users can either insert a new contact or pick an existing contact and edit it.
+    Any extended data fields you add to the intent populates the screen that appears. You can use
+    any of the key values specified in {@link android.provider.ContactsContract.Intents.Insert
+    Intents.Insert}. The following code snippet shows how to construct and send the intent:
+</p>
+<pre>
+    // Creates a new Intent to insert or edit a contact
+    Intent intentInsertEdit = new Intent(Intent.ACTION_INSERT_OR_EDIT);
+    // Sets the MIME type
+    intentInsertEdit.setType(Contacts.CONTENT_ITEM_TYPE);
+    // Add code here to insert extended data, if desired
+    ...
+    // Sends the Intent with an request ID
+    startActivity(intentInsertEdit);
+</pre>
diff --git a/docs/html/training/contacts-provider/retrieve-details.jd b/docs/html/training/contacts-provider/retrieve-details.jd
new file mode 100644
index 0000000..0de3b67
--- /dev/null
+++ b/docs/html/training/contacts-provider/retrieve-details.jd
@@ -0,0 +1,378 @@
+page.title=Retrieving Details for a Contact
+
+trainingnavtop=true
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<!-- table of contents -->
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li><a href="#RetrieveAll">Retrieve All Details for a Contact</a></li>
+  <li><a href="#RetrieveSpecific">Retrieve Specific Details for a Contact</a></li>
+</ol>
+
+<!-- other docs (NOT javadocs) -->
+<h2>You should also read</h2>
+<ul>
+    <li>
+        <a href="{@docRoot}guide/topics/providers/content-provider-basics.html">
+        Content Provider Basics</a>
+    </li>
+    <li>
+        <a href="{@docRoot}guide/topics/providers/contacts-provider.html">
+        Contacts Provider</a>
+    </li>
+    <li>
+        <a href="{@docRoot}guide/components/loaders.html">Loaders</a>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+    <a href="http://developer.android.com/shareables/training/ContactsList.zip" class="button">
+    Download the sample
+    </a>
+ <p class="filename">ContactsList.zip</p>
+</div>
+
+</div>
+</div>
+<p>
+    This lesson shows how to retrieve detail data for a contact, such as email addresses, phone
+    numbers, and so forth. It's the details that users are looking for when they retrieve a contact.
+    You can give them all the details for a contact, or only display details of a particular type,
+    such as email addresses.
+</p>
+<p>
+    The steps in this lesson assume that you already have a
+    {@link android.provider.ContactsContract.Contacts} row for a contact the user has picked.
+    The <a href="retrieve-names.html">Retrieving Contact Names</a> lesson shows how to
+    retrieve a list of contacts.
+</p>
+<h2 id="RetrieveAll">Retrieve All Details for a Contact</h2>
+<p>
+    To retrieve all the details for a contact, search the
+    {@link android.provider.ContactsContract.Data} table for any rows that contain the contact's
+    {@link android.provider.ContactsContract.Data#LOOKUP_KEY}. This column is available in
+    the {@link android.provider.ContactsContract.Data} table, because the Contacts
+    Provider makes an implicit join between the {@link android.provider.ContactsContract.Contacts}
+    table and the {@link android.provider.ContactsContract.Data} table. The
+    {@link android.provider.ContactsContract.Contacts#LOOKUP_KEY} column is described
+    in more detail in the <a href="retrieve-names.html">Retrieving Contact Names</a> lesson.
+</p>
+<p class="note">
+    <strong>Note:</strong> Retrieving all the details for a contact reduces the performance of a
+    device, because it needs to retrieve all of the columns in the
+    {@link android.provider.ContactsContract.Data} table. Consider the performance impact before
+    you use this technique.
+</p>
+<h3>Request permissions</h3>
+<p>
+    To read from the Contacts Provider, your app must have
+    {@link android.Manifest.permission#READ_CONTACTS READ_CONTACTS} permission.
+    To request this permission, add the following child element of
+    <code><a href="{@docRoot}guide/topics/manifest/manifest-element.html">
+    &lt;manifest&gt;</a></code> to your manifest file:
+</p>
+<pre>
+    &lt;uses-permission android:name="android.permission.READ_CONTACTS" /&gt;
+</pre>
+<h3>Set up a projection</h3>
+<p>
+    Depending on the data type a row contains, it may use only a few columns or many. In addition,
+    the data is in different columns depending on the data type.
+    To ensure you get all the possible columns for all possible data types, you need to add all the
+    column names to your projection. Always retrieve
+    {@link android.provider.ContactsContract.Data#_ID Data._ID} if you're binding the result
+    {@link android.database.Cursor} to a {@link android.widget.ListView}; otherwise, the binding
+    won't work. Also retrieve {@link android.provider.ContactsContract.Data#MIMETYPE Data.MIMETYPE}
+    so you can identify the data type of each row you retrieve. For example:
+</p>
+<pre>
+    private static final String PROJECTION =
+            {
+                Data._ID,
+                Data.MIMETYPE,
+                Data.DATA1,
+                Data.DATA2,
+                Data.DATA3,
+                Data.DATA4,
+                Data.DATA5,
+                Data.DATA6,
+                Data.DATA7,
+                Data.DATA8,
+                Data.DATA9,
+                Data.DATA10,
+                Data.DATA11,
+                Data.DATA12,
+                Data.DATA13,
+                Data.DATA14,
+                Data.DATA15
+            };
+</pre>
+<p>
+    This projection retrieves all the columns for a row in the
+    {@link android.provider.ContactsContract.Data} table, using the column names defined in
+    the {@link android.provider.ContactsContract.Data} class.
+</p>
+<p>
+    Optionally, you can also use any other column constants defined in or inherited by the
+    {@link android.provider.ContactsContract.Data} class. Notice, however, that the columns
+    {@link android.provider.ContactsContract.DataColumns#SYNC1} through
+    {@link android.provider.ContactsContract.DataColumns#SYNC4} are meant to be used by sync
+    adapters, so their data is not useful.
+</p>
+<h3>Define the selection criteria</h3>
+<p>
+    Define a constant for your selection clause, an array to hold selection arguments, and a
+    variable to hold the selection value. Use
+    the {@link android.provider.ContactsContract.Contacts#LOOKUP_KEY Contacts.LOOKUP_KEY} column to
+    find the contact. For example:
+</p>
+<pre>
+    // Defines the selection clause
+    private static final String SELECTION = Data.LOOKUP_KEY + " = ?";
+    // Defines the array to hold the search criteria
+    private String[] mSelectionArgs = { "" };
+    /*
+     * Defines a variable to contain the selection value. Once you
+     * have the Cursor from the Contacts table, and you've selected
+     * the desired row, move the row's LOOKUP_KEY value into this
+     * variable.
+     */
+    private String mLookupKey;
+</pre>
+<p>
+    Using "?" as a placeholder in your selection text expression ensures that the resulting search
+    is generated by binding rather than SQL compilation. This approach eliminates the
+    possibility of malicious SQL injection.
+</p>
+<h3>Define the sort order</h3>
+<p>
+    Define the sort order you want in the resulting {@link android.database.Cursor}. To
+    keep all rows for a particular data type together, sort by
+    {@link android.provider.ContactsContract.Data#MIMETYPE Data.MIMETYPE}. This query argument
+    groups all email rows together, all phone rows together, and so forth. For example:
+</p>
+<pre>
+    /*
+     * Defines a string that specifies a sort order of MIME type
+     */
+    private static final String SORT_ORDER = Data.MIMETYPE;
+</pre>
+<p class="note">
+    <strong>Note:</strong> Some data types don't use a subtype, so you can't sort on subtype.
+    Instead, you have to iterate through the returned {@link android.database.Cursor},
+    determine the data type of the current row, and store data for rows that use a subtype. When
+    you finish reading the cursor, you can then sort each data type by subtype and display the
+    results.
+</p>
+<h3>Initialize the Loader</h3>
+<p>
+   Always do retrievals from the Contacts Provider (and all other content providers) in a
+   background thread. Use the Loader framework defined by the
+   {@link android.support.v4.app.LoaderManager} class and the
+   {@link android.support.v4.app.LoaderManager.LoaderCallbacks} interface to do background
+   retrievals.
+</p>
+<p>
+    When you're ready to retrieve the rows, initialize the loader framework by
+    calling {@link android.support.v4.app.LoaderManager#initLoader initLoader()}. Pass an
+    integer identifier to the method; this identifier is passed to
+    {@link android.support.v4.app.LoaderManager.LoaderCallbacks} methods. The identifier helps you
+    use multiple loaders in an app by allowing you to differentiate between them.
+</p>
+<p>
+    The following snippet shows how to initialize the loader framework:
+</p>
+<pre>
+public class DetailsFragment extends Fragment implements
+        LoaderManager.LoaderCallbacks&lt;Cursor&gt; {
+    ...
+    // Defines a constant that identifies the loader
+    DETAILS_QUERY_ID = 0;
+    ...
+    /*
+     * Invoked when the parent Activity is instantiated
+     * and the Fragment's UI is ready. Put final initialization
+     * steps here.
+     */
+    &#64;Override
+    onActivityCreated(Bundle savedInstanceState) {
+        ...
+        // Initializes the loader framework
+        getLoaderManager().initLoader(DETAILS_QUERY_ID, null, this);
+</pre>
+<h3>Implement onCreateLoader()</h3>
+<p>
+    Implement the {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onCreateLoader
+    onCreateLoader()} method, which is called by the loader framework immediately after you call
+    {@link android.support.v4.app.LoaderManager#initLoader initLoader()}. Return a
+    {@link android.support.v4.content.CursorLoader} from this method. Since you're searching
+    the {@link android.provider.ContactsContract.Data} table, use the constant
+    {@link android.provider.ContactsContract.Data#CONTENT_URI Data.CONTENT_URI} as the content URI.
+    For example:
+</p>
+<pre>
+    &#64;Override
+    public Loader&lt;Cursor&gt; onCreateLoader(int loaderId, Bundle args) {
+        // Choose the proper action
+        switch (loaderId) {
+            case DETAILS_QUERY_ID:
+            // Assigns the selection parameter
+            mSelectionArgs[0] = mLookupKey;
+            // Starts the query
+            CursorLoader mLoader =
+                    new CursorLoader(
+                            getActivity(),
+                            Data.CONTENT_URI,
+                            PROJECTION,
+                            SELECTION,
+                            mSelectionArgs,
+                            SORT_ORDER
+                    );
+            ...
+    }
+</pre>
+<h3>Implement onLoadFinished() and onLoaderReset()</h3>
+<p>
+    Implement the
+    {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()}
+    method. The loader framework calls
+    {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()}
+    when the Contacts Provider returns the results of the query. For example:
+</p>
+<pre>
+    public void onLoadFinished(Loader&lt;Cursor&gt; loader, Cursor cursor) {
+        switch (loader.getId()) {
+            case DETAILS_QUERY_ID:
+                    /*
+                     * Process the resulting Cursor here.
+                     */
+                }
+                break;
+            ...
+        }
+    }
+</pre>
+<p>
+<p>
+    The method {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoaderReset
+    onLoaderReset()} is invoked when the loader framework detects that the data backing the result
+    {@link android.database.Cursor} has changed. At this point, remove any existing references
+    to the {@link android.database.Cursor} by setting them to null. If you don't, the loader
+    framework won't destroy the old {@link android.database.Cursor}, and you'll get a memory
+    leak. For example:
+<pre>
+    &#64;Override
+    public void onLoaderReset(Loader&lt;Cursor&gt; loader) {
+        switch (loader.getId()) {
+            case DETAILS_QUERY_ID:
+                /*
+                 * If you have current references to the Cursor,
+                 * remove them here.
+                 */
+                }
+                break;
+    }
+</pre>
+<h2 id="RetrieveSpecific">Retrieve Specific Details for a Contact</h2>
+<p>
+    Retrieving a specific data type for a contact, such as all the emails, follows the same pattern
+    as retrieving all details. These are the only changes you need to make to the code
+    listed in <a href="#RetrieveAll">Retrieve All Details for a Contact</a>:
+</p>
+<dl>
+    <dt>
+        Projection
+    </dt>
+    <dd>
+        Modify your projection to retrieve the columns that are specific to the
+        data type. Also modify the projection to use the column name constants defined in the
+        {@link android.provider.ContactsContract.CommonDataKinds} subclass corresponding to the
+        data type.
+    </dd>
+    <dt>
+        Selection
+    </dt>
+    <dd>
+        Modify the selection text to search for the
+        {@link android.provider.ContactsContract.Data#MIMETYPE MIMETYPE} value that's specific to
+        your data type.
+    </dd>
+    <dt>
+        Sort order
+    </dt>
+    <dd>
+        Since you're only selecting a single detail type, don't group the returned
+        {@link android.database.Cursor} by {@link android.provider.ContactsContract.Data#MIMETYPE
+        Data.MIMETYPE}.
+    </dd>
+</dl>
+<p>
+    These modifications are described in the following sections.
+</p>
+<h3>Define a projection</h3>
+<p>
+    Define the columns you want to retrieve, using the column name constants in the subclass
+    of {@link android.provider.ContactsContract.CommonDataKinds} for the data type.
+    If you plan to bind your {@link android.database.Cursor} to a {@link android.widget.ListView},
+    be sure to retrieve the <code>_ID</code> column. For example, to retrieve email data, define the
+    following projection:
+</p>
+<pre>
+    private static final String[] PROJECTION =
+            {
+                Email._ID,
+                Email.ADDRESS,
+                Email.TYPE,
+                Email.LABEL
+            };
+</pre>
+<p>
+    Notice that this projection uses the column names defined in the class
+    {@link android.provider.ContactsContract.CommonDataKinds.Email}, instead of the column names
+    defined in the class {@link android.provider.ContactsContract.Data}. Using the email-specific
+    column names makes the code more readable.
+</p>
+<p>
+    In the projection, you can also use any of the other columns defined in the
+    {@link android.provider.ContactsContract.CommonDataKinds} subclass.
+</p>
+<h3>Define selection criteria</h3>
+<p>
+    Define a search text expression that retrieves rows for a specific contact's
+    {@link android.provider.ContactsContract.Data#LOOKUP_KEY} and the
+    {@link android.provider.ContactsContract.Data#MIMETYPE Data.MIMETYPE} of the details you
+    want. Enclose the {@link android.provider.ContactsContract.Data#MIMETYPE MIMETYPE} value in
+    single quotes by concatenating a "<code>'</code>" (single-quote) character to the start and end
+    of the constant; otherwise, the provider interprets the constant as a variable name rather
+    than as a string value. You don't need to use a placeholder for this value, because you're
+    using a constant rather than a user-supplied value. For example:
+</p>
+<pre>
+    /*
+     * Defines the selection clause. Search for a lookup key
+     * and the Email MIME type
+     */
+    private static final String SELECTION =
+            Data.LOOKUP_KEY + " = ?" +
+            " AND " +
+            Data.MIMETYPE + " = " +
+            "'" + Email.CONTENT_ITEM_TYPE + "'";
+    // Defines the array to hold the search criteria
+    private String[] mSelectionArgs = { "" };
+</pre>
+<h3>Define a sort order</h3>
+<p>
+    Define a sort order for the returned {@link android.database.Cursor}. Since you're retrieving a
+    specific data type, omit the sort on {@link android.provider.ContactsContract.Data#MIMETYPE}.
+    Instead, if the type of detail data you're searching includes a subtype, sort on it.
+    For example, for email data you can sort on
+    {@link android.provider.ContactsContract.CommonDataKinds.Email#TYPE Email.TYPE}:
+</p>
+<pre>
+    private static final String SORT_ORDER = Email.TYPE + " ASC ";
+</pre>
diff --git a/docs/html/training/contacts-provider/retrieve-names.jd b/docs/html/training/contacts-provider/retrieve-names.jd
new file mode 100644
index 0000000..b034a6a
--- /dev/null
+++ b/docs/html/training/contacts-provider/retrieve-names.jd
@@ -0,0 +1,815 @@
+page.title=Retrieving a List of Contacts
+
+trainingnavtop=true
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<!-- table of contents -->
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li><a href="#Permissions">Request Permission to Read the Provider</a>
+  <li><a href="#NameMatch">Match a Contact by Name and List the Results</a></li>
+  <li><a href="#TypeMatch">Match a Contact By a Specific Type of Data</a></li>
+  <li><a href="#GeneralMatch">Match a Contact By Any Type of Data</a></li>
+</ol>
+
+<!-- other docs (NOT javadocs) -->
+<h2>You should also read</h2>
+<ul>
+    <li>
+        <a href="{@docRoot}guide/topics/providers/content-provider-basics.html">
+        Content Provider Basics</a>
+    </li>
+    <li>
+        <a href="{@docRoot}guide/topics/providers/contacts-provider.html">
+        Contacts Provider</a>
+    </li>
+    <li>
+        <a href="{@docRoot}guide/components/loaders.html">Loaders</a>
+    </li>
+    <li>
+        <a href="{@docRoot}guide/topics/search/search-dialog.html">Creating a Search Interface</a>
+    </li>
+</ul>
+
+<h2>Try it out</h2>
+
+<div class="download-box">
+    <a href="http://developer.android.com/shareables/training/ContactsList.zip" class="button">
+    Download the sample
+    </a>
+ <p class="filename">ContactsList.zip</p>
+</div>
+
+</div>
+</div>
+<p>
+    This lesson shows you how to retrieve a list of contacts whose data matches all or part of a
+    search string, using the following techniques:
+</p>
+<dl>
+    <dt>Match contact names</dt>
+    <dd>
+        Retrieve a list of contacts by matching the search string to all or part of the contact
+        name data. The Contacts Provider allows multiple instances of the same name, so this
+        technique can return a list of matches.
+    </dd>
+    <dt>Match a specific type of data, such as a phone number</dt>
+    <dd>
+        Retrieve a list of contacts by matching the search string to a particular type of detail
+        data such as an email address. For example, this technique allows you to list all of the
+        contacts whose email address matches the search string.
+    </dd>
+    <dt>Match any type of data</dt>
+    <dd>
+        Retrieve a list of contacts by matching the search string to any type of detail data,
+        including name, phone number, street address, email address, and so forth. For example,
+        this technique allows you to accept any type of data for a search string and then list the
+        contacts for which the data matches the string.
+    </dd>
+</dl>
+<p class="note">
+    <strong>Note:</strong> All the examples in this lesson use a
+    {@link android.support.v4.content.CursorLoader} to retrieve data from the Contacts
+    Provider. A {@link android.support.v4.content.CursorLoader} runs its query on a
+    thread that's separate from the UI thread. This ensures that the query doesn't slow down UI
+    response times and cause a poor user experience. For more information, see the Android
+    training class <a href="{@docRoot}training/load-data-background/index.html">
+    Loading Data in the Background</a>.
+</p>
+<h2 id="Permissions">Request Permission to Read the Provider</h2>
+<p>
+    To do any type of search of the Contacts Provider, your app must have
+    {@link android.Manifest.permission#READ_CONTACTS READ_CONTACTS} permission.
+    To request this, add this
+<code><a href="{@docRoot}guide/topics/manifest/uses-permission-element.html">&lt;uses-permission&gt;</a></code>
+    element to your manifest file as a child element of
+<code><a href="{@docRoot}guide/topics/manifest/manifest-element.html">&lt;manifest&gt;</a></code>:
+</p>
+<pre>
+    &lt;uses-permission android:name="android.permission.READ_CONTACTS" /&gt;
+</pre>
+<h2 id="NameMatch">Match a Contact by Name and List the Results</h2>
+<p>
+    This technique tries to match a search string to the name of a contact or contacts in the
+    Contact Provider's {@link android.provider.ContactsContract.Contacts} table. You usually want
+    to display the results in a {@link android.widget.ListView}, to allow the user to choose among
+    the matched contacts.
+</p>
+<h3 id="DefineListView">Define ListView and item layouts</h3>
+<p>
+    To display the search results in a {@link android.widget.ListView}, you need a main layout file
+    that defines the entire UI including the {@link android.widget.ListView}, and an item layout
+    file that defines one line of the {@link android.widget.ListView}. For example, you can define
+    the main layout file <code>res/layout/contacts_list_view.xml</code> that contains the
+    following XML:
+</p>
+<pre>
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;ListView xmlns:android="http://schemas.android.com/apk/res/android"
+          android:id="&#64;android:id/list"
+          android:layout_width="match_parent"
+          android:layout_height="match_parent"/&gt;
+</pre>
+<p>
+    This XML uses the built-in Android {@link android.widget.ListView} widget
+    {@link android.R.id#list android:id/list}.
+</p>
+<p>
+    Define the item layout file <code>contacts_list_item.xml</code> with the following XML:
+</p>
+<pre>
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;TextView xmlns:android="http://schemas.android.com/apk/res/android"
+          android:id="&#64;android:id/text1"
+          android:layout_width="match_parent"
+          android:layout_height="wrap_content"
+          android:clickable="true"/&gt;
+</pre>
+<p>
+    This XML uses the built-in Android {@link android.widget.TextView} widget
+    {@link android.R.id#text1 android:text1}.
+</p>
+<p class="note">
+    <strong>Note:</strong> This lesson doesn't describe the UI for getting a search string from the
+    user, because you may want to get the string indirectly. For example, you can give the user
+    an option to search for contacts whose name matches a string in an incoming text message.
+</p>
+<p>
+    The two layout files you've written define a user interface that shows a
+    {@link android.widget.ListView}. The next step is to write code that uses this UI to display a
+    list of contacts.
+</p>
+<h3 id="Fragment">Define a Fragment that displays the list of contacts</h3>
+<p>
+    To display the list of contacts, start by defining a {@link android.support.v4.app.Fragment}
+    that's loaded by an {@link android.app.Activity}. Using a
+    {@link android.support.v4.app.Fragment} is a more flexible technique, because you can use
+    one {@link android.support.v4.app.Fragment} to display the list and a second
+    {@link android.support.v4.app.Fragment} to display the details for a contact that the user
+    chooses from the list. Using this approach, you can combine one of the techniques presented in
+    this lesson with one from the lesson <a href="retrieve-details.html">
+    Retrieving Details for a Contact</a>.
+</p>
+<p>
+    To learn how to use one or more {@link android.support.v4.app.Fragment} objects from an
+    an {@link android.app.Activity}, read the training class
+    <a href="{@docRoot}training/basics/fragments/index.html">
+    Building a Dynamic UI with Fragments</a>.
+</p>
+<p>
+    To help you write queries against the Contacts Provider, the Android framework provides a
+    contracts class called {@link android.provider.ContactsContract}, which defines useful
+    constants and methods for accessing the provider. When you use this class, you don't have to
+    define your own constants for content URIs, table names, or columns. To use this class,
+    include the following statement:
+</p>
+<pre>
+import android.provider.ContactsContract;
+</pre>
+<p>
+    Since the code uses a {@link android.support.v4.content.CursorLoader} to retrieve data
+    from the provider, you must specify that it implements the loader interface
+    {@link android.support.v4.app.LoaderManager.LoaderCallbacks}. Also, to help detect which contact
+    the user selects from the list of search results, implement the adapter interface
+    {@link android.widget.AdapterView.OnItemClickListener}. For example:
+</p>
+<pre>
+...
+import android.support.v4.app.Fragment;
+import android.support.v4.app.LoaderManager.LoaderCallbacks;
+import android.widget.AdapterView;
+...
+public class ContactsFragment extends Fragment implements
+        LoaderManager.LoaderCallbacks&lt;Cursor&gt;,
+        AdapterView.OnItemClickListener {
+</pre>
+<h3 id="DefineVariables">Define global variables</h3>
+<p>
+    Define global variables that are used in other parts of the code:
+</p>
+<pre>
+    ...
+    /*
+     * Defines an array that contains column names to move from
+     * the Cursor to the ListView.
+     */
+    &#64;SuppressLint("InlinedApi")
+    private final static String[] FROM_COLUMNS = {
+            Build.VERSION.SDK_INT
+                    &gt;= Build.VERSION_CODES.HONEYCOMB ?
+                    Contacts.DISPLAY_NAME_PRIMARY :
+                    Contacts.DISPLAY_NAME
+    };
+    /*
+     * Defines an array that contains resource ids for the layout views
+     * that get the Cursor column contents. The id is pre-defined in
+     * the Android framework, so it is prefaced with "android.R.id"
+     */
+    private final static int[] TO_IDS = {
+           android.R.id.text1
+    };
+    // Define global mutable variables
+    // Define a ListView object
+    ListView mContactsList;
+    // Define variables for the contact the user selects
+    // The contact's _ID value
+    long mContactId;
+    // The contact's LOOKUP_KEY
+    String mContactKey;
+    // A content URI for the selected contact
+    Uri mContactUri;
+    // An adapter that binds the result Cursor to the ListView
+    private SimpleCursorAdapter mCursorAdapter;
+    ...
+</pre>
+<p class="note">
+    <strong>Note:</strong> Since
+    {@link android.provider.ContactsContract.Contacts#DISPLAY_NAME_PRIMARY
+    Contacts.DISPLAY_NAME_PRIMARY} requires Android 3.0 (API version 11) or later, setting your
+    app's <code>minSdkVersion</code> to 10 or below generates an Android Lint warning in
+    Eclipse with ADK. To turn off this warning, add the annotation
+    <code>@SuppressLint("InlinedApi")</code> before the definition of <code>FROM_COLUMNS</code>.
+</p>
+<h3 id="InitializeFragment">Initialize the Fragment</h3>
+<p>
+
+    Initialize the {@link android.support.v4.app.Fragment}. Add the empty, public constructor
+    required by the Android system, and inflate the {@link android.support.v4.app.Fragment} object's
+    UI in the callback method {@link android.support.v4.app.Fragment#onCreateView onCreateView()}.
+    For example:
+</p>
+<pre>
+    // Empty public constructor, required by the system
+    public ContactsFragment() {}
+
+    // A UI Fragment must inflate its View
+    &#64;Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        // Inflate the fragment layout
+        return inflater.inflate(R.layout.contacts_list_layout, container, false);
+    }
+</pre>
+<h3 id="DefineAdapter">Set up the CursorAdapter for the ListView</h3>
+<p>
+    Set up the {@link android.support.v4.widget.SimpleCursorAdapter} that binds the results of the
+    search to the {@link android.widget.ListView}. To get the {@link android.widget.ListView} object
+    that displays the contacts, you need to call {@link android.app.Activity#findViewById
+    Activity.findViewById()} using the parent activity of the
+    {@link android.support.v4.app.Fragment}. Use the {@link android.content.Context} of the
+    parent activity when you call {@link android.widget.ListView#setAdapter setAdapter()}.
+    For example:
+</p>
+<pre>
+    public void onActivityCreated(Bundle savedInstanceState) {
+        super.onActivityCreated(savedInstanceState);
+        ...
+        // Gets the ListView from the View list of the parent activity
+        mContactsList = (ListView) getActivity().findViewById(R.layout.contact_list_view);
+        // Gets a CursorAdapter
+        mCursorAdapter = new SimpleCursorAdapter(
+                getActivity(),
+                R.layout.contact_list_item,
+                null,
+                FROM_COLUMNS, TO_IDS,
+                0);
+        // Sets the adapter for the ListView
+        mContactsList.setAdapter(mCursorAdapter);
+    }
+</pre>
+<h3 id="SetListener">Set the selected contact listener</h3>
+<p>
+    When you display the results of a search, you usually want to allow the user to select a
+    single contact for further processing. For example, when the user clicks a contact you can
+    display the contact's address on a map. To provide this feature, you first defined the current
+    {@link android.support.v4.app.Fragment} as the click listener by specifying that the class
+    implements {@link android.widget.AdapterView.OnItemClickListener}, as shown in the section
+    <a href="#Fragment">Define a Fragment that displays the list of contacts</a>.
+</p>
+<p>
+    To continue setting up the listener, bind it to the {@link android.widget.ListView} by
+    calling the method {@link android.widget.ListView#setOnItemClickListener
+    setOnItemClickListener()} in {@link android.support.v4.app.Fragment#onActivityCreated
+    onActivityCreated()}. For example:
+</p>
+<pre>
+    public void onActivityCreated(Bundle savedInstanceState) {
+        ...
+        // Set the item click listener to be the current fragment.
+        mContactsList.setOnItemClickListener(this);
+        ...
+    }
+</pre>
+<p>
+    Since you specified that the current {@link android.support.v4.app.Fragment} is the
+    {@link android.widget.AdapterView.OnItemClickListener OnItemClickListener} for the
+    {@link android.widget.ListView}, you now need to implement its required method
+    {@link android.widget.AdapterView.OnItemClickListener#onItemClick onItemClick()}, which
+    handles the click event. This is described in a succeeding section.
+</p>
+<h3 id="DefineProjection">Define a projection</h3>
+<p>
+    Define a constant that contains the columns you want to return from your query. Each item in
+    the {@link android.widget.ListView} displays the contact's display name,
+    which contains the main form of the contact's name. In Android 3.0 (API version 11) and later,
+    the name of this column is
+    {@link android.provider.ContactsContract.Contacts#DISPLAY_NAME_PRIMARY
+    Contacts.DISPLAY_NAME_PRIMARY}; in versions previous to that, its name is
+    {@link android.provider.ContactsContract.Contacts#DISPLAY_NAME Contacts.DISPLAY_NAME}.
+</p>
+<p>
+    The column {@link android.provider.ContactsContract.Contacts#_ID Contacts._ID} is used by the
+    {@link android.support.v4.widget.SimpleCursorAdapter} binding process.
+    {@link android.provider.ContactsContract.Contacts#_ID Contacts._ID} and
+    {@link android.provider.ContactsContract.Contacts#LOOKUP_KEY} are used together to
+    construct a content URI for the contact the user selects.
+</p>
+<pre>
+...
+&#64;SuppressLint("InlinedApi")
+private static final String[] PROJECTION =
+        {
+            Contacts._ID,
+            Contacts.LOOKUP_KEY,
+            Build.VERSION.SDK_INT
+                    &gt;= Build.VERSION_CODES.HONEYCOMB ?
+                    Contacts.DISPLAY_NAME_PRIMARY :
+                    Contacts.DISPLAY_NAME
+
+        };
+</pre>
+<h3 id="DefineConstants">Define constants for the Cursor column indexes</h3>
+<p>
+    To get data from an individual column in a {@link android.database.Cursor}, you need
+    the column's index within the {@link android.database.Cursor}. You can define constants
+    for the indexes of the {@link android.database.Cursor} columns, because the indexes are
+    the same as the order of the column names in your projection. For example:
+</p>
+<pre>
+// The column index for the _ID column
+private static final int CONTACT_ID_INDEX = 0;
+// The column index for the LOOKUP_KEY column
+private static final int LOOKUP_KEY_INDEX = 1;
+</pre>
+<h3 id="SelectionCriteria">Specify the selection criteria</h3>
+<p>
+    To specify the data you want, create a combination of text expressions and variables
+    that tell the provider the data columns to search and the values to find.
+</p>
+<p>
+    For the text expression, define a constant that lists the search columns. Although this
+    expression can contain values as well, the preferred practice is to represent the values with
+    a "?" placeholder. During retrieval, the placeholder is replaced with values from an
+    array. Using "?" as a placeholder ensures that the search specification is generated by binding
+    rather than by SQL compilation. This practice eliminates the possibility of malicious SQL
+    injection. For example:
+</p>
+<pre>
+    // Defines the text expression
+    &#64;SuppressLint("InlinedApi")
+    private static final String SELECTION =
+            Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.HONEYCOMB ?
+            Contacts.DISPLAY_NAME_PRIMARY + " LIKE ?" :
+            Contacts.DISPLAY_NAME + " LIKE ?";
+    // Defines a variable for the search string
+    private String mSearchString;
+    // Defines the array to hold values that replace the ?
+    private String[] mSelectionArgs = { mSearchString };
+</pre>
+<h3 id="OnItemClick">Define the onItemClick() method</h3>
+<p>
+    In a previous section, you set the item click listener for the {@link android.widget.ListView}.
+    Now implement the action for the listener by defining the method
+    {@link android.widget.AdapterView.OnItemClickListener#onItemClick
+    AdapterView.OnItemClickListener.onItemClick()}:
+</p>
+<pre>
+    &#64;Override
+    public void onItemClick(
+        AdapterView&lt;?&gt; parent, View item, int position, long rowID) {
+        // Get the Cursor
+        Cursor cursor = parent.getAdapter().getCursor();
+        // Move to the selected contact
+        cursor.moveToPosition(position);
+        // Get the _ID value
+        mContactId = getLong(CONTACT_ID_INDEX);
+        // Get the selected LOOKUP KEY
+        mContactKey = getString(CONTACT_KEY_INDEX);
+        // Create the contact's content Uri
+        mContactUri = Contacts.getLookupUri(mContactId, mContactKey);
+        /*
+         * You can use mContactUri as the content URI for retrieving
+         * the details for a contact.
+         */
+    }
+</pre>
+<h3 id="InitializeLoader">Initialize the loader</h3>
+<p>
+    Since you're using a {@link android.support.v4.content.CursorLoader} to retrieve data,
+    you must initialize the background thread and other variables that control asynchronous
+    retrieval. Do the initialization in
+    {@link android.support.v4.app.Fragment#onActivityCreated onActivityCreated()}, which
+    is invoked immediately before the {@link android.support.v4.app.Fragment} UI appears, as
+    shown in the following example:
+</p>
+<pre>
+public class ContactsFragment extends Fragment implements
+        LoaderManager.LoaderCallbacks&lt;Cursor&gt; {
+    ...
+    // Called just before the Fragment displays its UI
+    &#64;Override
+    public void onActivityCreated(Bundle savedInstanceState) {
+        // Always call the super method first
+        super.onActivityCreated(savedInstanceState);
+        ...
+        // Initializes the loader
+        getLoaderManager().initLoader(0, null, this);
+</pre>
+<h3 id="OnCreateLoader">Implement onCreateLoader()</h3>
+<p>
+    Implement the method
+    {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()},
+    which is called by the loader framework immediately after you call
+    {@link android.support.v4.app.LoaderManager#initLoader initLoader()}.
+<p>
+    In {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onCreateLoader onCreateLoader()},
+    set up the search string pattern. To make a string into a pattern, insert "%"
+    (percent) characters to represent a sequence of zero or more characters, or "_" (underscore)
+    characters to represent a single character, or both. For example, the pattern "%Jefferson%"
+    would match both "Thomas Jefferson" and "Jefferson Davis".
+</p>
+<p>
+    Return a new {@link android.support.v4.content.CursorLoader} from the method. For the content
+    URI, use {@link android.provider.ContactsContract.Contacts#CONTENT_URI Contacts.CONTENT_URI}.
+    This URI refers to the entire table, as shown in the following example:
+</p>
+<pre>
+    ...
+    &#64;Override
+    public Loader&lt;Cursor&gt; onCreateLoader(int loaderId, Bundle args) {
+        /*
+         * Makes search string into pattern and
+         * stores it in the selection array
+         */
+        mSelectionArgs[0] = "%" + mSearchString + "%";
+        // Starts the query
+        return new CursorLoader(
+                getActivity(),
+                Contacts.CONTENT_URI,
+                PROJECTION,
+                SELECTION,
+                mSelectionArgs,
+                null
+        );
+    }
+</pre>
+<h3 id="FinishedReset">Implement onLoadFinished() and onLoaderReset()</h3>
+<p>
+    Implement the
+    {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()}
+    method. The loader framework calls
+    {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoadFinished onLoadFinished()}
+    when the Contacts Provider returns the results of the query. In this method, put the
+    result {@link android.database.Cursor} in the
+    {@link android.support.v4.widget.SimpleCursorAdapter}. This automatically updates the
+    {@link android.widget.ListView} with the search results:
+</p>
+<pre>
+    public void onLoadFinished(Loader&lt;Cursor&gt; loader, Cursor cursor) {
+        // Put the result Cursor in the adapter for the ListView
+        mCursorAdapter.swapCursor(cursor);
+    }
+</pre>
+<p>
+    The method {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onLoaderReset
+    onLoaderReset()} is invoked when the loader framework detects that the
+    result {@link android.database.Cursor} contains stale data. Delete the
+    {@link android.support.v4.widget.SimpleCursorAdapter} reference to the existing
+    {@link android.database.Cursor}. If you don't, the loader framework will not
+    recycle the {@link android.database.Cursor}, which causes a memory leak. For example:
+</p>
+<pre>
+    &#64;Override
+    public void onLoaderReset(Loader&lt;Cursor&gt; loader) {
+        // Delete the reference to the existing Cursor
+        mCursorAdapter.swapCursor(null);
+
+    }
+</pre>
+
+<p>
+    You now have the key pieces of an app that matches a search string to contact names and returns
+    the result in a {@link android.widget.ListView}. The user can click a contact name to select it.
+    This triggers a listener, in which you can work further with the contact's data. For example,
+    you can retrieve the contact's details. To learn how to do this, continue with the next
+    lesson, <a href="#retrieve-details.html">Retrieving Details for a Contact</a>.
+</p>
+<p>
+    To learn more about search user interfaces, read the API guide
+    <a href="{@docRoot}guide/topics/search/search-dialog.html">Creating a Search Interface</a>.
+</p>
+<p>
+    The remaining sections in this lesson demonstrate other ways of finding contacts in the
+    Contacts Provider.
+</p>
+<h2 id="TypeMatch">Match a Contact By a Specific Type of Data</h2>
+<p>
+    This technique allows you to specify the type of data you want to match. Retrieving
+    by name is a specific example of this type of query, but you can also do it for any of the types
+    of detail data associated with a contact. For example, you can retrieve contacts that have a
+    specific postal code; in this case, the search string has to match data stored in a postal code
+    row.
+</p>
+<p>
+    To implement this type of retrieval, first implement the following code, as listed in
+    previous sections:
+</p>
+<ul>
+    <li>
+        Request Permission to Read the Provider.
+    </li>
+    <li>
+        Define ListView and item layouts.
+    </li>
+    <li>
+        Define a Fragment that displays the list of contacts.
+    </li>
+    <li>
+        Define global variables.
+    </li>
+    <li>
+        Initialize the Fragment.
+    </li>
+    <li>
+        Set up the CursorAdapter for the ListView.
+    </li>
+    <li>
+        Set the selected contact listener.
+    </li>
+    <li>
+        Define constants for the Cursor column indexes.
+        <p>
+            Although you're retrieving data from a different table, the order of the columns in
+            the projection is the same, so you can use the same indexes for the Cursor.
+        </p>
+    </li>
+    <li>
+        Define the onItemClick() method.
+    </li>
+    <li>
+        Initialize the loader.
+    </li>
+    <li>
+
+        Implement onLoadFinished() and onLoaderReset().
+    </li>
+</ul>
+<p>
+    The following steps show you the additional code you need to match a search string to
+    a particular type of detail data and display the results.
+</p>
+<h3>Choose the data type and table</h3>
+<p>
+    To search for a particular type of detail data, you have to know the custom MIME type value
+    for the data type. Each data type has a unique MIME type
+    value defined by a constant <code>CONTENT_ITEM_TYPE</code> in the subclass of
+    {@link android.provider.ContactsContract.CommonDataKinds} associated with the data type.
+    The subclasses have names that indicate their data type; for example, the subclass for email
+    data is {@link android.provider.ContactsContract.CommonDataKinds.Email}, and the custom MIME
+    type for email data is defined by the constant
+    {@link android.provider.ContactsContract.CommonDataKinds.Email#CONTENT_ITEM_TYPE
+    Email.CONTENT_ITEM_TYPE}.
+</p>
+<p>
+    Use the {@link android.provider.ContactsContract.Data} table for your search. All of the
+    constants you need for your projection, selection clause, and sort order are defined in or
+    inherited by this table.
+</p>
+<h3 id="SpecificProjection">Define a projection</h3>
+<p>
+    To define a projection, choose one or more of the columns defined in
+    {@link android.provider.ContactsContract.Data} or the classes from which it inherits. The
+    Contacts Provider does an implicit join between {@link android.provider.ContactsContract.Data}
+    and other tables before it returns rows. For example:
+</p>
+<pre>
+    &#64;SuppressLint("InlinedApi")
+    private static final String[] PROJECTION =
+        {
+            /*
+             * The detail data row ID. To make a ListView work,
+             * this column is required.
+             */
+            Data._ID,
+            // The primary display name
+            Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.HONEYCOMB ?
+                    Data.DISPLAY_NAME_PRIMARY :
+                    Data.DISPLAY_NAME,
+            // The contact's _ID, to construct a content URI
+            Data.CONTACT_ID
+            // The contact's LOOKUP_KEY, to construct a content URI
+            Data.LOOKUP_KEY (a permanent link to the contact
+        };
+</pre>
+<h3 id="SpecificCriteria">Define search criteria</h3>
+<p>
+    To search for a string within a particular type of data, construct a selection clause from
+    the following:
+</p>
+<ul>
+    <li>
+        The name of the column that contains your search string. This name varies by data type,
+        so you need to find the subclass of
+        {@link android.provider.ContactsContract.CommonDataKinds} that corresponds to the data type
+        and then choose the column name from that subclass. For example, to search for
+        email addresses, use the column
+        {@link android.provider.ContactsContract.CommonDataKinds.Email#ADDRESS Email.ADDRESS}.
+    </li>
+    <li>
+        The search string itself, represented as the "?" character in the selection clause.
+    </li>
+    <li>
+        The name of the column that contains the custom MIME type value. This name is always
+        {@link android.provider.ContactsContract.Data#MIMETYPE Data.MIMETYPE}.
+    </li>
+    <li>
+        The custom MIME type value for the data type. As described previously, this is the constant
+        <code>CONTENT_ITEM_TYPE</code> in the
+        {@link android.provider.ContactsContract.CommonDataKinds} subclass. For example, the MIME
+        type value for email data is
+        {@link android.provider.ContactsContract.CommonDataKinds.Email#CONTENT_ITEM_TYPE
+        Email.CONTENT_ITEM_TYPE}. Enclose the value in single quotes by concatenating a
+        "<code>'</code>" (single quote) character to the start and end of the constant; otherwise,
+        the provider interprets the value as a variable name rather than as a string value.
+        You don't need to use a placeholder for this value, because you're using a constant
+        rather than a user-supplied value.
+    </li>
+</ul>
+<p>
+    For example:
+</p>
+<pre>
+    /*
+     * Constructs search criteria from the search string
+     * and email MIME type
+     */
+    private static final String SELECTION =
+            /*
+             * Searches for an email address
+             * that matches the search string
+             */
+            Email.ADDRESS + " LIKE ? " + "AND " +
+            /*
+             * Searches for a MIME type that matches
+             * the value of the constant
+             * Email.CONTENT_ITEM_TYPE. Note the
+             * single quotes surrounding Email.CONTENT_ITEM_TYPE.
+             */
+            Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'";
+</pre>
+<p>
+    Next, define variables to contain the selection argument:
+</p>
+<pre>
+    String mSearchString;
+    String[] mSelectionArgs = { "" };
+</pre>
+<h3 id="SpecificLoader">Implement onCreateLoader()</h3>
+<p>
+    Now that you've specified the data you want and how to find it, define a query in your
+    implementation of {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onCreateLoader
+    onCreateLoader()}. Return a new {@link android.support.v4.content.CursorLoader} from this
+    method, using your projection, selection text expression, and selection array as
+    arguments. For a content URI, use
+    {@link android.provider.ContactsContract.Data#CONTENT_URI Data.CONTENT_URI}. For example:
+</p>
+<pre>
+    &#64;Override
+    public Loader&lt;Cursor&gt; onCreateLoader(int loaderId, Bundle args) {
+        // OPTIONAL: Makes search string into pattern
+        mSearchString = "%" + mSearchString + "%";
+        // Puts the search string into the selection criteria
+        mSelectionArgs[0] = mSearchString;
+        // Starts the query
+        return new CursorLoader(
+                getActivity(),
+                Data.CONTENT_URI,
+                PROJECTION,
+                SELECTION,
+                mSelectionArgs,
+                null
+        );
+    }
+</pre>
+<p>
+    These code snippets are the basis of a simple reverse lookup based on a specific type of detail
+    data. This is the best technique to use if your app focuses on a particular type of data, such
+    as emails, and you want allow users to get the names associated with a piece of data.
+</p>
+<h2 id="GeneralMatch">Match a Contact By Any Type of Data</h2>
+<p>
+    Retrieving a contact based on any type of data returns contacts if any of their data matches a
+    the search string, including name, email address, postal address, phone number, and so forth.
+    This results in a broad set of search results. For example, if the search string
+    is "Doe", then searching for any data type returns the contact "John Doe"; it also returns
+    contacts who live on "Doe Street".
+</p>
+<p>
+    To implement this type of retrieval, first implement the following code, as listed in
+    previous sections:
+</p>
+<ul>
+    <li>
+        Request Permission to Read the Provider.
+    </li>
+    <li>
+        Define ListView and item layouts.
+    </li>
+    <li>
+    <li>
+        Define a Fragment that displays the list of contacts.
+    </li>
+    <li>
+        Define global variables.
+    </li>
+    <li>
+        Initialize the Fragment.
+    </li>
+    <li>
+        Set up the CursorAdapter for the ListView.
+    </li>
+    <li>
+        Set the selected contact listener.
+    </li>
+    <li>
+        Define a projection.
+    </li>
+    <li>
+        Define constants for the Cursor column indexes.
+        <p>
+            For this type of retrieval, you're using the same table you used in the section
+            <a href="#NameMatch">Match a Contact by Name and List the Results</a>. Use the
+            same column indexes as well.
+        </p>
+    </li>
+    <li>
+        Define the onItemClick() method.
+    </li>
+    <li>
+        Initialize the loader.
+    </li>
+    <li>
+
+        Implement onLoadFinished() and onLoaderReset().
+    </li>
+</ul>
+<p>
+    The following steps show you the additional code you need to match a search string to
+    any type of data and display the results.
+</p>
+<h3 id="NoSelection">Remove selection criteria</h3>
+<p>
+    Don't define the <code>SELECTION</code> constants or the <code>mSelectionArgs</code> variable.
+    These aren't used in this type of retrieval.
+</p>
+<h3 id="CreateLoaderAny">Implement onCreateLoader()</h3>
+<p>
+    Implement the {@link android.support.v4.app.LoaderManager.LoaderCallbacks#onCreateLoader
+    onCreateLoader()} method, returning a new {@link android.support.v4.content.CursorLoader}.
+    You don't need to convert the search string into a pattern, because the Contacts Provider does
+    that automatically. Use
+    {@link android.provider.ContactsContract.Contacts#CONTENT_FILTER_URI
+    Contacts.CONTENT_FILTER_URI} as the base URI, and append your search string to it by calling
+    {@link android.net.Uri#withAppendedPath Uri.withAppendedPath()}. Using this URI
+    automatically triggers searching for any data type, as shown in the following example:
+</p>
+<pre>
+    &#64;Override
+    public Loader&lt;Cursor&gt; onCreateLoader(int loaderId, Bundle args) {
+        /*
+         * Appends the search string to the base URI. Always
+         * encode search strings to ensure they're in proper
+         * format.
+         */
+        Uri contentUri = Uri.withAppendedPath(
+                Contacts.CONTENT_FILTER_URI,
+                Uri.encode(mSearchString));
+        // Starts the query
+        return new CursorLoader(
+                getActivity(),
+                contentUri,
+                PROJECTION,
+                null,
+                null,
+                null
+        );
+    }
+</pre>
+<p>
+    These code snippets are the basis of an app that does a broad search of the Contacts Provider.
+    The technique is useful for apps that want to implement functionality similar to the
+    People app's contact list screen.
+</p>
diff --git a/docs/html/training/in-app-billing/purchase-iab-products.jd b/docs/html/training/in-app-billing/purchase-iab-products.jd
index 7fa77d3..4e6e035 100644
--- a/docs/html/training/in-app-billing/purchase-iab-products.jd
+++ b/docs/html/training/in-app-billing/purchase-iab-products.jd
@@ -104,7 +104,7 @@
 </pre>
 
 <h2 id="Consume">Consume a Purchase</h2>
-<p>You can use the In-app Billing Version 3 API to track the ownership of purchased items in Google Play. Once an item is purchased, it is considered to be "owned" and cannot be purchased again from Google Play while in that state. You must send a consumption request for the item before Google Play makes it available for purchase again. All managed in-app products are consumable.  How you use the consumption mechanism in your app is up to you. Typically, you would implement consumption for products with temporary benefits that users may want to purchase multiple times (for example, in-game currency or replensihable game tokens). You would typically not want to implement consumption for products that are purchased once and provide a permanent effect (for example, a premium upgrade).</p>
+<p>You can use the In-app Billing Version 3 API to track the ownership of purchased items in Google Play. Once an item is purchased, it is considered to be "owned" and cannot be purchased again from Google Play while in that state. You must send a consumption request for the item before Google Play makes it available for purchase again. All managed in-app products are consumable.  How you use the consumption mechanism in your app is up to you. Typically, you would implement consumption for products with temporary benefits that users may want to purchase multiple times (for example, in-game currency or replenishable game tokens). You would typically not want to implement consumption for products that are purchased once and provide a permanent effect (for example, a premium upgrade).</p>
 <p>It's your responsibility to control and track how the in-app product is provisioned to the user. For example, if the user purchased in-game currency, you should update the player's inventory with the amount of currency purchased.</p>
 <p class="note"><strong>Security Recommendation:</strong> You must send a consumption request before provisioning the benefit of the consumable in-app purchase to the user. Make sure that you have received a successful consumption response from Google Play before you provision the item.</p>
 <p>To record a purchase consumption, call {@code consumeAsync(Purchase, OnConsumeFinishedListener)} on your {@code IabHelper} instance. The first argument that the method takes is the {@code Purchase} object representing the item to consume. The second argument is a {@code OnConsumeFinishedListener} that is notified when the consumption operation has completed and handles the consumption response from Google Play. It is safe to make this call fom your main thread.</p>
diff --git a/docs/html/training/training_toc.cs b/docs/html/training/training_toc.cs
index 985fc44..7a3f2ca 100644
--- a/docs/html/training/training_toc.cs
+++ b/docs/html/training/training_toc.cs
@@ -484,7 +484,37 @@
       </a>
     </div>
     <ul>
-    
+      <li class="nav-section">
+        <div class="nav-section-header">
+          <a href="<?cs var:toroot ?>training/contacts-provider/index.html"
+             description=
+             "How to use Android's central address book, the Contacts Provider, to
+             display contacts and their details and modify contact information.">
+          Accessing Contacts Data</a>
+        </div>
+        <ul>
+          <li>
+                <a href="<?cs var:toroot ?>training/contacts-provider/retrieve-names.html">
+                Retrieving a List of Contacts
+                </a>
+          </li>
+          <li>
+                <a href="<?cs var:toroot ?>training/contacts-provider/retrieve-details.html">
+                Retrieving Details for a Contact
+                </a>
+          </li>
+          <li>
+                <a href="<?cs var:toroot ?>training/contacts-provider/modify-data.html">
+                Modifying Contacts Using Intents
+                </a>
+          </li>
+          <li>
+                <a href="<?cs var:toroot ?>training/contacts-provider/display-contact-badge.html">
+                Displaying the Quick Contact Badge
+                </a>
+          </li>
+        </ul>
+      </li>
       <li class="nav-section">
         <div class="nav-section-header">
           <a href="<?cs var:toroot ?>training/id-auth/index.html"
@@ -879,7 +909,7 @@
         </ul>
       </li>
     </ul>
-  </li> <!-- end of User Input -->    
+  </li> <!-- end of User Input -->
 
   <li class="nav-section">
     <div class="nav-section-header">
diff --git a/graphics/java/android/graphics/DashPathEffect.java b/graphics/java/android/graphics/DashPathEffect.java
index 4f16dc4..2bdecce 100644
--- a/graphics/java/android/graphics/DashPathEffect.java
+++ b/graphics/java/android/graphics/DashPathEffect.java
@@ -26,7 +26,7 @@
      * controls the length of the dashes. The paint's strokeWidth controls the
      * thickness of the dashes.
      * Note: this patheffect only affects drawing with the paint's style is set
-     * to STROKE or STROKE_AND_FILL. It is ignored if the drawing is done with
+     * to STROKE or FILL_AND_STROKE. It is ignored if the drawing is done with
      * style == FILL.
      * @param intervals array of ON and OFF distances
      * @param phase offset into the intervals array
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index d5cee3a..1485d8d 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -1295,17 +1295,17 @@
             return 0f;
         }
         if (!mHasCompatScaling) {
-            return (float) Math.ceil(native_measureText(text, index, count));
+            return (float) Math.ceil(native_measureText(text, index, count, mBidiFlags));
         }
 
         final float oldSize = getTextSize();
         setTextSize(oldSize*mCompatScaling);
-        float w = native_measureText(text, index, count);
+        float w = native_measureText(text, index, count, mBidiFlags);
         setTextSize(oldSize);
         return (float) Math.ceil(w*mInvCompatScaling);
     }
 
-    private native float native_measureText(char[] text, int index, int count);
+    private native float native_measureText(char[] text, int index, int count, int bidiFlags);
     
     /**
      * Return the width of the text.
@@ -1327,17 +1327,17 @@
             return 0f;
         }
         if (!mHasCompatScaling) {
-            return (float) Math.ceil(native_measureText(text, start, end));
+            return (float) Math.ceil(native_measureText(text, start, end, mBidiFlags));
         }
 
         final float oldSize = getTextSize();
         setTextSize(oldSize*mCompatScaling);
-        float w = native_measureText(text, start, end);
+        float w = native_measureText(text, start, end, mBidiFlags);
         setTextSize(oldSize);
         return (float) Math.ceil(w*mInvCompatScaling);
     }
 
-    private native float native_measureText(String text, int start, int end);
+    private native float native_measureText(String text, int start, int end, int bidiFlags);
     
     /**
      * Return the width of the text.
@@ -1355,16 +1355,16 @@
         }
 
         if (!mHasCompatScaling) {
-            return (float) Math.ceil(native_measureText(text));
+            return (float) Math.ceil(native_measureText(text, mBidiFlags));
         }
         final float oldSize = getTextSize();
         setTextSize(oldSize*mCompatScaling);
-        float w = native_measureText(text);
+        float w = native_measureText(text, mBidiFlags);
         setTextSize(oldSize);
         return (float) Math.ceil(w*mInvCompatScaling);
     }
 
-    private native float native_measureText(String text);
+    private native float native_measureText(String text, int bidiFlags);
     
     /**
      * Return the width of the text.
@@ -1431,12 +1431,12 @@
             return 0;
         }
         if (!mHasCompatScaling) {
-            return native_breakText(text, index, count, maxWidth, measuredWidth);
+            return native_breakText(text, index, count, maxWidth, mBidiFlags, measuredWidth);
         }
 
         final float oldSize = getTextSize();
         setTextSize(oldSize*mCompatScaling);
-        int res = native_breakText(text, index, count, maxWidth*mCompatScaling,
+        int res = native_breakText(text, index, count, maxWidth*mCompatScaling, mBidiFlags,
                 measuredWidth);
         setTextSize(oldSize);
         if (measuredWidth != null) measuredWidth[0] *= mInvCompatScaling;
@@ -1444,7 +1444,7 @@
     }
 
     private native int native_breakText(char[] text, int index, int count,
-                                        float maxWidth, float[] measuredWidth);
+                                        float maxWidth, int bidiFlags, float[] measuredWidth);
 
     /**
      * Measure the text, stopping early if the measured width exceeds maxWidth.
@@ -1521,12 +1521,12 @@
             return 0;
         }
         if (!mHasCompatScaling) {
-            return native_breakText(text, measureForwards, maxWidth, measuredWidth);
+            return native_breakText(text, measureForwards, maxWidth, mBidiFlags, measuredWidth);
         }
 
         final float oldSize = getTextSize();
         setTextSize(oldSize*mCompatScaling);
-        int res = native_breakText(text, measureForwards, maxWidth*mCompatScaling,
+        int res = native_breakText(text, measureForwards, maxWidth*mCompatScaling, mBidiFlags,
                 measuredWidth);
         setTextSize(oldSize);
         if (measuredWidth != null) measuredWidth[0] *= mInvCompatScaling;
@@ -1534,7 +1534,7 @@
     }
 
     private native int native_breakText(String text, boolean measureForwards,
-                                        float maxWidth, float[] measuredWidth);
+                                        float maxWidth, int bidiFlags, float[] measuredWidth);
 
     /**
      * Return the advance widths for the characters in the string.
@@ -1560,12 +1560,12 @@
             return 0;
         }
         if (!mHasCompatScaling) {
-            return native_getTextWidths(mNativePaint, text, index, count, widths);
+            return native_getTextWidths(mNativePaint, text, index, count, mBidiFlags, widths);
         }
 
         final float oldSize = getTextSize();
         setTextSize(oldSize*mCompatScaling);
-        int res = native_getTextWidths(mNativePaint, text, index, count, widths);
+        int res = native_getTextWidths(mNativePaint, text, index, count, mBidiFlags, widths);
         setTextSize(oldSize);
         for (int i=0; i<res; i++) {
             widths[i] *= mInvCompatScaling;
@@ -1642,12 +1642,12 @@
             return 0;
         }
         if (!mHasCompatScaling) {
-            return native_getTextWidths(mNativePaint, text, start, end, widths);
+            return native_getTextWidths(mNativePaint, text, start, end, mBidiFlags, widths);
         }
 
         final float oldSize = getTextSize();
         setTextSize(oldSize*mCompatScaling);
-        int res = native_getTextWidths(mNativePaint, text, start, end, widths);
+        int res = native_getTextWidths(mNativePaint, text, start, end, mBidiFlags, widths);
         setTextSize(oldSize);
         for (int i=0; i<res; i++) {
             widths[i] *= mInvCompatScaling;
@@ -2073,7 +2073,7 @@
         if (bounds == null) {
             throw new NullPointerException("need bounds Rect");
         }
-        nativeGetStringBounds(mNativePaint, text, start, end, bounds);
+        nativeGetStringBounds(mNativePaint, text, start, end, mBidiFlags, bounds);
     }
     
     /**
@@ -2093,7 +2093,7 @@
         if (bounds == null) {
             throw new NullPointerException("need bounds Rect");
         }
-        nativeGetCharArrayBounds(mNativePaint, text, index, count, bounds);
+        nativeGetCharArrayBounds(mNativePaint, text, index, count, mBidiFlags, bounds);
     }
     
     @Override
@@ -2140,9 +2140,9 @@
                                                     String locale);
 
     private static native int native_getTextWidths(int native_object,
-                            char[] text, int index, int count, float[] widths);
+                            char[] text, int index, int count, int bidiFlags, float[] widths);
     private static native int native_getTextWidths(int native_object,
-                            String text, int start, int end, float[] widths);
+                            String text, int start, int end, int bidiFlags, float[] widths);
 
     private static native int native_getTextGlyphs(int native_object,
             String text, int start, int end, int contextStart, int contextEnd,
@@ -2165,8 +2165,8 @@
     private static native void native_getTextPath(int native_object, int bidiFlags,
                 String text, int start, int end, float x, float y, int path);
     private static native void nativeGetStringBounds(int nativePaint,
-                                String text, int start, int end, Rect bounds);
+                                String text, int start, int end, int bidiFlags, Rect bounds);
     private static native void nativeGetCharArrayBounds(int nativePaint,
-                                char[] text, int index, int count, Rect bounds);
+                                char[] text, int index, int count, int bidiFlags, Rect bounds);
     private static native void finalizer(int nativePaint);
 }
diff --git a/graphics/java/android/renderscript/ScriptIntrinsicBlur.java b/graphics/java/android/renderscript/ScriptIntrinsicBlur.java
index 7ffd1e7..2848f64 100644
--- a/graphics/java/android/renderscript/ScriptIntrinsicBlur.java
+++ b/graphics/java/android/renderscript/ScriptIntrinsicBlur.java
@@ -46,7 +46,7 @@
      * @return ScriptIntrinsicBlur
      */
     public static ScriptIntrinsicBlur create(RenderScript rs, Element e) {
-        if ((e != Element.U8_4(rs)) && e != (Element.U8(rs))) {
+        if ((!e.isCompatible(Element.U8_4(rs))) && (!e.isCompatible(Element.U8(rs)))) {
             throw new RSIllegalArgumentException("Unsuported element type.");
         }
         int id = rs.nScriptIntrinsicCreate(5, e.getID(rs));
diff --git a/graphics/java/android/renderscript/ScriptIntrinsicColorMatrix.java b/graphics/java/android/renderscript/ScriptIntrinsicColorMatrix.java
index b219978..f7e844e 100644
--- a/graphics/java/android/renderscript/ScriptIntrinsicColorMatrix.java
+++ b/graphics/java/android/renderscript/ScriptIntrinsicColorMatrix.java
@@ -47,7 +47,7 @@
      * @return ScriptIntrinsicColorMatrix
      */
     public static ScriptIntrinsicColorMatrix create(RenderScript rs, Element e) {
-        if (e != Element.U8_4(rs)) {
+        if (!e.isCompatible(Element.U8_4(rs))) {
             throw new RSIllegalArgumentException("Unsuported element type.");
         }
         int id = rs.nScriptIntrinsicCreate(2, e.getID(rs));
diff --git a/graphics/java/android/renderscript/ScriptIntrinsicConvolve3x3.java b/graphics/java/android/renderscript/ScriptIntrinsicConvolve3x3.java
index b40ea84..d54df96 100644
--- a/graphics/java/android/renderscript/ScriptIntrinsicConvolve3x3.java
+++ b/graphics/java/android/renderscript/ScriptIntrinsicConvolve3x3.java
@@ -48,7 +48,7 @@
      */
     public static ScriptIntrinsicConvolve3x3 create(RenderScript rs, Element e) {
         float f[] = { 0, 0, 0, 0, 1, 0, 0, 0, 0};
-        if (e != Element.U8_4(rs)) {
+        if (!e.isCompatible(Element.U8_4(rs))) {
             throw new RSIllegalArgumentException("Unsuported element type.");
         }
         int id = rs.nScriptIntrinsicCreate(1, e.getID(rs));
diff --git a/graphics/java/android/renderscript/Type.java b/graphics/java/android/renderscript/Type.java
index 9507030..d7c8255 100644
--- a/graphics/java/android/renderscript/Type.java
+++ b/graphics/java/android/renderscript/Type.java
@@ -110,6 +110,16 @@
     }
 
     /**
+     * Get the YUV format
+     *
+     * @hide
+     * @return int
+     */
+    public int getYuv() {
+        return mDimYuv;
+    }
+
+    /**
      * Return if the Type has a mipmap chain.
      *
      * @return boolean
diff --git a/keystore/java/android/security/AndroidKeyPairGeneratorSpec.java b/keystore/java/android/security/AndroidKeyPairGeneratorSpec.java
index 79a7630..83faf35 100644
--- a/keystore/java/android/security/AndroidKeyPairGeneratorSpec.java
+++ b/keystore/java/android/security/AndroidKeyPairGeneratorSpec.java
@@ -28,10 +28,28 @@
 import javax.security.auth.x500.X500Principal;
 
 /**
- * This provides the required parameters needed for initializing the KeyPair
- * generator that works with
- * <a href="{@docRoot}guide/topics/security/keystore.html">Android KeyStore
- * facility</a>.
+ * This provides the required parameters needed for initializing the
+ * {@code KeyPairGenerator} that works with <a href="{@docRoot}
+ * guide/topics/security/keystore.html">Android KeyStore facility</a>. The
+ * Android KeyStore facility is accessed through a
+ * {@link java.security.KeyPairGenerator} API using the
+ * {@code AndroidKeyPairGenerator} provider. The {@code context} passed in may
+ * be used to pop up some UI to ask the user to unlock or initialize the Android
+ * keystore facility.
+ * <p>
+ * After generation, the {@code keyStoreAlias} is used with the
+ * {@link java.security.KeyStore#getEntry(String, java.security.KeyStore.ProtectionParameter)}
+ * interface to retrieve the {@link PrivateKey} and its associated
+ * {@link Certificate} chain.
+ * <p>
+ * The KeyPair generator will create a self-signed certificate with the subject
+ * as its X.509v3 Subject Distinguished Name and as its X.509v3 Issuer
+ * Distinguished Name along with the other parameters specified with the
+ * {@link Builder}.
+ * <p>
+ * The self-signed certificate may be replaced at a later time by a certificate
+ * signed by a real Certificate Authority.
+ *
  * @hide
  */
 public class AndroidKeyPairGeneratorSpec implements AlgorithmParameterSpec {
@@ -74,6 +92,7 @@
      *            period
      * @throws IllegalArgumentException when any argument is {@code null} or
      *             {@code endDate} is before {@code startDate}.
+     * @hide should be built with AndroidKeyPairGeneratorSpecBuilder
      */
     public AndroidKeyPairGeneratorSpec(Context context, String keyStoreAlias,
             X500Principal subjectDN, BigInteger serialNumber, Date startDate, Date endDate) {
@@ -142,4 +161,121 @@
     Date getEndDate() {
         return mEndDate;
     }
+
+    /**
+     * Builder class for {@link AndroidKeyPairGeneratorSpec} objects.
+     * <p>
+     * This will build a parameter spec for use with the <a href="{@docRoot}
+     * guide/topics/security/keystore.html">Android KeyStore facility</a>.
+     * <p>
+     * The required fields must be filled in with the builder.
+     * <p>
+     * Example:
+     *
+     * <pre class="prettyprint">
+     * Calendar start = new Calendar();
+     * Calendar end = new Calendar();
+     * end.add(1, Calendar.YEAR);
+     *
+     * AndroidKeyPairGeneratorSpec spec = new AndroidKeyPairGeneratorSpec.Builder(mContext)
+     *         .setAlias("myKey")
+     *         .setSubject(new X500Principal("CN=myKey"))
+     *         .setSerial(BigInteger.valueOf(1337))
+     *         .setStartDate(start.getTime())
+     *         .setEndDate(end.getTime())
+     *         .build();
+     * </pre>
+     */
+    public static class Builder {
+        private final Context mContext;
+
+        private String mKeystoreAlias;
+
+        private X500Principal mSubjectDN;
+
+        private BigInteger mSerialNumber;
+
+        private Date mStartDate;
+
+        private Date mEndDate;
+
+        public Builder(Context context) {
+            if (context == null) {
+                throw new NullPointerException("context == null");
+            }
+            mContext = context;
+        }
+
+        /**
+         * Sets the alias to be used to retrieve the key later from a
+         * {@link java.security.KeyStore} instance using the
+         * {@code AndroidKeyStore} provider.
+         */
+        public Builder setAlias(String alias) {
+            if (alias == null) {
+                throw new NullPointerException("alias == null");
+            }
+            mKeystoreAlias = alias;
+            return this;
+        }
+
+        /**
+         * Sets the subject used for the self-signed certificate of the
+         * generated key pair.
+         */
+        public Builder setSubject(X500Principal subject) {
+            if (subject == null) {
+                throw new NullPointerException("subject == null");
+            }
+            mSubjectDN = subject;
+            return this;
+        }
+
+        /**
+         * Sets the serial number used for the self-signed certificate of the
+         * generated key pair.
+         */
+        public Builder setSerialNumber(BigInteger serialNumber) {
+            if (serialNumber == null) {
+                throw new NullPointerException("serialNumber == null");
+            }
+            mSerialNumber = serialNumber;
+            return this;
+        }
+
+        /**
+         * Sets the start of the validity period for the self-signed certificate
+         * of the generated key pair.
+         */
+        public Builder setStartDate(Date startDate) {
+            if (startDate == null) {
+                throw new NullPointerException("startDate == null");
+            }
+            mStartDate = startDate;
+            return this;
+        }
+
+        /**
+         * Sets the end of the validity period for the self-signed certificate
+         * of the generated key pair.
+         */
+        public Builder setEndDate(Date endDate) {
+            if (endDate == null) {
+                throw new NullPointerException("endDate == null");
+            }
+            mEndDate = endDate;
+            return this;
+        }
+
+        /**
+         * Builds the instance of the {@code AndroidKeyPairGeneratorSpec}.
+         *
+         * @throws IllegalArgumentException if a required field is missing
+         * @return built instance of {@code AndroidKeyPairGeneratorSpec}
+         */
+        public AndroidKeyPairGeneratorSpec build() {
+            return new AndroidKeyPairGeneratorSpec(mContext, mKeystoreAlias, mSubjectDN,
+                    mSerialNumber, mStartDate, mEndDate);
+        }
+    }
 }
diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java
index d8109ce..166849d 100644
--- a/keystore/java/android/security/Credentials.java
+++ b/keystore/java/android/security/Credentials.java
@@ -49,6 +49,8 @@
 
     public static final String INSTALL_ACTION = "android.credentials.INSTALL";
 
+    public static final String INSTALL_AS_USER_ACTION = "android.credentials.INSTALL_AS_USER";
+
     public static final String UNLOCK_ACTION = "com.android.credentials.UNLOCK";
 
     /** Key prefix for CA certificates. */
@@ -83,6 +85,12 @@
     public static final String EXTENSION_PFX = ".pfx";
 
     /**
+     * Intent extra: install the certificate bundle as this UID instead of
+     * system.
+     */
+    public static final String EXTRA_INSTALL_AS_UID = "install_as_uid";
+
+    /**
      * Intent extra: name for the user's private key.
      */
     public static final String EXTRA_USER_PRIVATE_KEY_NAME = "user_private_key_name";
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index d7119fff..c99dff0 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -356,6 +356,30 @@
         }
     }
 
+    /**
+     * Returns {@code true} if the current device's {@code KeyChain} supports a
+     * specific {@code PrivateKey} type indicated by {@code algorithm} (e.g.,
+     * "RSA").
+     */
+    public static boolean isKeyAlgorithmSupported(String algorithm) {
+        return "RSA".equals(algorithm);
+    }
+
+    /**
+     * Returns {@code true} if the current device's {@code KeyChain} binds any
+     * {@code PrivateKey} of the given {@code algorithm} to the device once
+     * imported or generated. This can be used to tell if there is special
+     * hardware support that can be used to bind keys to the device in a way
+     * that makes it non-exportable.
+     */
+    public static boolean isBoundKeyAlgorithm(String algorithm) {
+        if (!isKeyAlgorithmSupported(algorithm)) {
+            return false;
+        }
+
+        return KeyStore.getInstance().isHardwareBacked();
+    }
+
     private static X509Certificate toCertificate(byte[] bytes) {
         if (bytes == null) {
             throw new IllegalArgumentException("bytes == null");
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 12c0ed8..2037472 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -296,6 +296,15 @@
         }
     }
 
+    public boolean isHardwareBacked() {
+        try {
+            return mBinder.is_hardware_backed() == NO_ERROR;
+        } catch (RemoteException e) {
+            Log.w(TAG, "Cannot connect to keystore", e);
+            return false;
+        }
+    }
+
     public int getLastError() {
         return mError;
     }
diff --git a/keystore/tests/src/android/security/AndroidKeyPairGeneratorSpecTest.java b/keystore/tests/src/android/security/AndroidKeyPairGeneratorSpecTest.java
index e6a3750..3d275cd 100644
--- a/keystore/tests/src/android/security/AndroidKeyPairGeneratorSpecTest.java
+++ b/keystore/tests/src/android/security/AndroidKeyPairGeneratorSpecTest.java
@@ -53,6 +53,26 @@
         assertEquals("endDate should be the one specified", NOW_PLUS_10_YEARS, spec.getEndDate());
     }
 
+    public void testBuilder_Success() throws Exception {
+        AndroidKeyPairGeneratorSpec spec = new AndroidKeyPairGeneratorSpec.Builder(getContext())
+                .setAlias(TEST_ALIAS_1)
+                .setSubject(TEST_DN_1)
+                .setSerialNumber(SERIAL_1)
+                .setStartDate(NOW)
+                .setEndDate(NOW_PLUS_10_YEARS)
+                .build();
+
+        assertEquals("Context should be the one specified", getContext(), spec.getContext());
+
+        assertEquals("Alias should be the one specified", TEST_ALIAS_1, spec.getKeystoreAlias());
+
+        assertEquals("subjectDN should be the one specified", TEST_DN_1, spec.getSubjectDN());
+
+        assertEquals("startDate should be the one specified", NOW, spec.getStartDate());
+
+        assertEquals("endDate should be the one specified", NOW_PLUS_10_YEARS, spec.getEndDate());
+    }
+
     public void testConstructor_NullContext_Failure() throws Exception {
         try {
             new AndroidKeyPairGeneratorSpec(null, TEST_ALIAS_1, TEST_DN_1, SERIAL_1, NOW,
diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp
index 7907224..5ff92be 100644
--- a/libs/hwui/DeferredDisplayList.cpp
+++ b/libs/hwui/DeferredDisplayList.cpp
@@ -75,7 +75,7 @@
         for (unsigned int i = 0; i < mOps.size(); i++) {
             DrawOp* op = mOps[i];
 
-            renderer.restoreDisplayState(op->state, kStateDeferFlag_Draw);
+            renderer.restoreDisplayState(op->state);
 
 #if DEBUG_DISPLAY_LIST_OPS_AS_EVENTS
             renderer.eventMark(op->name());
@@ -106,7 +106,7 @@
 
     virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty) {
         DEFER_LOGD("replaying state op batch %p", this);
-        renderer.restoreDisplayState(mOp->state, 0);
+        renderer.restoreDisplayState(mOp->state);
 
         // use invalid save count because it won't be used at flush time - RestoreToCountOp is the
         // only one to use it, and we don't use that class at flush time, instead calling
@@ -117,12 +117,12 @@
     }
 
 private:
-    StateOp* mOp;
+    const StateOp* mOp;
 };
 
 class RestoreToCountBatch : public DrawOpBatch {
 public:
-    RestoreToCountBatch(int restoreCount) : mRestoreCount(restoreCount) {}
+    RestoreToCountBatch(StateOp* op, int restoreCount) : mOp(op), mRestoreCount(restoreCount) {}
 
     bool intersects(Rect& rect) {
         // if something checks for intersection, it's trying to go backwards across a state op,
@@ -133,11 +133,15 @@
 
     virtual status_t replay(OpenGLRenderer& renderer, Rect& dirty) {
         DEFER_LOGD("batch %p restoring to count %d", this, mRestoreCount);
+
+        renderer.restoreDisplayState(mOp->state);
         renderer.restoreToCount(mRestoreCount);
         return DrawGlInfo::kStatusDone;
     }
 
 private:
+    // we use the state storage for the RestoreToCountOp, but don't replay the op itself
+    const StateOp* mOp;
     /*
      * The count used here represents the flush() time saveCount. This is as opposed to the
      * DisplayList record time, or defer() time values (which are RestoreToCountOp's mCount, and
@@ -251,7 +255,8 @@
  * Either will act as a barrier to draw operation reordering, as we want to play back layer
  * save/restore and complex canvas modifications (including save/restore) in order.
  */
-void DeferredDisplayList::addRestoreToCount(OpenGLRenderer& renderer, int newSaveCount) {
+void DeferredDisplayList::addRestoreToCount(OpenGLRenderer& renderer, StateOp* op,
+        int newSaveCount) {
     DEFER_LOGD("%p addRestoreToCount %d", this, newSaveCount);
 
     if (recordingComplexClip() && newSaveCount <= mComplexClipStackStart) {
@@ -265,7 +270,7 @@
 
     while (!mSaveStack.isEmpty() && mSaveStack.top() >= newSaveCount) mSaveStack.pop();
 
-    storeRestoreToCountBarrier(mSaveStack.size() + 1);
+    storeRestoreToCountBarrier(renderer, op, mSaveStack.size() + 1);
 }
 
 void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) {
@@ -338,11 +343,15 @@
     resetBatchingState();
 }
 
-void DeferredDisplayList::storeRestoreToCountBarrier(int newSaveCount) {
+void DeferredDisplayList::storeRestoreToCountBarrier(OpenGLRenderer& renderer, StateOp* op,
+        int newSaveCount) {
     DEFER_LOGD("%p adding restore to count %d barrier, pos %d",
             this, newSaveCount, mBatches.size());
 
-    mBatches.add(new RestoreToCountBatch(newSaveCount));
+    // store displayState for the restore operation, as it may be associated with a saveLayer that
+    // doesn't have kClip_SaveFlag set
+    renderer.storeDisplayState(op->state, getStateOpDeferFlags());
+    mBatches.add(new RestoreToCountBatch(op, newSaveCount));
     resetBatchingState();
 }
 
@@ -368,13 +377,18 @@
     status_t status = DrawGlInfo::kStatusDone;
 
     if (isEmpty()) return status; // nothing to flush
+    renderer.restoreToCount(1);
 
     DEFER_LOGD("--flushing");
     renderer.eventMark("Flush");
 
+    // save and restore (with draw modifiers) so that reordering doesn't affect final state
     DrawModifiers restoreDrawModifiers = renderer.getDrawModifiers();
-    renderer.restoreToCount(1);
+    renderer.save(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
+
     status |= replayBatchList(mBatches, renderer, dirty);
+
+    renderer.restoreToCount(1);
     renderer.setDrawModifiers(restoreDrawModifiers);
 
     DEFER_LOGD("--flush complete, returning %x", status);
diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h
index cb9da8f..3e450da 100644
--- a/libs/hwui/DeferredDisplayList.h
+++ b/libs/hwui/DeferredDisplayList.h
@@ -52,6 +52,8 @@
         kOpBatch_Count, // Add other batch ids before this
     };
 
+    void clear();
+
     bool isEmpty() { return mBatches.isEmpty(); }
 
     /**
@@ -63,7 +65,7 @@
     void addClip(OpenGLRenderer& renderer, ClipOp* op);
     void addSaveLayer(OpenGLRenderer& renderer, SaveLayerOp* op, int newSaveCount);
     void addSave(OpenGLRenderer& renderer, SaveOp* op, int newSaveCount);
-    void addRestoreToCount(OpenGLRenderer& renderer, int newSaveCount);
+    void addRestoreToCount(OpenGLRenderer& renderer, StateOp* op, int newSaveCount);
 
     /**
      * Add a draw op into the DeferredDisplayList, reordering as needed (for performance) if
@@ -78,10 +80,8 @@
      */
     void resetBatchingState();
 
-    void clear();
-
     void storeStateOpBarrier(OpenGLRenderer& renderer, StateOp* op);
-    void storeRestoreToCountBarrier(int newSaveCount);
+    void storeRestoreToCountBarrier(OpenGLRenderer& renderer, StateOp* op, int newSaveCount);
 
     bool recordingComplexClip() const { return mComplexClipStackStart >= 0; }
 
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index d985ad0..36c95f9 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -352,7 +352,9 @@
         }
     }
     if (mAlpha < 1) {
-        if (mCaching || !mHasOverlappingRendering) {
+        if (mCaching) {
+            ALOGD("%*sSetOverrideLayerAlpha %.2f", level * 2, "", mAlpha);
+        } else if (!mHasOverlappingRendering) {
             ALOGD("%*sScaleAlpha %.2f", level * 2, "", mAlpha);
         } else {
             int flags = SkCanvas::kHasAlphaLayer_SaveFlag;
@@ -400,7 +402,9 @@
         }
     }
     if (mAlpha < 1) {
-        if (mCaching || !mHasOverlappingRendering) {
+        if (mCaching) {
+            renderer.setOverrideLayerAlpha(mAlpha);
+        } else if (!mHasOverlappingRendering) {
             renderer.scaleAlpha(mAlpha);
         } else {
             // TODO: should be able to store the size of a DL at record time and not
@@ -513,6 +517,7 @@
     DISPLAY_LIST_LOGD("%*sRestoreToCount %d", (level + 1) * 2, "", restoreTo);
     handler(mRestoreToCountOp->reinit(restoreTo), PROPERTY_SAVECOUNT);
     renderer.restoreToCount(restoreTo);
+    renderer.setOverrideLayerAlpha(1.0f);
 }
 
 }; // namespace uirenderer
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 4f2db69..9c3d058 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -117,7 +117,7 @@
         applyState(replayStruct.mRenderer, saveCount);
     }
 
-    virtual void applyState(OpenGLRenderer& renderer, int saveCount) = 0;
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) const = 0;
 };
 
 class DrawOp : public DisplayListOp {
@@ -223,7 +223,7 @@
         deferStruct.mDeferredList.addSave(deferStruct.mRenderer, this, newSaveCount);
     }
 
-    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
         renderer.save(mFlags);
     }
 
@@ -251,11 +251,12 @@
             : mCount(count) {}
 
     virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level) {
-        deferStruct.mDeferredList.addRestoreToCount(deferStruct.mRenderer, saveCount + mCount);
+        deferStruct.mDeferredList.addRestoreToCount(deferStruct.mRenderer,
+                this, saveCount + mCount);
         deferStruct.mRenderer.restoreToCount(saveCount + mCount);
     }
 
-    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
         renderer.restoreToCount(saveCount + mCount);
     }
 
@@ -293,7 +294,7 @@
                 mAlpha, mMode, mFlags);
     }
 
-    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
         renderer.saveLayer(mArea.left, mArea.top, mArea.right, mArea.bottom, mAlpha, mMode, mFlags);
     }
 
@@ -330,7 +331,7 @@
     TranslateOp(float dx, float dy)
             : mDx(dx), mDy(dy) {}
 
-    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
         renderer.translate(mDx, mDy);
     }
 
@@ -350,7 +351,7 @@
     RotateOp(float degrees)
             : mDegrees(degrees) {}
 
-    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
         renderer.rotate(mDegrees);
     }
 
@@ -369,7 +370,7 @@
     ScaleOp(float sx, float sy)
             : mSx(sx), mSy(sy) {}
 
-    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
         renderer.scale(mSx, mSy);
     }
 
@@ -389,7 +390,7 @@
     SkewOp(float sx, float sy)
             : mSx(sx), mSy(sy) {}
 
-    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
         renderer.skew(mSx, mSy);
     }
 
@@ -409,7 +410,7 @@
     SetMatrixOp(SkMatrix* matrix)
             : mMatrix(matrix) {}
 
-    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
         renderer.setMatrix(mMatrix);
     }
 
@@ -428,7 +429,7 @@
     ConcatMatrixOp(SkMatrix* matrix)
             : mMatrix(matrix) {}
 
-    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
         renderer.concatMatrix(mMatrix);
     }
 
@@ -471,7 +472,7 @@
     ClipRectOp(float left, float top, float right, float bottom, SkRegion::Op op)
             : ClipOp(op), mArea(left, top, right, bottom) {}
 
-    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
         renderer.clipRect(mArea.left, mArea.top, mArea.right, mArea.bottom, mOp);
     }
 
@@ -500,7 +501,7 @@
     ClipPathOp(SkPath* path, SkRegion::Op op)
             : ClipOp(op), mPath(path) {}
 
-    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
         renderer.clipPath(mPath, mOp);
     }
 
@@ -521,7 +522,7 @@
     ClipRegionOp(SkRegion* region, SkRegion::Op op)
             : ClipOp(op), mRegion(region) {}
 
-    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
         renderer.clipRegion(mRegion, mOp);
     }
 
@@ -540,7 +541,7 @@
 
 class ResetShaderOp : public StateOp {
 public:
-    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
         renderer.resetShader();
     }
 
@@ -555,7 +556,7 @@
 public:
     SetupShaderOp(SkiaShader* shader)
             : mShader(shader) {}
-    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
         renderer.setupShader(mShader);
     }
 
@@ -571,7 +572,7 @@
 
 class ResetColorFilterOp : public StateOp {
 public:
-    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
         renderer.resetColorFilter();
     }
 
@@ -587,7 +588,7 @@
     SetupColorFilterOp(SkiaColorFilter* colorFilter)
             : mColorFilter(colorFilter) {}
 
-    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
         renderer.setupColorFilter(mColorFilter);
     }
 
@@ -603,7 +604,7 @@
 
 class ResetShadowOp : public StateOp {
 public:
-    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
         renderer.resetShadow();
     }
 
@@ -619,7 +620,7 @@
     SetupShadowOp(float radius, float dx, float dy, int color)
             : mRadius(radius), mDx(dx), mDy(dy), mColor(color) {}
 
-    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
         renderer.setupShadow(mRadius, mDx, mDy, mColor);
     }
 
@@ -638,7 +639,7 @@
 
 class ResetPaintFilterOp : public StateOp {
 public:
-    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
         renderer.resetPaintFilter();
     }
 
@@ -654,7 +655,7 @@
     SetupPaintFilterOp(int clearBits, int setBits)
             : mClearBits(clearBits), mSetBits(setBits) {}
 
-    virtual void applyState(OpenGLRenderer& renderer, int saveCount) {
+    virtual void applyState(OpenGLRenderer& renderer, int saveCount) const {
         renderer.setupPaintFilter(mClearBits, mSetBits);
     }
 
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 07daa3b..0b8f7e6 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -248,9 +248,7 @@
 }
 
 status_t DisplayListRenderer::drawLayer(Layer* layer, float x, float y) {
-    mLayers.add(layer);
-    mCaches.resourceCache.incrementRefcount(layer);
-
+    layer = refLayer(layer);
     addDrawOp(new (alloc()) DrawLayerOp(layer, x, y));
     return DrawGlInfo::kStatusDone;
 }
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index 50e552f..19f7eb6 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -271,6 +271,12 @@
         return copy;
     }
 
+    inline Layer* refLayer(Layer* layer) {
+        mLayers.add(layer);
+        mCaches.resourceCache.incrementRefcount(layer);
+        return layer;
+    }
+
     inline SkBitmap* refBitmap(SkBitmap* bitmap) {
         // Note that this assumes the bitmap is immutable. There are cases this won't handle
         // correctly, such as creating the bitmap from scratch, drawing with it, changing its
diff --git a/libs/hwui/Dither.cpp b/libs/hwui/Dither.cpp
old mode 100755
new mode 100644
diff --git a/libs/hwui/Dither.h b/libs/hwui/Dither.h
old mode 100755
new mode 100644
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 1899002..63bb73f 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -18,6 +18,8 @@
 
 #include <utils/Log.h>
 
+#include "DisplayList.h"
+#include "DeferredDisplayList.h"
 #include "Layer.h"
 #include "LayerRenderer.h"
 #include "OpenGLRenderer.h"
@@ -43,15 +45,18 @@
     fbo = 0;
     stencil = NULL;
     debugDrawUpdate = false;
+    deferredList = NULL;
     Caches::getInstance().resourceCache.incrementRefcount(this);
 }
 
 Layer::~Layer() {
-    if (mesh) delete mesh;
-    if (meshIndices) delete meshIndices;
     if (colorFilter) Caches::getInstance().resourceCache.decrementRefcount(colorFilter);
     removeFbo();
     deleteTexture();
+
+    delete[] mesh;
+    delete[] meshIndices;
+    delete deferredList;
 }
 
 uint32_t Layer::computeIdealWidth(uint32_t layerWidth) {
@@ -70,6 +75,13 @@
         return true;
     }
 
+    const uint32_t maxTextureSize = Caches::getInstance().maxTextureSize;
+    if (desiredWidth > maxTextureSize || desiredHeight > maxTextureSize) {
+        ALOGW("Layer exceeds max. dimensions supported by the GPU (%dx%d, max=%dx%d)",
+                desiredWidth, desiredHeight, maxTextureSize, maxTextureSize);
+        return false;
+    }
+
     uint32_t oldWidth = getWidth();
     uint32_t oldHeight = getHeight();
 
@@ -133,5 +145,62 @@
     }
 }
 
+void Layer::defer() {
+    if (!deferredList) {
+        deferredList = new DeferredDisplayList;
+    }
+    DeferStateStruct deferredState(*deferredList, *renderer,
+            DisplayList::kReplayFlag_ClipChildren);
+
+    const float width = layer.getWidth();
+    const float height = layer.getHeight();
+
+    if (dirtyRect.isEmpty() || (dirtyRect.left <= 0 && dirtyRect.top <= 0 &&
+            dirtyRect.right >= width && dirtyRect.bottom >= height)) {
+        dirtyRect.set(0, 0, width, height);
+    }
+
+    renderer->initViewport(width, height);
+    renderer->setupFrameState(dirtyRect.left, dirtyRect.top,
+            dirtyRect.right, dirtyRect.bottom, !isBlend());
+
+    displayList->defer(deferredState, 0);
+
+    deferredUpdateScheduled = false;
+    displayList = NULL;
+}
+
+void Layer::flush() {
+    if (deferredList) {
+        renderer->setViewport(layer.getWidth(), layer.getHeight());
+        renderer->prepareDirty(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom,
+                !isBlend());
+
+        deferredList->flush(*renderer, dirtyRect);
+
+        renderer->finish();
+        renderer = NULL;
+
+        dirtyRect.setEmpty();
+        deferredList->clear();
+    }
+}
+
+void Layer::render() {
+    renderer->setViewport(layer.getWidth(), layer.getHeight());
+    renderer->prepareDirty(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom,
+            !isBlend());
+
+    renderer->drawDisplayList(displayList, dirtyRect, DisplayList::kReplayFlag_ClipChildren);
+
+    renderer->finish();
+    renderer = NULL;
+
+    dirtyRect.setEmpty();
+
+    deferredUpdateScheduled = false;
+    displayList = NULL;
+}
+
 }; // namespace uirenderer
 }; // namespace android
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index ccf1da5..27e0cf1 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -42,6 +42,8 @@
 // Forward declarations
 class OpenGLRenderer;
 class DisplayList;
+class DeferredDisplayList;
+class DeferStateStruct;
 
 /**
  * A layer has dimensions and is backed by an OpenGL texture or FBO.
@@ -271,6 +273,10 @@
         return transform;
     }
 
+    void defer();
+    void flush();
+    void render();
+
     /**
      * Bounds of the layer.
      */
@@ -379,6 +385,12 @@
      */
     mat4 transform;
 
+    /**
+     * Used to defer display lists when the layer is updated with a
+     * display list.
+     */
+    DeferredDisplayList* deferredList;
+
 }; // struct Layer
 
 }; // namespace uirenderer
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index 9aa9615..8451048 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -129,8 +129,8 @@
 void LayerRenderer::generateMesh() {
     if (mLayer->region.isRect() || mLayer->region.isEmpty()) {
         if (mLayer->mesh) {
-            delete mLayer->mesh;
-            delete mLayer->meshIndices;
+            delete[] mLayer->mesh;
+            delete[] mLayer->meshIndices;
 
             mLayer->mesh = NULL;
             mLayer->meshIndices = NULL;
@@ -153,8 +153,8 @@
     GLsizei elementCount = count * 6;
 
     if (mLayer->mesh && mLayer->meshElementCount < elementCount) {
-        delete mLayer->mesh;
-        delete mLayer->meshIndices;
+        delete[] mLayer->mesh;
+        delete[] mLayer->meshIndices;
 
         mLayer->mesh = NULL;
         mLayer->meshIndices = NULL;
@@ -222,6 +222,21 @@
         return NULL;
     }
 
+    // We first obtain a layer before comparing against the max texture size
+    // because layers are not allocated at the exact desired size. They are
+    // always created slighly larger to improve recycling
+    const uint32_t maxTextureSize = caches.maxTextureSize;
+    if (layer->getWidth() > maxTextureSize || layer->getHeight() > maxTextureSize) {
+        ALOGW("Layer exceeds max. dimensions supported by the GPU (%dx%d, max=%dx%d)",
+                width, height, maxTextureSize, maxTextureSize);
+
+        // Creating a new layer always increment its refcount by 1, this allows
+        // us to destroy the layer object if one was created for us
+        Caches::getInstance().resourceCache.decrementRefcount(layer);
+
+        return NULL;
+    }
+
     layer->setFbo(fbo);
     layer->layer.set(0.0f, 0.0f, width, height);
     layer->texCoords.set(0.0f, height / float(layer->getHeight()),
@@ -243,14 +258,11 @@
         layer->setEmpty(false);
         layer->allocateTexture(GL_RGBA, GL_UNSIGNED_BYTE);
 
+        // This should only happen if we run out of memory
         if (glGetError() != GL_NO_ERROR) {
-            ALOGD("Could not allocate texture for layer (fbo=%d %dx%d)",
-                    fbo, width, height);
-
+            ALOGE("Could not allocate texture for layer (fbo=%d %dx%d)", fbo, width, height);
             glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
-
-            Caches::getInstance().resourceCache.decrementRefcount(layer);
-
+            caches.resourceCache.decrementRefcount(layer);
             return NULL;
         }
     }
@@ -272,7 +284,6 @@
             layer->texCoords.set(0.0f, height / float(layer->getHeight()),
                     width / float(layer->getWidth()), 0.0f);
         } else {
-            Caches::getInstance().resourceCache.decrementRefcount(layer);
             return false;
         }
     }
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 2cf7183..1138998 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -114,12 +114,14 @@
         mCaches(Caches::getInstance()), mExtensions(Extensions::getInstance()) {
     mDrawModifiers.mShader = NULL;
     mDrawModifiers.mColorFilter = NULL;
+    mDrawModifiers.mOverrideLayerAlpha = 1.0f;
     mDrawModifiers.mHasShadow = false;
     mDrawModifiers.mHasDrawFilter = false;
 
     memcpy(mMeshVertices, gMeshVertices, sizeof(gMeshVertices));
 
     mFirstSnapshot = new Snapshot;
+    mFrameStarted = false;
 
     mScissorOptimizationDisabled = false;
 }
@@ -179,14 +181,11 @@
     mFirstSnapshot->viewport.set(0, 0, width, height);
 }
 
-status_t OpenGLRenderer::prepare(bool opaque) {
-    return prepareDirty(0.0f, 0.0f, mWidth, mHeight, opaque);
-}
-
-status_t OpenGLRenderer::prepareDirty(float left, float top,
+void OpenGLRenderer::setupFrameState(float left, float top,
         float right, float bottom, bool opaque) {
     mCaches.clearGarbage();
 
+    mOpaque = opaque;
     mSnapshot = new Snapshot(mFirstSnapshot,
             SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag);
     mSnapshot->fbo = getTargetFbo();
@@ -194,13 +193,17 @@
 
     mSnapshot->setClip(left, top, right, bottom);
     mTilingClip.set(left, top, right, bottom);
+}
+
+status_t OpenGLRenderer::startFrame() {
+    if (mFrameStarted) return DrawGlInfo::kStatusDone;
+    mFrameStarted = true;
+
     mDirtyClip = true;
 
-    updateLayers();
+    discardFramebuffer(mTilingClip.left, mTilingClip.top, mTilingClip.right, mTilingClip.bottom);
 
-    discardFramebuffer(left, top, right, bottom);
-
-    syncState();
+    glViewport(0, 0, mWidth, mHeight);
 
     // Functors break the tiling extension in pretty spectacular ways
     // This ensures we don't use tiling when a functor is going to be
@@ -211,7 +214,30 @@
 
     debugOverdraw(true, true);
 
-    return clear(left, top, right, bottom, opaque);
+    return clear(mTilingClip.left, mTilingClip.top,
+            mTilingClip.right, mTilingClip.bottom, mOpaque);
+}
+
+status_t OpenGLRenderer::prepare(bool opaque) {
+    return prepareDirty(0.0f, 0.0f, mWidth, mHeight, opaque);
+}
+
+status_t OpenGLRenderer::prepareDirty(float left, float top,
+        float right, float bottom, bool opaque) {
+    setupFrameState(left, top, right, bottom, opaque);
+
+    // Layer renderers will start the frame immediately
+    // The framebuffer renderer will first defer the display list
+    // for each layer and wait until the first drawing command
+    // to start the frame
+    if (mSnapshot->fbo == 0) {
+        syncState();
+        updateLayers();
+    } else {
+        return startFrame();
+    }
+
+    return DrawGlInfo::kStatusDone;
 }
 
 void OpenGLRenderer::discardFramebuffer(float left, float top, float right, float bottom) {
@@ -241,8 +267,6 @@
 }
 
 void OpenGLRenderer::syncState() {
-    glViewport(0, 0, mWidth, mHeight);
-
     if (mCaches.blend) {
         glEnable(GL_BLEND);
     } else {
@@ -312,6 +336,8 @@
         }
 #endif
     }
+
+    mFrameStarted = false;
 }
 
 void OpenGLRenderer::interrupt() {
@@ -406,6 +432,8 @@
 }
 
 status_t OpenGLRenderer::callDrawGLFunction(Functor* functor, Rect& dirty) {
+    if (mSnapshot->isIgnored()) return DrawGlInfo::kStatusDone;
+
     interrupt();
     detachFunctor(functor);
 
@@ -503,8 +531,8 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 bool OpenGLRenderer::updateLayer(Layer* layer, bool inFrame) {
-    if (layer->deferredUpdateScheduled && layer->renderer && layer->displayList) {
-        OpenGLRenderer* renderer = layer->renderer;
+    if (layer->deferredUpdateScheduled && layer->renderer &&
+            layer->displayList && layer->displayList->isRenderable()) {
         Rect& dirty = layer->dirtyRect;
 
         if (inFrame) {
@@ -512,20 +540,17 @@
             debugOverdraw(false, false);
         }
 
-        renderer->setViewport(layer->layer.getWidth(), layer->layer.getHeight());
-        renderer->prepareDirty(dirty.left, dirty.top, dirty.right, dirty.bottom, !layer->isBlend());
-        renderer->drawDisplayList(layer->displayList, dirty, DisplayList::kReplayFlag_ClipChildren);
-        renderer->finish();
+        if (CC_UNLIKELY(inFrame || mCaches.drawDeferDisabled)) {
+            layer->render();
+        } else {
+            layer->defer();
+        }
 
         if (inFrame) {
             resumeAfterLayer();
             startTiling(mSnapshot);
         }
 
-        dirty.setEmpty();
-        layer->deferredUpdateScheduled = false;
-        layer->renderer = NULL;
-        layer->displayList = NULL;
         layer->debugDrawUpdate = mCaches.debugLayersUpdates;
 
         return true;
@@ -535,25 +560,70 @@
 }
 
 void OpenGLRenderer::updateLayers() {
+    // If draw deferring is enabled this method will simply defer
+    // the display list of each individual layer. The layers remain
+    // in the layer updates list which will be cleared by flushLayers().
     int count = mLayerUpdates.size();
     if (count > 0) {
-        startMark("Layer Updates");
+        if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
+            startMark("Layer Updates");
+        } else {
+            startMark("Defer Layer Updates");
+        }
 
         // Note: it is very important to update the layers in reverse order
         for (int i = count - 1; i >= 0; i--) {
             Layer* layer = mLayerUpdates.itemAt(i);
             updateLayer(layer, false);
-            mCaches.resourceCache.decrementRefcount(layer);
+            if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
+                mCaches.resourceCache.decrementRefcount(layer);
+            }
         }
-        mLayerUpdates.clear();
 
+        if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
+            mLayerUpdates.clear();
+            glBindFramebuffer(GL_FRAMEBUFFER, getTargetFbo());
+        }
+        endMark();
+    }
+}
+
+void OpenGLRenderer::flushLayers() {
+    int count = mLayerUpdates.size();
+    if (count > 0) {
+        startMark("Apply Layer Updates");
+        char layerName[12];
+
+        // Note: it is very important to update the layers in reverse order
+        for (int i = count - 1; i >= 0; i--) {
+            sprintf(layerName, "Layer #%d", i);
+            startMark(layerName);
+
+            Layer* layer = mLayerUpdates.itemAt(i);
+            layer->flush();
+            mCaches.resourceCache.decrementRefcount(layer);
+
+            endMark();
+        }
+
+        mLayerUpdates.clear();
         glBindFramebuffer(GL_FRAMEBUFFER, getTargetFbo());
+
         endMark();
     }
 }
 
 void OpenGLRenderer::pushLayerUpdate(Layer* layer) {
     if (layer) {
+        // Make sure we don't introduce duplicates.
+        // SortedVector would do this automatically but we need to respect
+        // the insertion order. The linear search is not an issue since
+        // this list is usually very short (typically one item, at most a few)
+        for (int i = mLayerUpdates.size() - 1; i >= 0; i--) {
+            if (mLayerUpdates.itemAt(i) == layer) {
+                return;
+            }
+        }
         mLayerUpdates.push_back(layer);
         mCaches.resourceCache.incrementRefcount(layer);
     }
@@ -624,7 +694,10 @@
     }
 
     if (restoreLayer) {
+        endMark(); // Savelayer
+        startMark("ComposeLayer");
         composeLayer(current, previous);
+        endMark();
     }
 
     return restoreClip;
@@ -679,6 +752,17 @@
     }
 }
 
+void OpenGLRenderer::updateSnapshotIgnoreForLayer(const Rect& bounds, const Rect& clip,
+        bool fboLayer, int alpha) {
+    if (bounds.isEmpty() || bounds.getWidth() > mCaches.maxTextureSize ||
+            bounds.getHeight() > mCaches.maxTextureSize ||
+            (fboLayer && clip.isEmpty())) {
+        mSnapshot->empty = fboLayer;
+    } else {
+        mSnapshot->invisible = mSnapshot->invisible || (alpha <= ALPHA_THRESHOLD && fboLayer);
+    }
+}
+
 int OpenGLRenderer::saveLayerDeferred(float left, float top, float right, float bottom,
         int alpha, SkXfermode::Mode mode, int flags) {
     const GLuint previousFbo = mSnapshot->fbo;
@@ -692,8 +776,9 @@
         Rect bounds(left, top, right, bottom);
         Rect clip;
         calculateLayerBoundsAndClip(bounds, clip, true);
+        updateSnapshotIgnoreForLayer(bounds, clip, true, alpha);
 
-        if (!bounds.isEmpty() && !clip.isEmpty()) {
+        if (!mSnapshot->isIgnored()) {
             mSnapshot->resetTransform(-bounds.left, -bounds.top, 0.0f);
             mSnapshot->resetClip(clip.left, clip.top, clip.right, clip.bottom);
         }
@@ -765,17 +850,10 @@
     Rect clip;
     Rect bounds(left, top, right, bottom);
     calculateLayerBoundsAndClip(bounds, clip, fboLayer);
-
-    if (bounds.isEmpty() || bounds.getWidth() > mCaches.maxTextureSize ||
-            bounds.getHeight() > mCaches.maxTextureSize ||
-            (fboLayer && clip.isEmpty())) {
-        mSnapshot->empty = fboLayer;
-    } else {
-        mSnapshot->invisible = mSnapshot->invisible || (alpha <= ALPHA_THRESHOLD && fboLayer);
-    }
+    updateSnapshotIgnoreForLayer(bounds, clip, fboLayer, alpha);
 
     // Bail out if we won't draw in this snapshot
-    if (mSnapshot->invisible || mSnapshot->empty) {
+    if (mSnapshot->isIgnored()) {
         return false;
     }
 
@@ -797,6 +875,7 @@
     mSnapshot->flags |= Snapshot::kFlagIsLayer;
     mSnapshot->layer = layer;
 
+    startMark("SaveLayer");
     if (fboLayer) {
         return createFboLayer(layer, bounds, clip, previousFbo);
     } else {
@@ -996,7 +1075,7 @@
             layer->setFilter(GL_LINEAR, true);
         }
 
-        float alpha = layer->getAlpha() / 255.0f * mSnapshot->alpha;
+        float alpha = getLayerAlpha(layer);
         bool blend = layer->isBlend() || alpha < 1.0f;
         drawTextureMesh(x, y, x + rect.getWidth(), y + rect.getHeight(),
                 layer->getTexture(), alpha, layer->getMode(), blend,
@@ -1034,7 +1113,7 @@
             rects = safeRegion.getArray(&count);
         }
 
-        const float alpha = layer->getAlpha() / 255.0f * mSnapshot->alpha;
+        const float alpha = getLayerAlpha(layer);
         const float texX = 1.0f / float(layer->getWidth());
         const float texY = 1.0f / float(layer->getHeight());
         const float height = rect.getHeight();
@@ -1249,8 +1328,6 @@
         } else {
             state.mBounds.set(currentClip);
         }
-        state.mDrawModifiers = mDrawModifiers;
-        state.mAlpha = mSnapshot->alpha;
     }
 
     if (stateDeferFlags & kStateDeferFlag_Clip) {
@@ -1259,18 +1336,18 @@
         state.mClip.setEmpty();
     }
 
-    // transform always deferred
+    // Transform, drawModifiers, and alpha always deferred, since they are used by state operations
+    // (Note: saveLayer/restore use colorFilter and alpha, so we just save restore everything)
     state.mMatrix.load(currentMatrix);
+    state.mDrawModifiers = mDrawModifiers;
+    state.mAlpha = mSnapshot->alpha;
     return false;
 }
 
-void OpenGLRenderer::restoreDisplayState(const DeferredDisplayState& state, int stateDeferFlags) {
+void OpenGLRenderer::restoreDisplayState(const DeferredDisplayState& state) {
     currentTransform().load(state.mMatrix);
-
-    if (stateDeferFlags & kStateDeferFlag_Draw) {
-        mDrawModifiers = state.mDrawModifiers;
-        mSnapshot->alpha = state.mAlpha;
-    }
+    mDrawModifiers = state.mDrawModifiers;
+    mSnapshot->alpha = state.mAlpha;
 
     if (!state.mClip.isEmpty()) {
         mSnapshot->setClip(state.mClip.left, state.mClip.top, state.mClip.right, state.mClip.bottom);
@@ -1832,6 +1909,7 @@
     // will be performed by the display list itself
     if (displayList && displayList->isRenderable()) {
         if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
+            startFrame();
             ReplayStateStruct replayStruct(*this, dirty, replayFlags);
             displayList->replay(replayStruct, 0);
             return replayStruct.mDrawGlStatus;
@@ -1840,6 +1918,10 @@
         DeferredDisplayList deferredList;
         DeferStateStruct deferStruct(deferredList, *this, replayFlags);
         displayList->defer(deferStruct, 0);
+
+        flushLayers();
+        startFrame();
+
         return deferredList.flush(*this, dirty);
     }
 
@@ -2156,7 +2238,7 @@
         float left, float top, float right, float bottom, SkPaint* paint) {
     int alpha;
     SkXfermode::Mode mode;
-    getAlphaAndModeDirect(paint, &alpha, &mode);
+    getAlphaAndMode(paint, &alpha, &mode);
 
     return drawPatch(bitmap, xDivs, yDivs, colors, width, height, numColors,
             left, top, right, bottom, alpha, mode);
@@ -2909,7 +2991,7 @@
         if (layer->region.isRect()) {
             composeLayerRect(layer, layer->regionRect);
         } else if (layer->mesh) {
-            const float a = layer->getAlpha() / 255.0f * mSnapshot->alpha;
+            const float a = getLayerAlpha(layer);
             setupDraw();
             setupDrawWithTexture();
             setupDrawColor(a, a, a, a);
@@ -3365,10 +3447,24 @@
     TextureVertex::setUV(v++, u2, v2);
 }
 
-void OpenGLRenderer::getAlphaAndMode(SkPaint* paint, int* alpha, SkXfermode::Mode* mode) {
+void OpenGLRenderer::getAlphaAndMode(SkPaint* paint, int* alpha, SkXfermode::Mode* mode) const {
     getAlphaAndModeDirect(paint, alpha,  mode);
+    if (mDrawModifiers.mOverrideLayerAlpha < 1.0f) {
+        // if drawing a layer, ignore the paint's alpha
+        *alpha = mDrawModifiers.mOverrideLayerAlpha;
+    }
     *alpha *= mSnapshot->alpha;
 }
 
+float OpenGLRenderer::getLayerAlpha(Layer* layer) const {
+    float alpha;
+    if (mDrawModifiers.mOverrideLayerAlpha < 1.0f) {
+        alpha = mDrawModifiers.mOverrideLayerAlpha;
+    } else {
+        alpha = layer->getAlpha() / 255.0f;
+    }
+    return alpha * mSnapshot->alpha;
+}
+
 }; // namespace uirenderer
 }; // namespace android
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 7bb9395..dd7a5a2 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -51,6 +51,7 @@
 struct DrawModifiers {
     SkiaShader* mShader;
     SkiaColorFilter* mColorFilter;
+    float mOverrideLayerAlpha;
 
     // Drop shadow
     bool mHasShadow;
@@ -275,15 +276,17 @@
     virtual void resetPaintFilter();
     virtual void setupPaintFilter(int clearBits, int setBits);
 
+    // If this value is set to < 1.0, it overrides alpha set on layer (see drawBitmap, drawLayer)
+    void setOverrideLayerAlpha(float alpha) { mDrawModifiers.mOverrideLayerAlpha = alpha; }
+
     SkPaint* filterPaint(SkPaint* paint);
 
     bool storeDisplayState(DeferredDisplayState& state, int stateDeferFlags);
-    void restoreDisplayState(const DeferredDisplayState& state, int stateDeferFlags);
+    void restoreDisplayState(const DeferredDisplayState& state);
 
     const DrawModifiers& getDrawModifiers() { return mDrawModifiers; }
     void setDrawModifiers(const DrawModifiers& drawModifiers) { mDrawModifiers = drawModifiers; }
 
-    // TODO: what does this mean? no perspective? no rotate?
     ANDROID_API bool isCurrentTransformSimple() {
         return mSnapshot->transform->isSimple();
     }
@@ -325,7 +328,8 @@
     /**
      * Gets the alpha and xfermode out of a paint object. If the paint is null
      * alpha will be 255 and the xfermode will be SRC_OVER. This method does
-     * not multiply the paint's alpha by the current snapshot's alpha.
+     * not multiply the paint's alpha by the current snapshot's alpha, and does
+     * not replace the alpha with the overrideLayerAlpha
      *
      * @param paint The paint to extract values from
      * @param alpha Where to store the resulting alpha
@@ -362,6 +366,18 @@
     void initViewport(int width, int height);
 
     /**
+     * Perform the setup specific to a frame. This method does not
+     * issue any OpenGL commands.
+     */
+    void setupFrameState(float left, float top, float right, float bottom, bool opaque);
+
+    /**
+     * Indicates the start of rendering. This method will setup the
+     * initial OpenGL state (viewport, clearing the buffer, etc.)
+     */
+    status_t startFrame();
+
+    /**
      * Clears the underlying surface if needed.
      */
     virtual status_t clear(float left, float top, float right, float bottom, bool opaque);
@@ -438,13 +454,21 @@
 
     /**
      * Gets the alpha and xfermode out of a paint object. If the paint is null
-     * alpha will be 255 and the xfermode will be SRC_OVER.
+     * alpha will be 255 and the xfermode will be SRC_OVER. Accounts for both
+     * snapshot alpha, and overrideLayerAlpha
      *
      * @param paint The paint to extract values from
      * @param alpha Where to store the resulting alpha
      * @param mode Where to store the resulting xfermode
      */
-    inline void getAlphaAndMode(SkPaint* paint, int* alpha, SkXfermode::Mode* mode);
+    inline void getAlphaAndMode(SkPaint* paint, int* alpha, SkXfermode::Mode* mode) const;
+
+    /**
+     * Gets the alpha from a layer, accounting for snapshot alpha and overrideLayerAlpha
+     *
+     * @param layer The layer from which the alpha is extracted
+     */
+    inline float getLayerAlpha(Layer* layer) const;
 
     /**
      * Safely retrieves the mode from the specified xfermode. If the specified
@@ -544,11 +568,17 @@
     bool quickRejectPreStroke(float left, float top, float right, float bottom, SkPaint* paint);
 
     /**
-     * given the local bounds of the layer, calculates ...
+     * Given the local bounds of the layer, calculates ...
      */
     void calculateLayerBoundsAndClip(Rect& bounds, Rect& clip, bool fboLayer);
 
     /**
+     * Given the local bounds + clip of the layer, updates current snapshot's empty/invisible
+     */
+    void updateSnapshotIgnoreForLayer(const Rect& bounds, const Rect& clip,
+            bool fboLayer, int alpha);
+
+    /**
      * Creates a new layer stored in the specified snapshot.
      *
      * @param snapshot The snapshot associated with the new layer
@@ -897,6 +927,7 @@
 
     bool updateLayer(Layer* layer, bool inFrame);
     void updateLayers();
+    void flushLayers();
 
     /**
      * Renders the specified region as a series of rectangles. This method
@@ -948,6 +979,10 @@
     sp<Snapshot> mSnapshot;
     // State used to define the clipping region
     Rect mTilingClip;
+    // Is the target render surface opaque
+    bool mOpaque;
+    // Is a frame currently being rendered
+    bool mFrameStarted;
 
     // Used to draw textured quads
     TextureVertex mMeshVertices[4];
@@ -996,6 +1031,7 @@
     String8 mName;
 
     friend class DisplayListRenderer;
+    friend class Layer;
     friend class TextSetupFunctor;
 
 }; // class OpenGLRenderer
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index 07c4207..fdb10e2 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -262,12 +262,13 @@
 
     PathTexture* texture = createTexture(left, top, offset, width, height,
             path->getGenerationID());
-    addTexture(entry, &bitmap, texture);
+    generateTexture(entry, &bitmap, texture);
 
     return texture;
 }
 
-void PathCache::addTexture(const PathDescription& entry, SkBitmap* bitmap, PathTexture* texture) {
+void PathCache::generateTexture(const PathDescription& entry, SkBitmap* bitmap,
+        PathTexture* texture, bool addToCache) {
     generateTexture(*bitmap, texture);
 
     uint32_t size = texture->width * texture->height;
@@ -278,7 +279,9 @@
         if (mDebugEnabled) {
             ALOGD("Shape created, size = %d", size);
         }
-        mCache.put(entry, texture);
+        if (addToCache) {
+            mCache.put(entry, texture);
+        }
     } else {
         texture->cleanup = true;
     }
@@ -414,7 +417,7 @@
             // producing the bitmap, so let's wait
             SkBitmap* bitmap = task->getResult();
             if (bitmap) {
-                addTexture(entry, bitmap, texture);
+                generateTexture(entry, bitmap, texture, false);
                 texture->clearTask();
             } else {
                 ALOGW("Path too large to be rendered into a texture");
@@ -423,6 +426,8 @@
                 mCache.remove(entry);
             }
         } else if (path->getGenerationID() != texture->generation) {
+            // The size of the path might have changed so we first
+            // remove the entry from the cache
             mCache.remove(entry);
             texture = addTexture(entry, path, paint);
         }
diff --git a/libs/hwui/PathCache.h b/libs/hwui/PathCache.h
index 1467231..dd1f996 100644
--- a/libs/hwui/PathCache.h
+++ b/libs/hwui/PathCache.h
@@ -253,7 +253,13 @@
     PathTexture* addTexture(const PathDescription& entry,
             const SkPath *path, const SkPaint* paint);
     PathTexture* addTexture(const PathDescription& entry, SkBitmap* bitmap);
-    void addTexture(const PathDescription& entry, SkBitmap* bitmap, PathTexture* texture);
+
+    /**
+     * Generates the texture from a bitmap into the specified texture structure.
+     */
+    void generateTexture(SkBitmap& bitmap, Texture* texture);
+    void generateTexture(const PathDescription& entry, SkBitmap* bitmap, PathTexture* texture,
+            bool addToCache = true);
 
     PathTexture* get(const PathDescription& entry) {
         return mCache.get(entry);
@@ -282,11 +288,6 @@
         return true;
     }
 
-    /**
-     * Generates the texture from a bitmap into the specified texture structure.
-     */
-    void generateTexture(SkBitmap& bitmap, Texture* texture);
-
     void init();
 
     class PathTask: public Task<SkBitmap*> {
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 135d2c8..0f160ce 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -2047,11 +2047,28 @@
     }
 
     /**
+     * Register a component to be the sole receiver of MEDIA_BUTTON intents.  This is like
+     * {@link #registerMediaButtonEventReceiver(android.content.ComponentName)}, but allows
+     * the buttons to go to any PendingIntent.  Note that you should only use this form if
+     * you know you will continue running for the full time until unregistering the
+     * PendingIntent.
+     * @param eventReceiver target that will receive media button intents.  The PendingIntent
+     * will be sent as-is when a media button action occurs, with {@link Intent#EXTRA_KEY_EVENT}
+     * added and holding the key code of the media button that was pressed.
+     */
+    public void registerMediaButtonEventReceiver(PendingIntent eventReceiver) {
+        if (eventReceiver == null) {
+            return;
+        }
+        registerMediaButtonIntent(eventReceiver, null);
+    }
+
+    /**
      * @hide
      * no-op if (pi == null) or (eventReceiver == null)
      */
     public void registerMediaButtonIntent(PendingIntent pi, ComponentName eventReceiver) {
-        if ((pi == null) || (eventReceiver == null)) {
+        if (pi == null) {
             Log.e(TAG, "Cannot call registerMediaButtonIntent() with a null parameter");
             return;
         }
@@ -2110,16 +2127,28 @@
         mediaButtonIntent.setComponent(eventReceiver);
         PendingIntent pi = PendingIntent.getBroadcast(mContext,
                 0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/);
-        unregisterMediaButtonIntent(pi, eventReceiver);
+        unregisterMediaButtonIntent(pi);
+    }
+
+    /**
+     * Unregister the receiver of MEDIA_BUTTON intents.
+     * @param eventReceiver same PendingIntent that was registed with
+     *      {@link #registerMediaButtonEventReceiver(PendingIntent)}.
+     */
+    public void unregisterMediaButtonEventReceiver(PendingIntent eventReceiver) {
+        if (eventReceiver == null) {
+            return;
+        }
+        unregisterMediaButtonIntent(eventReceiver);
     }
 
     /**
      * @hide
      */
-    public void unregisterMediaButtonIntent(PendingIntent pi, ComponentName eventReceiver) {
+    public void unregisterMediaButtonIntent(PendingIntent pi) {
         IAudioService service = getService();
         try {
-            service.unregisterMediaButtonIntent(pi, eventReceiver);
+            service.unregisterMediaButtonIntent(pi);
         } catch (RemoteException e) {
             Log.e(TAG, "Dead object in unregisterMediaButtonIntent"+e);
         }
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 3e4d6f3..23f6e47 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -165,13 +165,10 @@
     private static final int MSG_PERSIST_SAFE_VOLUME_STATE = 28;
     private static final int MSG_PROMOTE_RCC = 29;
     private static final int MSG_BROADCAST_BT_CONNECTION_STATE = 30;
+    private static final int MSG_UNLOAD_SOUND_EFFECTS = 31;
+    private static final int MSG_RCC_NEW_PLAYBACK_STATE = 32;
 
 
-    // flags for MSG_PERSIST_VOLUME indicating if current and/or last audible volume should be
-    // persisted
-    private static final int PERSIST_CURRENT = 0x1;
-    private static final int PERSIST_LAST_AUDIBLE = 0x2;
-
     private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000;
     // Timeout for connection to bluetooth headset service
     private static final int BT_HEADSET_CNCT_TIMEOUT_MS = 3000;
@@ -257,6 +254,8 @@
     };
     private int[] mStreamVolumeAlias;
 
+    private final boolean mUseFixedVolume;
+
     // stream names used by dumpStreamStates()
     private final String[] STREAM_NAMES = new String[] {
             "STREAM_VOICE_CALL",
@@ -487,6 +486,9 @@
         mSafeMediaVolumeIndex = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_safe_media_volume_index) * 10;
 
+        mUseFixedVolume = mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_useFixedVolume);
+
         readPersistedSettings();
         mSettingsObserver = new SettingsObserver();
         updateStreamVolumeAlias(false /*updateVolumes*/);
@@ -575,14 +577,10 @@
         for (int streamType = 0; streamType < numStreamTypes; streamType++) {
             if (streamType != mStreamVolumeAlias[streamType]) {
                 mStreamStates[streamType].
-                                    setAllIndexes(mStreamStates[mStreamVolumeAlias[streamType]],
-                                                  false /*lastAudible*/);
-                mStreamStates[streamType].
-                                    setAllIndexes(mStreamStates[mStreamVolumeAlias[streamType]],
-                                                  true /*lastAudible*/);
+                                    setAllIndexes(mStreamStates[mStreamVolumeAlias[streamType]]);
             }
             // apply stream volume
-            if (mStreamStates[streamType].muteCount() == 0) {
+            if (!mStreamStates[streamType].isMuted()) {
                 mStreamStates[streamType].applyAllVolumes();
             }
         }
@@ -626,10 +624,7 @@
         }
         mStreamVolumeAlias[AudioSystem.STREAM_DTMF] = dtmfStreamAlias;
         if (updateVolumes) {
-            mStreamStates[AudioSystem.STREAM_DTMF].setAllIndexes(mStreamStates[dtmfStreamAlias],
-                                                                 false /*lastAudible*/);
-            mStreamStates[AudioSystem.STREAM_DTMF].setAllIndexes(mStreamStates[dtmfStreamAlias],
-                                                                 true /*lastAudible*/);
+            mStreamStates[AudioSystem.STREAM_DTMF].setAllIndexes(mStreamStates[dtmfStreamAlias]);
             sendMsg(mAudioHandler,
                     MSG_SET_ALL_VOLUMES,
                     SENDMSG_QUEUE,
@@ -678,6 +673,9 @@
         if (ringerMode != ringerModeFromSettings) {
             Settings.Global.putInt(cr, Settings.Global.MODE_RINGER, ringerMode);
         }
+        if (mUseFixedVolume) {
+            ringerMode = AudioManager.RINGER_MODE_NORMAL;
+        }
         synchronized(mSettingsLock) {
             mRingerMode = ringerMode;
 
@@ -736,6 +734,10 @@
 
         boolean masterMute = System.getIntForUser(cr, System.VOLUME_MASTER_MUTE,
                                                   0, UserHandle.USER_CURRENT) == 1;
+        if (mUseFixedVolume) {
+            masterMute = false;
+            AudioSystem.setMasterVolume(1.0f);
+        }
         AudioSystem.setMasterMute(masterMute);
         broadcastMasterMuteStatus(masterMute);
 
@@ -776,7 +778,7 @@
         }
     }
 
-    /** @see AudioManager#adjustVolume(int, int, int) */
+    /** @see AudioManager#adjustVolume(int, int) */
     public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) {
         if (DEBUG_VOL) Log.d(TAG, "adjustSuggestedStreamVolume() stream="+suggestedStreamType);
         int streamType;
@@ -806,6 +808,9 @@
 
     /** @see AudioManager#adjustStreamVolume(int, int, int) */
     public void adjustStreamVolume(int streamType, int direction, int flags) {
+        if (mUseFixedVolume) {
+            return;
+        }
         if (DEBUG_VOL) Log.d(TAG, "adjustStreamVolume() stream="+streamType+", dir="+direction);
 
         ensureValidDirection(direction);
@@ -819,14 +824,9 @@
 
         final int device = getDeviceForStream(streamTypeAlias);
 
-        // get last audible index if stream is muted, current index otherwise
-        int aliasIndex = streamState.getIndex(device,
-                        (streamState.muteCount() != 0) /* lastAudible */);
+        int aliasIndex = streamState.getIndex(device);
         boolean adjustVolume = true;
-
         int step;
-        int index;
-        int oldIndex;
 
         // reset any pending volume command
         synchronized (mSafeMediaVolumeState) {
@@ -855,69 +855,48 @@
             step = rescaleIndex(10, streamType, streamTypeAlias);
         }
 
-        if ((direction == AudioManager.ADJUST_RAISE) &&
-                !checkSafeMediaVolume(streamTypeAlias, aliasIndex + step, device)) {
-            index = mStreamStates[streamType].getIndex(device,
-                                                 (streamState.muteCount() != 0)  /* lastAudible */);
-            oldIndex = index;
-            mVolumePanel.postDisplaySafeVolumeWarning(flags);
-        } else {
-            // If either the client forces allowing ringer modes for this adjustment,
-            // or the stream type is one that is affected by ringer modes
-            if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
-                    (streamTypeAlias == getMasterStreamType())) {
-                int ringerMode = getRingerMode();
-                // do not vibrate if already in vibrate mode
-                if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
-                    flags &= ~AudioManager.FLAG_VIBRATE;
-                }
-                // Check if the ringer mode changes with this volume adjustment. If
-                // it does, it will handle adjusting the volume, so we won't below
-                adjustVolume = checkForRingerModeChange(aliasIndex, direction, step);
-                if ((streamTypeAlias == getMasterStreamType()) &&
-                        (mRingerMode == AudioManager.RINGER_MODE_SILENT)) {
-                    streamState.setLastAudibleIndex(0, device);
-                }
+        // If either the client forces allowing ringer modes for this adjustment,
+        // or the stream type is one that is affected by ringer modes
+        if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
+                (streamTypeAlias == getMasterStreamType())) {
+            int ringerMode = getRingerMode();
+            // do not vibrate if already in vibrate mode
+            if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
+                flags &= ~AudioManager.FLAG_VIBRATE;
             }
+            // Check if the ringer mode changes with this volume adjustment. If
+            // it does, it will handle adjusting the volume, so we won't below
+            adjustVolume = checkForRingerModeChange(aliasIndex, direction, step);
+        }
 
-            // If stream is muted, adjust last audible index only
-            oldIndex = mStreamStates[streamType].getIndex(device,
-                    (mStreamStates[streamType].muteCount() != 0) /* lastAudible */);
+        int oldIndex = mStreamStates[streamType].getIndex(device);
 
-            if (streamState.muteCount() != 0) {
-                if (adjustVolume) {
-                    // Post a persist volume msg
-                    // no need to persist volume on all streams sharing the same alias
-                    streamState.adjustLastAudibleIndex(direction * step, device);
-                    sendMsg(mAudioHandler,
-                            MSG_PERSIST_VOLUME,
-                            SENDMSG_QUEUE,
-                            PERSIST_LAST_AUDIBLE,
-                            device,
-                            streamState,
-                            PERSIST_DELAY);
-                }
-                index = mStreamStates[streamType].getIndex(device, true  /* lastAudible */);
-            } else {
-                if (adjustVolume && streamState.adjustIndex(direction * step, device)) {
-                    // Post message to set system volume (it in turn will post a message
-                    // to persist). Do not change volume if stream is muted.
-                    sendMsg(mAudioHandler,
-                            MSG_SET_DEVICE_VOLUME,
-                            SENDMSG_QUEUE,
-                            device,
-                            0,
-                            streamState,
-                            0);
-                }
-                index = mStreamStates[streamType].getIndex(device, false  /* lastAudible */);
+        if (adjustVolume && (direction != AudioManager.ADJUST_SAME)) {
+            if ((direction == AudioManager.ADJUST_RAISE) &&
+                    !checkSafeMediaVolume(streamTypeAlias, aliasIndex + step, device)) {
+                Log.e(TAG, "adjustStreamVolume() safe volume index = "+oldIndex);
+                mVolumePanel.postDisplaySafeVolumeWarning(flags);
+            } else if (streamState.adjustIndex(direction * step, device)) {
+                // Post message to set system volume (it in turn will post a message
+                // to persist). Do not change volume if stream is muted.
+                sendMsg(mAudioHandler,
+                        MSG_SET_DEVICE_VOLUME,
+                        SENDMSG_QUEUE,
+                        device,
+                        0,
+                        streamState,
+                        0);
             }
         }
+        int index = mStreamStates[streamType].getIndex(device);
         sendVolumeUpdate(streamType, oldIndex, index, flags);
     }
 
-    /** @see AudioManager#adjustMasterVolume(int) */
+    /** @see AudioManager#adjustMasterVolume(int, int) */
     public void adjustMasterVolume(int steps, int flags) {
+        if (mUseFixedVolume) {
+            return;
+        }
         ensureValidSteps(steps);
         int volume = Math.round(AudioSystem.getMasterVolume() * MAX_MASTER_VOLUME);
         int delta = 0;
@@ -949,6 +928,7 @@
     };
 
     private void onSetStreamVolume(int streamType, int index, int flags, int device) {
+        setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, false);
         // setting volume on master stream type also controls silent mode
         if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
                 (mStreamVolumeAlias[streamType] == getMasterStreamType())) {
@@ -956,22 +936,19 @@
             if (index == 0) {
                 newRingerMode = mHasVibrator ? AudioManager.RINGER_MODE_VIBRATE
                                               : AudioManager.RINGER_MODE_SILENT;
-                setStreamVolumeInt(mStreamVolumeAlias[streamType],
-                                   index,
-                                   device,
-                                   false,
-                                   true);
             } else {
                 newRingerMode = AudioManager.RINGER_MODE_NORMAL;
             }
             setRingerMode(newRingerMode);
         }
-
-        setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, false, true);
     }
 
     /** @see AudioManager#setStreamVolume(int, int, int) */
     public void setStreamVolume(int streamType, int index, int flags) {
+        if (mUseFixedVolume) {
+            return;
+        }
+
         ensureValidStreamType(streamType);
         VolumeStreamState streamState = mStreamStates[mStreamVolumeAlias[streamType]];
 
@@ -982,9 +959,7 @@
             // reset any pending volume command
             mPendingVolumeCommand = null;
 
-            // get last audible index if stream is muted, current index otherwise
-            oldIndex = streamState.getIndex(device,
-                                            (streamState.muteCount() != 0) /* lastAudible */);
+            oldIndex = streamState.getIndex(device);
 
             index = rescaleIndex(index * 10, streamType, mStreamVolumeAlias[streamType]);
 
@@ -1010,9 +985,7 @@
                                                     streamType, index, flags, device);
             } else {
                 onSetStreamVolume(streamType, index, flags, device);
-                // get last audible index if stream is muted, current index otherwise
-                index = mStreamStates[streamType].getIndex(device,
-                                    (mStreamStates[streamType].muteCount() != 0) /* lastAudible */);
+                index = mStreamStates[streamType].getIndex(device);
             }
         }
         sendVolumeUpdate(streamType, oldIndex, index, flags);
@@ -1174,55 +1147,44 @@
      * @param device the device whose volume must be changed
      * @param force If true, set the volume even if the desired volume is same
      * as the current volume.
-     * @param lastAudible If true, stores new index as last audible one
      */
     private void setStreamVolumeInt(int streamType,
                                     int index,
                                     int device,
-                                    boolean force,
-                                    boolean lastAudible) {
+                                    boolean force) {
         VolumeStreamState streamState = mStreamStates[streamType];
 
-        // If stream is muted, set last audible index only
-        if (streamState.muteCount() != 0) {
-            // Do not allow last audible index to be 0
-            if (index != 0) {
-                streamState.setLastAudibleIndex(index, device);
-                // Post a persist volume msg
-                sendMsg(mAudioHandler,
-                        MSG_PERSIST_VOLUME,
-                        SENDMSG_QUEUE,
-                        PERSIST_LAST_AUDIBLE,
-                        device,
-                        streamState,
-                        PERSIST_DELAY);
-            }
-        } else {
-            if (streamState.setIndex(index, device, lastAudible) || force) {
-                // Post message to set system volume (it in turn will post a message
-                // to persist).
-                sendMsg(mAudioHandler,
-                        MSG_SET_DEVICE_VOLUME,
-                        SENDMSG_QUEUE,
-                        device,
-                        0,
-                        streamState,
-                        0);
-            }
+        if (streamState.setIndex(index, device) || force) {
+            // Post message to set system volume (it in turn will post a message
+            // to persist).
+            sendMsg(mAudioHandler,
+                    MSG_SET_DEVICE_VOLUME,
+                    SENDMSG_QUEUE,
+                    device,
+                    0,
+                    streamState,
+                    0);
         }
     }
 
     /** @see AudioManager#setStreamSolo(int, boolean) */
     public void setStreamSolo(int streamType, boolean state, IBinder cb) {
+        if (mUseFixedVolume) {
+            return;
+        }
+
         for (int stream = 0; stream < mStreamStates.length; stream++) {
             if (!isStreamAffectedByMute(stream) || stream == streamType) continue;
-            // Bring back last audible volume
             mStreamStates[stream].mute(cb, state);
          }
     }
 
     /** @see AudioManager#setStreamMute(int, boolean) */
     public void setStreamMute(int streamType, boolean state, IBinder cb) {
+        if (mUseFixedVolume) {
+            return;
+        }
+
         if (isStreamAffectedByMute(streamType)) {
             mStreamStates[streamType].mute(cb, state);
         }
@@ -1230,11 +1192,15 @@
 
     /** get stream mute state. */
     public boolean isStreamMute(int streamType) {
-        return (mStreamStates[streamType].muteCount() != 0);
+        return mStreamStates[streamType].isMuted();
     }
 
-    /** @see AudioManager#setMasterMute(boolean, IBinder) */
+    /** @see AudioManager#setMasterMute(boolean, int) */
     public void setMasterMute(boolean state, int flags, IBinder cb) {
+        if (mUseFixedVolume) {
+            return;
+        }
+
         if (state != AudioSystem.getMasterMute()) {
             AudioSystem.setMasterMute(state);
             // Post a persist master volume msg
@@ -1253,8 +1219,12 @@
     public int getStreamVolume(int streamType) {
         ensureValidStreamType(streamType);
         int device = getDeviceForStream(streamType);
-        int index = mStreamStates[streamType].getIndex(device, false  /* lastAudible */);
+        int index = mStreamStates[streamType].getIndex(device);
 
+        // by convention getStreamVolume() returns 0 when a stream is muted.
+        if (mStreamStates[streamType].isMuted()) {
+            index = 0;
+        }
         if (index != 0 && (mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) &&
                 (device & mFixedVolumeDevices) != 0) {
             index = mStreamStates[streamType].getMaxIndex();
@@ -1268,6 +1238,10 @@
     }
 
     public void setMasterVolume(int volume, int flags) {
+        if (mUseFixedVolume) {
+            return;
+        }
+
         if (volume < 0) {
             volume = 0;
         } else if (volume > MAX_MASTER_VOLUME) {
@@ -1307,7 +1281,7 @@
     public int getLastAudibleStreamVolume(int streamType) {
         ensureValidStreamType(streamType);
         int device = getDeviceForStream(streamType);
-        return (mStreamStates[streamType].getIndex(device, true  /* lastAudible */) + 5) / 10;
+        return (mStreamStates[streamType].getIndex(device) + 5) / 10;
     }
 
     /** Get last audible master volume before it was muted. */
@@ -1315,7 +1289,7 @@
         return Math.round(AudioSystem.getMasterVolume() * MAX_MASTER_VOLUME);
     }
 
-    /** @see AudioManager#getMasterStreamType(int) */
+    /** @see AudioManager#getMasterStreamType()  */
     public int getMasterStreamType() {
         if (mVoiceCapable) {
             return AudioSystem.STREAM_RING;
@@ -1339,6 +1313,10 @@
 
     /** @see AudioManager#setRingerMode(int) */
     public void setRingerMode(int ringerMode) {
+        if (mUseFixedVolume) {
+            return;
+        }
+
         if ((ringerMode == AudioManager.RINGER_MODE_VIBRATE) && !mHasVibrator) {
             ringerMode = AudioManager.RINGER_MODE_SILENT;
         }
@@ -1368,7 +1346,7 @@
                     if (mVoiceCapable &&
                             mStreamVolumeAlias[streamType] == AudioSystem.STREAM_RING) {
                         synchronized (mStreamStates[streamType]) {
-                            Set set = mStreamStates[streamType].mLastAudibleIndex.entrySet();
+                            Set set = mStreamStates[streamType].mIndex.entrySet();
                             Iterator i = set.iterator();
                             while (i.hasNext()) {
                                 Map.Entry entry = (Map.Entry)i.next();
@@ -1398,6 +1376,10 @@
     }
 
     private void restoreMasterVolume() {
+        if (mUseFixedVolume) {
+            AudioSystem.setMasterVolume(1.0f);
+            return;
+        }
         if (mUseMasterVolume) {
             float volume = Settings.System.getFloatForUser(mContentResolver,
                     Settings.System.VOLUME_MASTER, -1.0f, UserHandle.USER_CURRENT);
@@ -1486,7 +1468,9 @@
             // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
             // SCO connections not started by the application changing the mode
             if (newModeOwnerPid != 0) {
-                 disconnectBluetoothSco(newModeOwnerPid);
+                final long ident = Binder.clearCallingIdentity();
+                disconnectBluetoothSco(newModeOwnerPid);
+                Binder.restoreCallingIdentity(ident);
             }
         }
 
@@ -1611,8 +1595,8 @@
                 streamType = AudioManager.STREAM_MUSIC;
             }
             int device = getDeviceForStream(streamType);
-            int index = mStreamStates[mStreamVolumeAlias[streamType]].getIndex(device, false);
-            setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, true, false);
+            int index = mStreamStates[mStreamVolumeAlias[streamType]].getIndex(device);
+            setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, true);
 
             updateStreamVolumeAlias(true /*updateVolumes*/);
         }
@@ -1639,6 +1623,12 @@
     private static final String ASSET_FILE_VERSION = "1.0";
     private static final String GROUP_TOUCH_SOUNDS = "touch_sounds";
 
+    private static final int SOUND_EFECTS_LOAD_TIMEOUT_MS = 5000;
+
+    class LoadSoundEffectReply {
+        public int mStatus = 1;
+    };
+
     private void loadTouchSoundAssetDefaults() {
         SOUND_EFFECT_FILES.add("Effect_Tick.ogg");
         for (int i = 0; i < AudioManager.NUM_SOUND_EFFECTS; i++) {
@@ -1650,6 +1640,11 @@
     private void loadTouchSoundAssets() {
         XmlResourceParser parser = null;
 
+        // only load assets once.
+        if (!SOUND_EFFECT_FILES.isEmpty()) {
+            return;
+        }
+
         loadTouchSoundAssetDefaults();
 
         try {
@@ -1719,14 +1714,12 @@
 
     /** @see AudioManager#playSoundEffect(int) */
     public void playSoundEffect(int effectType) {
-        sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SENDMSG_NOOP,
-                effectType, -1, null, 0);
+        playSoundEffectVolume(effectType, -1.0f);
     }
 
     /** @see AudioManager#playSoundEffect(int, float) */
     public void playSoundEffectVolume(int effectType, float volume) {
-        loadSoundEffects();
-        sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SENDMSG_NOOP,
+        sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SENDMSG_QUEUE,
                 effectType, (int) (volume * 1000), null, 0);
     }
 
@@ -1735,116 +1728,20 @@
      * This method must be called at first when sound effects are enabled
      */
     public boolean loadSoundEffects() {
-        int status;
+        int attempts = 3;
+        LoadSoundEffectReply reply = new LoadSoundEffectReply();
 
-        loadTouchSoundAssets();
-
-        synchronized (mSoundEffectsLock) {
-            if (!mBootCompleted) {
-                Log.w(TAG, "loadSoundEffects() called before boot complete");
-                return false;
-            }
-
-            if (mSoundPool != null) {
-                return true;
-            }
-            mSoundPool = new SoundPool(NUM_SOUNDPOOL_CHANNELS, AudioSystem.STREAM_SYSTEM, 0);
-
-            try {
-                mSoundPoolCallBack = null;
-                mSoundPoolListenerThread = new SoundPoolListenerThread();
-                mSoundPoolListenerThread.start();
-                // Wait for mSoundPoolCallBack to be set by the other thread
-                mSoundEffectsLock.wait();
-            } catch (InterruptedException e) {
-                Log.w(TAG, "Interrupted while waiting sound pool listener thread.");
-            }
-
-            if (mSoundPoolCallBack == null) {
-                Log.w(TAG, "loadSoundEffects() could not create SoundPool listener or thread");
-                if (mSoundPoolLooper != null) {
-                    mSoundPoolLooper.quit();
-                    mSoundPoolLooper = null;
-                }
-                mSoundPoolListenerThread = null;
-                mSoundPool.release();
-                mSoundPool = null;
-                return false;
-            }
-            /*
-             * poolId table: The value -1 in this table indicates that corresponding
-             * file (same index in SOUND_EFFECT_FILES[] has not been loaded.
-             * Once loaded, the value in poolId is the sample ID and the same
-             * sample can be reused for another effect using the same file.
-             */
-            int[] poolId = new int[SOUND_EFFECT_FILES.size()];
-            for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.size(); fileIdx++) {
-                poolId[fileIdx] = -1;
-            }
-            /*
-             * Effects whose value in SOUND_EFFECT_FILES_MAP[effect][1] is -1 must be loaded.
-             * If load succeeds, value in SOUND_EFFECT_FILES_MAP[effect][1] is > 0:
-             * this indicates we have a valid sample loaded for this effect.
-             */
-
-            int lastSample = 0;
-            for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
-                // Do not load sample if this effect uses the MediaPlayer
-                if (SOUND_EFFECT_FILES_MAP[effect][1] == 0) {
-                    continue;
-                }
-                if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == -1) {
-                    String filePath = Environment.getRootDirectory()
-                            + SOUND_EFFECTS_PATH
-                            + SOUND_EFFECT_FILES.get(SOUND_EFFECT_FILES_MAP[effect][0]);
-                    int sampleId = mSoundPool.load(filePath, 0);
-                    if (sampleId <= 0) {
-                        Log.w(TAG, "Soundpool could not load file: "+filePath);
-                    } else {
-                        SOUND_EFFECT_FILES_MAP[effect][1] = sampleId;
-                        poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = sampleId;
-                        lastSample = sampleId;
-                    }
-                } else {
-                    SOUND_EFFECT_FILES_MAP[effect][1] = poolId[SOUND_EFFECT_FILES_MAP[effect][0]];
-                }
-            }
-            // wait for all samples to be loaded
-            if (lastSample != 0) {
-                mSoundPoolCallBack.setLastSample(lastSample);
-
+        synchronized (reply) {
+            sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SENDMSG_QUEUE, 0, 0, reply, 0);
+            while ((reply.mStatus == 1) && (attempts-- > 0)) {
                 try {
-                    mSoundEffectsLock.wait();
-                    status = mSoundPoolCallBack.status();
-                } catch (java.lang.InterruptedException e) {
-                    Log.w(TAG, "Interrupted while waiting sound pool callback.");
-                    status = -1;
+                    reply.wait(SOUND_EFECTS_LOAD_TIMEOUT_MS);
+                } catch (InterruptedException e) {
+                    Log.w(TAG, "loadSoundEffects Interrupted while waiting sound pool loaded.");
                 }
-            } else {
-                status = -1;
-            }
-
-            if (mSoundPoolLooper != null) {
-                mSoundPoolLooper.quit();
-                mSoundPoolLooper = null;
-            }
-            mSoundPoolListenerThread = null;
-            if (status != 0) {
-                Log.w(TAG,
-                        "loadSoundEffects(), Error "
-                                + ((lastSample != 0) ? mSoundPoolCallBack.status() : -1)
-                                + " while loading samples");
-                for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
-                    if (SOUND_EFFECT_FILES_MAP[effect][1] > 0) {
-                        SOUND_EFFECT_FILES_MAP[effect][1] = -1;
-                    }
-                }
-
-                mSoundPool.release();
-                mSoundPool = null;
             }
         }
-        return (status == 0);
+        return (reply.mStatus == 0);
     }
 
     /**
@@ -1853,32 +1750,7 @@
      *  sound effects are disabled.
      */
     public void unloadSoundEffects() {
-        synchronized (mSoundEffectsLock) {
-            if (mSoundPool == null) {
-                return;
-            }
-
-            mAudioHandler.removeMessages(MSG_LOAD_SOUND_EFFECTS);
-            mAudioHandler.removeMessages(MSG_PLAY_SOUND_EFFECT);
-
-            int[] poolId = new int[SOUND_EFFECT_FILES.size()];
-            for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.size(); fileIdx++) {
-                poolId[fileIdx] = 0;
-            }
-
-            for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
-                if (SOUND_EFFECT_FILES_MAP[effect][1] <= 0) {
-                    continue;
-                }
-                if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == 0) {
-                    mSoundPool.unload(SOUND_EFFECT_FILES_MAP[effect][1]);
-                    SOUND_EFFECT_FILES_MAP[effect][1] = -1;
-                    poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = -1;
-                }
-            }
-            mSoundPool.release();
-            mSoundPool = null;
-        }
+        sendMsg(mAudioHandler, MSG_UNLOAD_SOUND_EFFECTS, SENDMSG_QUEUE, 0, 0, null, 0);
     }
 
     class SoundPoolListenerThread extends Thread {
@@ -1906,23 +1778,30 @@
     private final class SoundPoolCallback implements
             android.media.SoundPool.OnLoadCompleteListener {
 
-        int mStatus;
-        int mLastSample;
+        int mStatus = 1; // 1 means neither error nor last sample loaded yet
+        List<Integer> mSamples = new ArrayList<Integer>();
 
         public int status() {
             return mStatus;
         }
 
-        public void setLastSample(int sample) {
-            mLastSample = sample;
+        public void setSamples(int[] samples) {
+            for (int i = 0; i < samples.length; i++) {
+                // do not wait ack for samples rejected upfront by SoundPool
+                if (samples[i] > 0) {
+                    mSamples.add(samples[i]);
+                }
+            }
         }
 
         public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
             synchronized (mSoundEffectsLock) {
-                if (status != 0) {
-                    mStatus = status;
+                int i = mSamples.indexOf(sampleId);
+                if (i >= 0) {
+                    mSamples.remove(i);
                 }
-                if (sampleId == mLastSample) {
+                if ((status != 0) || mSamples. isEmpty()) {
+                    mStatus = status;
                     mSoundEffectsLock.notify();
                 }
             }
@@ -1951,8 +1830,8 @@
                 streamState.readSettings();
 
                 // unmute stream that was muted but is not affect by mute anymore
-                if (streamState.muteCount() != 0 && !isStreamAffectedByMute(streamType) &&
-                        !isStreamMutedByRingerMode(streamType)) {
+                if (streamState.isMuted() && ((!isStreamAffectedByMute(streamType) &&
+                        !isStreamMutedByRingerMode(streamType)) || mUseFixedVolume)) {
                     int size = streamState.mDeathHandlers.size();
                     for (int i = 0; i < size; i++) {
                         streamState.mDeathHandlers.get(i).mMuteCount = 1;
@@ -1975,7 +1854,7 @@
         }
     }
 
-    /** @see AudioManager#setSpeakerphoneOn() */
+    /** @see AudioManager#setSpeakerphoneOn(boolean) */
     public void setSpeakerphoneOn(boolean on){
         if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) {
             return;
@@ -1991,7 +1870,7 @@
         return (mForcedUseForComm == AudioSystem.FORCE_SPEAKER);
     }
 
-    /** @see AudioManager#setBluetoothScoOn() */
+    /** @see AudioManager#setBluetoothScoOn(boolean) */
     public void setBluetoothScoOn(boolean on){
         if (!checkAudioSettingsPermission("setBluetoothScoOn()")) {
             return;
@@ -2009,7 +1888,7 @@
         return (mForcedUseForComm == AudioSystem.FORCE_BT_SCO);
     }
 
-    /** @see AudioManager#setBluetoothA2dpOn() */
+    /** @see AudioManager#setBluetoothA2dpOn(boolean) */
     public void setBluetoothA2dpOn(boolean on) {
         synchronized (mBluetoothA2dpEnabledLock) {
             mBluetoothA2dpEnabled = on;
@@ -2451,8 +2330,7 @@
                             0,
                             null,
                             MUSIC_ACTIVE_POLL_PERIOD_MS);
-                    int index = mStreamStates[AudioSystem.STREAM_MUSIC].getIndex(device,
-                                                                            false /*lastAudible*/);
+                    int index = mStreamStates[AudioSystem.STREAM_MUSIC].getIndex(device);
                     if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0) &&
                             (index > mSafeMediaVolumeIndex)) {
                         // Approximate cumulative active music time
@@ -2797,18 +2675,14 @@
         private final int mStreamType;
 
         private String mVolumeIndexSettingName;
-        private String mLastAudibleVolumeIndexSettingName;
         private int mIndexMax;
         private final ConcurrentHashMap<Integer, Integer> mIndex =
                                             new ConcurrentHashMap<Integer, Integer>(8, 0.75f, 4);
-        private final ConcurrentHashMap<Integer, Integer> mLastAudibleIndex =
-                                            new ConcurrentHashMap<Integer, Integer>(8, 0.75f, 4);
         private ArrayList<VolumeDeathHandler> mDeathHandlers; //handles mute/solo clients death
 
         private VolumeStreamState(String settingName, int streamType) {
 
             mVolumeIndexSettingName = settingName;
-            mLastAudibleVolumeIndexSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE;
 
             mStreamType = streamType;
             mIndexMax = MAX_STREAM_VOLUME[streamType];
@@ -2821,10 +2695,8 @@
             readSettings();
         }
 
-        public String getSettingNameForDevice(boolean lastAudible, int device) {
-            String name = lastAudible ?
-                            mLastAudibleVolumeIndexSettingName :
-                            mVolumeIndexSettingName;
+        public String getSettingNameForDevice(int device) {
+            String name = mVolumeIndexSettingName;
             String suffix = AudioSystem.getDeviceName(device);
             if (suffix.isEmpty()) {
                 return name;
@@ -2833,13 +2705,14 @@
         }
 
         public synchronized void readSettings() {
-            int remainingDevices = AudioSystem.DEVICE_OUT_ALL;
-
+            // force maximum volume on all streams if fixed volume property is set
+            if (mUseFixedVolume) {
+                mIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, mIndexMax);
+                return;
+            }
             // do not read system stream volume from settings: this stream is always aliased
             // to another stream type and its volume is never persisted. Values in settings can
             // only be stale values
-            // on first call to readSettings() at init time, muteCount() is always 0 so we will
-            // always create entries for default device
             if ((mStreamType == AudioSystem.STREAM_SYSTEM) ||
                     (mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED)) {
                 int index = 10 * AudioManager.DEFAULT_STREAM_VOLUME[mStreamType];
@@ -2848,13 +2721,12 @@
                         index = mIndexMax;
                     }
                 }
-                if (muteCount() == 0) {
-                    mIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, index);
-                }
-                mLastAudibleIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, index);
+                mIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, index);
                 return;
             }
 
+            int remainingDevices = AudioSystem.DEVICE_OUT_ALL;
+
             for (int i = 0; remainingDevices != 0; i++) {
                 int device = (1 << i);
                 if ((device & remainingDevices) == 0) {
@@ -2863,7 +2735,7 @@
                 remainingDevices &= ~device;
 
                 // retrieve current volume for device
-                String name = getSettingNameForDevice(false /* lastAudible */, device);
+                String name = getSettingNameForDevice(device);
                 // if no volume stored for current stream and device, use default volume if default
                 // device, continue otherwise
                 int defaultIndex = (device == AudioSystem.DEVICE_OUT_DEFAULT) ?
@@ -2877,72 +2749,33 @@
                 // ignore settings for fixed volume devices: volume should always be at max or 0
                 if ((mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_MUSIC) &&
                         ((device & mFixedVolumeDevices) != 0)) {
-                    if ((muteCount()) == 0 && (index != 0)) {
-                        mIndex.put(device, mIndexMax);
-                    } else {
-                        mIndex.put(device, 0);
-                    }
-                    mLastAudibleIndex.put(device, mIndexMax);
-                    continue;
-                }
-
-                // retrieve last audible volume for device
-                name = getSettingNameForDevice(true  /* lastAudible */, device);
-                // use stored last audible index if present, otherwise use current index if not 0
-                // or default index
-                defaultIndex = (index > 0) ?
-                                    index : AudioManager.DEFAULT_STREAM_VOLUME[mStreamType];
-                int lastAudibleIndex = Settings.System.getIntForUser(
-                        mContentResolver, name, defaultIndex, UserHandle.USER_CURRENT);
-
-                // a last audible index of 0 should never be stored for ring and notification
-                // streams on phones (voice capable devices).
-                if ((lastAudibleIndex == 0) && mVoiceCapable &&
-                                (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_RING)) {
-                    lastAudibleIndex = AudioManager.DEFAULT_STREAM_VOLUME[mStreamType];
-                    // Correct the data base
-                    sendMsg(mAudioHandler,
-                            MSG_PERSIST_VOLUME,
-                            SENDMSG_QUEUE,
-                            PERSIST_LAST_AUDIBLE,
-                            device,
-                            this,
-                            PERSIST_DELAY);
-                }
-                mLastAudibleIndex.put(device, getValidIndex(10 * lastAudibleIndex));
-                // the initial index should never be 0 for ring and notification streams on phones
-                // (voice capable devices) if not in silent or vibrate mode.
-                if ((index == 0) && (mRingerMode == AudioManager.RINGER_MODE_NORMAL) &&
-                        mVoiceCapable &&
-                        (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_RING)) {
-                    index = lastAudibleIndex;
-                    // Correct the data base
-                    sendMsg(mAudioHandler,
-                            MSG_PERSIST_VOLUME,
-                            SENDMSG_QUEUE,
-                            PERSIST_CURRENT,
-                            device,
-                            this,
-                            PERSIST_DELAY);
-                }
-                if (muteCount() == 0) {
+                    mIndex.put(device, (index != 0) ? mIndexMax : 0);
+                } else {
                     mIndex.put(device, getValidIndex(10 * index));
                 }
             }
         }
 
         public void applyDeviceVolume(int device) {
-            AudioSystem.setStreamVolumeIndex(mStreamType,
-                                             (getIndex(device, false  /* lastAudible */) + 5)/10,
-                                             device);
+            int index;
+            if (isMuted()) {
+                index = 0;
+            } else {
+                index = (getIndex(device) + 5)/10;
+            }
+            AudioSystem.setStreamVolumeIndex(mStreamType, index, device);
         }
 
         public synchronized void applyAllVolumes() {
             // apply default volume first: by convention this will reset all
             // devices volumes in audio policy manager to the supplied value
-            AudioSystem.setStreamVolumeIndex(mStreamType,
-                    (getIndex(AudioSystem.DEVICE_OUT_DEFAULT, false /* lastAudible */) + 5)/10,
-                    AudioSystem.DEVICE_OUT_DEFAULT);
+            int index;
+            if (isMuted()) {
+                index = 0;
+            } else {
+                index = (getIndex(AudioSystem.DEVICE_OUT_DEFAULT) + 5)/10;
+            }
+            AudioSystem.setStreamVolumeIndex(mStreamType, index, AudioSystem.DEVICE_OUT_DEFAULT);
             // then apply device specific volumes
             Set set = mIndex.entrySet();
             Iterator i = set.iterator();
@@ -2950,34 +2783,32 @@
                 Map.Entry entry = (Map.Entry)i.next();
                 int device = ((Integer)entry.getKey()).intValue();
                 if (device != AudioSystem.DEVICE_OUT_DEFAULT) {
-                    AudioSystem.setStreamVolumeIndex(mStreamType,
-                                                     ((Integer)entry.getValue() + 5)/10,
-                                                     device);
+                    if (isMuted()) {
+                        index = 0;
+                    } else {
+                        index = ((Integer)entry.getValue() + 5)/10;
+                    }
+                    AudioSystem.setStreamVolumeIndex(mStreamType, index, device);
                 }
             }
         }
 
         public boolean adjustIndex(int deltaIndex, int device) {
-            return setIndex(getIndex(device,
-                                     false  /* lastAudible */) + deltaIndex,
-                            device,
-                            true  /* lastAudible */);
+            return setIndex(getIndex(device) + deltaIndex,
+                            device);
         }
 
-        public synchronized boolean setIndex(int index, int device, boolean lastAudible) {
-            int oldIndex = getIndex(device, false  /* lastAudible */);
+        public synchronized boolean setIndex(int index, int device) {
+            int oldIndex = getIndex(device);
             index = getValidIndex(index);
             synchronized (mCameraSoundForced) {
                 if ((mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED) && mCameraSoundForced) {
                     index = mIndexMax;
                 }
             }
-            mIndex.put(device, getValidIndex(index));
+            mIndex.put(device, index);
 
             if (oldIndex != index) {
-                if (lastAudible) {
-                    mLastAudibleIndex.put(device, index);
-                }
                 // Apply change to all streams using this one as alias
                 // if changing volume of current device, also change volume of current
                 // device on aliased stream
@@ -2988,12 +2819,10 @@
                             mStreamVolumeAlias[streamType] == mStreamType) {
                         int scaledIndex = rescaleIndex(index, mStreamType, streamType);
                         mStreamStates[streamType].setIndex(scaledIndex,
-                                                           device,
-                                                           lastAudible);
+                                                           device);
                         if (currentDevice) {
                             mStreamStates[streamType].setIndex(scaledIndex,
-                                                               getDeviceForStream(streamType),
-                                                               lastAudible);
+                                                               getDeviceForStream(streamType));
                         }
                     }
                 }
@@ -3003,63 +2832,21 @@
             }
         }
 
-        public synchronized int getIndex(int device, boolean lastAudible) {
-            ConcurrentHashMap <Integer, Integer> indexes;
-            if (lastAudible) {
-                indexes = mLastAudibleIndex;
-            } else {
-                indexes = mIndex;
-            }
-            Integer index = indexes.get(device);
+        public synchronized int getIndex(int device) {
+            Integer index = mIndex.get(device);
             if (index == null) {
                 // there is always an entry for AudioSystem.DEVICE_OUT_DEFAULT
-                index = indexes.get(AudioSystem.DEVICE_OUT_DEFAULT);
+                index = mIndex.get(AudioSystem.DEVICE_OUT_DEFAULT);
             }
             return index.intValue();
         }
 
-        public synchronized void setLastAudibleIndex(int index, int device) {
-            // Apply change to all streams using this one as alias
-            // if changing volume of current device, also change volume of current
-            // device on aliased stream
-            boolean currentDevice = (device == getDeviceForStream(mStreamType));
-            int numStreamTypes = AudioSystem.getNumStreamTypes();
-            for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
-                if (streamType != mStreamType &&
-                        mStreamVolumeAlias[streamType] == mStreamType) {
-                    int scaledIndex = rescaleIndex(index, mStreamType, streamType);
-                    mStreamStates[streamType].setLastAudibleIndex(scaledIndex, device);
-                    if (currentDevice) {
-                        mStreamStates[streamType].setLastAudibleIndex(scaledIndex,
-                                                                   getDeviceForStream(streamType));
-                    }
-                }
-            }
-            mLastAudibleIndex.put(device, getValidIndex(index));
-        }
-
-        public synchronized void adjustLastAudibleIndex(int deltaIndex, int device) {
-            setLastAudibleIndex(getIndex(device,
-                                         true  /* lastAudible */) + deltaIndex,
-                                device);
-        }
-
         public int getMaxIndex() {
             return mIndexMax;
         }
 
-        // only called by setAllIndexes() which is already synchronized
-        public ConcurrentHashMap <Integer, Integer> getAllIndexes(boolean lastAudible) {
-            if (lastAudible) {
-                return mLastAudibleIndex;
-            } else {
-                return mIndex;
-            }
-        }
-
-        public synchronized void setAllIndexes(VolumeStreamState srcStream, boolean lastAudible) {
-            ConcurrentHashMap <Integer, Integer> indexes = srcStream.getAllIndexes(lastAudible);
-            Set set = indexes.entrySet();
+        public synchronized void setAllIndexes(VolumeStreamState srcStream) {
+            Set set = srcStream.mIndex.entrySet();
             Iterator i = set.iterator();
             while (i.hasNext()) {
                 Map.Entry entry = (Map.Entry)i.next();
@@ -3067,11 +2854,7 @@
                 int index = ((Integer)entry.getValue()).intValue();
                 index = rescaleIndex(index, srcStream.getStreamType(), mStreamType);
 
-                if (lastAudible) {
-                    setLastAudibleIndex(index, device);
-                } else {
-                    setIndex(index, device, false /* lastAudible */);
-                }
+                setIndex(index, device);
             }
         }
 
@@ -3082,12 +2865,6 @@
                 Map.Entry entry = (Map.Entry)i.next();
                 entry.setValue(mIndexMax);
             }
-            set = mLastAudibleIndex.entrySet();
-            i = set.iterator();
-            while (i.hasNext()) {
-                Map.Entry entry = (Map.Entry)i.next();
-                entry.setValue(mIndexMax);
-            }
         }
 
         public synchronized void mute(IBinder cb, boolean state) {
@@ -3106,7 +2883,7 @@
         private int getValidIndex(int index) {
             if (index < 0) {
                 return 0;
-            } else if (index > mIndexMax) {
+            } else if (mUseFixedVolume || index > mIndexMax) {
                 return mIndexMax;
             }
 
@@ -3123,6 +2900,7 @@
 
             // must be called while synchronized on parent VolumeStreamState
             public void mute(boolean state) {
+                boolean updateVolume = false;
                 if (state) {
                     if (mMuteCount == 0) {
                         // Register for client death notification
@@ -3131,22 +2909,10 @@
                             if (mICallback != null) {
                                 mICallback.linkToDeath(this, 0);
                             }
-                            mDeathHandlers.add(this);
+                            VolumeStreamState.this.mDeathHandlers.add(this);
                             // If the stream is not yet muted by any client, set level to 0
-                            if (muteCount() == 0) {
-                                Set set = mIndex.entrySet();
-                                Iterator i = set.iterator();
-                                while (i.hasNext()) {
-                                    Map.Entry entry = (Map.Entry)i.next();
-                                    int device = ((Integer)entry.getKey()).intValue();
-                                    setIndex(0, device, false /* lastAudible */);
-                                }
-                                sendMsg(mAudioHandler,
-                                        MSG_SET_ALL_VOLUMES,
-                                        SENDMSG_QUEUE,
-                                        0,
-                                        0,
-                                        VolumeStreamState.this, 0);
+                            if (!VolumeStreamState.this.isMuted()) {
+                                updateVolume = true;
                             }
                         } catch (RemoteException e) {
                             // Client has died!
@@ -3164,37 +2930,25 @@
                         mMuteCount--;
                         if (mMuteCount == 0) {
                             // Unregister from client death notification
-                            mDeathHandlers.remove(this);
+                            VolumeStreamState.this.mDeathHandlers.remove(this);
                             // mICallback can be 0 if muted by AudioService
                             if (mICallback != null) {
                                 mICallback.unlinkToDeath(this, 0);
                             }
-                            if (muteCount() == 0) {
-                                // If the stream is not muted any more, restore its volume if
-                                // ringer mode allows it
-                                if (!isStreamAffectedByRingerMode(mStreamType) ||
-                                        mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
-                                    Set set = mIndex.entrySet();
-                                    Iterator i = set.iterator();
-                                    while (i.hasNext()) {
-                                        Map.Entry entry = (Map.Entry)i.next();
-                                        int device = ((Integer)entry.getKey()).intValue();
-                                        setIndex(getIndex(device,
-                                                          true  /* lastAudible */),
-                                                 device,
-                                                 false  /* lastAudible */);
-                                    }
-                                    sendMsg(mAudioHandler,
-                                            MSG_SET_ALL_VOLUMES,
-                                            SENDMSG_QUEUE,
-                                            0,
-                                            0,
-                                            VolumeStreamState.this, 0);
-                                }
+                            if (!VolumeStreamState.this.isMuted()) {
+                                updateVolume = true;
                             }
                         }
                     }
                 }
+                if (updateVolume) {
+                    sendMsg(mAudioHandler,
+                            MSG_SET_ALL_VOLUMES,
+                            SENDMSG_QUEUE,
+                            0,
+                            0,
+                            VolumeStreamState.this, 0);
+                }
             }
 
             public void binderDied() {
@@ -3216,6 +2970,10 @@
             return count;
         }
 
+        private synchronized boolean isMuted() {
+            return muteCount() != 0;
+        }
+
         // only called by mute() which is already synchronized
         private VolumeDeathHandler getDeathHandler(IBinder cb, boolean state) {
             VolumeDeathHandler handler;
@@ -3248,14 +3006,6 @@
                 pw.print(Integer.toHexString(((Integer)entry.getKey()).intValue())
                              + ": " + ((((Integer)entry.getValue()).intValue() + 5) / 10)+", ");
             }
-            pw.print("\n   Last audible: ");
-            set = mLastAudibleIndex.entrySet();
-            i = set.iterator();
-            while (i.hasNext()) {
-                Map.Entry entry = (Map.Entry)i.next();
-                pw.print(Integer.toHexString(((Integer)entry.getKey()).intValue())
-                             + ": " + ((((Integer)entry.getValue()).intValue() + 5) / 10)+", ");
-            }
         }
     }
 
@@ -3303,8 +3053,8 @@
             sendMsg(mAudioHandler,
                     MSG_PERSIST_VOLUME,
                     SENDMSG_QUEUE,
-                    PERSIST_CURRENT|PERSIST_LAST_AUDIBLE,
                     device,
+                    0,
                     streamState,
                     PERSIST_DELAY);
 
@@ -3325,29 +3075,177 @@
             }
         }
 
-        private void persistVolume(VolumeStreamState streamState,
-                                   int persistType,
-                                   int device) {
-            if ((persistType & PERSIST_CURRENT) != 0) {
-                System.putIntForUser(mContentResolver,
-                          streamState.getSettingNameForDevice(false /* lastAudible */, device),
-                          (streamState.getIndex(device, false /* lastAudible */) + 5)/ 10,
-                          UserHandle.USER_CURRENT);
+        private void persistVolume(VolumeStreamState streamState, int device) {
+            if (mUseFixedVolume) {
+                return;
             }
-            if ((persistType & PERSIST_LAST_AUDIBLE) != 0) {
-                System.putIntForUser(mContentResolver,
-                        streamState.getSettingNameForDevice(true /* lastAudible */, device),
-                        (streamState.getIndex(device, true  /* lastAudible */) + 5) / 10,
-                        UserHandle.USER_CURRENT);
-            }
+            System.putIntForUser(mContentResolver,
+                      streamState.getSettingNameForDevice(device),
+                      (streamState.getIndex(device) + 5)/ 10,
+                      UserHandle.USER_CURRENT);
         }
 
         private void persistRingerMode(int ringerMode) {
+            if (mUseFixedVolume) {
+                return;
+            }
             Settings.Global.putInt(mContentResolver, Settings.Global.MODE_RINGER, ringerMode);
         }
 
-        private void playSoundEffect(int effectType, int volume) {
+        private boolean onLoadSoundEffects() {
+            int status;
+
             synchronized (mSoundEffectsLock) {
+                if (!mBootCompleted) {
+                    Log.w(TAG, "onLoadSoundEffects() called before boot complete");
+                    return false;
+                }
+
+                if (mSoundPool != null) {
+                    return true;
+                }
+
+                loadTouchSoundAssets();
+
+                mSoundPool = new SoundPool(NUM_SOUNDPOOL_CHANNELS, AudioSystem.STREAM_SYSTEM, 0);
+                mSoundPoolCallBack = null;
+                mSoundPoolListenerThread = new SoundPoolListenerThread();
+                mSoundPoolListenerThread.start();
+                int attempts = 3;
+                while ((mSoundPoolCallBack == null) && (attempts-- > 0)) {
+                    try {
+                        // Wait for mSoundPoolCallBack to be set by the other thread
+                        mSoundEffectsLock.wait(SOUND_EFECTS_LOAD_TIMEOUT_MS);
+                    } catch (InterruptedException e) {
+                        Log.w(TAG, "Interrupted while waiting sound pool listener thread.");
+                    }
+                }
+
+                if (mSoundPoolCallBack == null) {
+                    Log.w(TAG, "onLoadSoundEffects() SoundPool listener or thread creation error");
+                    if (mSoundPoolLooper != null) {
+                        mSoundPoolLooper.quit();
+                        mSoundPoolLooper = null;
+                    }
+                    mSoundPoolListenerThread = null;
+                    mSoundPool.release();
+                    mSoundPool = null;
+                    return false;
+                }
+                /*
+                 * poolId table: The value -1 in this table indicates that corresponding
+                 * file (same index in SOUND_EFFECT_FILES[] has not been loaded.
+                 * Once loaded, the value in poolId is the sample ID and the same
+                 * sample can be reused for another effect using the same file.
+                 */
+                int[] poolId = new int[SOUND_EFFECT_FILES.size()];
+                for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.size(); fileIdx++) {
+                    poolId[fileIdx] = -1;
+                }
+                /*
+                 * Effects whose value in SOUND_EFFECT_FILES_MAP[effect][1] is -1 must be loaded.
+                 * If load succeeds, value in SOUND_EFFECT_FILES_MAP[effect][1] is > 0:
+                 * this indicates we have a valid sample loaded for this effect.
+                 */
+
+                int numSamples = 0;
+                for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
+                    // Do not load sample if this effect uses the MediaPlayer
+                    if (SOUND_EFFECT_FILES_MAP[effect][1] == 0) {
+                        continue;
+                    }
+                    if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == -1) {
+                        String filePath = Environment.getRootDirectory()
+                                + SOUND_EFFECTS_PATH
+                                + SOUND_EFFECT_FILES.get(SOUND_EFFECT_FILES_MAP[effect][0]);
+                        int sampleId = mSoundPool.load(filePath, 0);
+                        if (sampleId <= 0) {
+                            Log.w(TAG, "Soundpool could not load file: "+filePath);
+                        } else {
+                            SOUND_EFFECT_FILES_MAP[effect][1] = sampleId;
+                            poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = sampleId;
+                            numSamples++;
+                        }
+                    } else {
+                        SOUND_EFFECT_FILES_MAP[effect][1] =
+                                poolId[SOUND_EFFECT_FILES_MAP[effect][0]];
+                    }
+                }
+                // wait for all samples to be loaded
+                if (numSamples > 0) {
+                    mSoundPoolCallBack.setSamples(poolId);
+
+                    attempts = 3;
+                    status = 1;
+                    while ((status == 1) && (attempts-- > 0)) {
+                        try {
+                            mSoundEffectsLock.wait(SOUND_EFECTS_LOAD_TIMEOUT_MS);
+                            status = mSoundPoolCallBack.status();
+                        } catch (InterruptedException e) {
+                            Log.w(TAG, "Interrupted while waiting sound pool callback.");
+                        }
+                    }
+                } else {
+                    status = -1;
+                }
+
+                if (mSoundPoolLooper != null) {
+                    mSoundPoolLooper.quit();
+                    mSoundPoolLooper = null;
+                }
+                mSoundPoolListenerThread = null;
+                if (status != 0) {
+                    Log.w(TAG,
+                            "onLoadSoundEffects(), Error "+status+ " while loading samples");
+                    for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
+                        if (SOUND_EFFECT_FILES_MAP[effect][1] > 0) {
+                            SOUND_EFFECT_FILES_MAP[effect][1] = -1;
+                        }
+                    }
+
+                    mSoundPool.release();
+                    mSoundPool = null;
+                }
+            }
+            return (status == 0);
+        }
+
+        /**
+         *  Unloads samples from the sound pool.
+         *  This method can be called to free some memory when
+         *  sound effects are disabled.
+         */
+        private void onUnloadSoundEffects() {
+            synchronized (mSoundEffectsLock) {
+                if (mSoundPool == null) {
+                    return;
+                }
+
+                int[] poolId = new int[SOUND_EFFECT_FILES.size()];
+                for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.size(); fileIdx++) {
+                    poolId[fileIdx] = 0;
+                }
+
+                for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
+                    if (SOUND_EFFECT_FILES_MAP[effect][1] <= 0) {
+                        continue;
+                    }
+                    if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == 0) {
+                        mSoundPool.unload(SOUND_EFFECT_FILES_MAP[effect][1]);
+                        SOUND_EFFECT_FILES_MAP[effect][1] = -1;
+                        poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = -1;
+                    }
+                }
+                mSoundPool.release();
+                mSoundPool = null;
+            }
+        }
+
+        private void onPlaySoundEffect(int effectType, int volume) {
+            synchronized (mSoundEffectsLock) {
+
+                onLoadSoundEffects();
+
                 if (mSoundPool == null) {
                     return;
                 }
@@ -3360,7 +3258,8 @@
                 }
 
                 if (SOUND_EFFECT_FILES_MAP[effectType][1] > 0) {
-                    mSoundPool.play(SOUND_EFFECT_FILES_MAP[effectType][1], volFloat, volFloat, 0, 0, 1.0f);
+                    mSoundPool.play(SOUND_EFFECT_FILES_MAP[effectType][1],
+                                        volFloat, volFloat, 0, 0, 1.0f);
                 } else {
                     MediaPlayer mediaPlayer = new MediaPlayer();
                     try {
@@ -3435,10 +3334,13 @@
                     break;
 
                 case MSG_PERSIST_VOLUME:
-                    persistVolume((VolumeStreamState) msg.obj, msg.arg1, msg.arg2);
+                    persistVolume((VolumeStreamState) msg.obj, msg.arg1);
                     break;
 
                 case MSG_PERSIST_MASTER_VOLUME:
+                    if (mUseFixedVolume) {
+                        return;
+                    }
                     Settings.System.putFloatForUser(mContentResolver,
                                                     Settings.System.VOLUME_MASTER,
                                                     (float)msg.arg1 / (float)1000.0,
@@ -3446,6 +3348,9 @@
                     break;
 
                 case MSG_PERSIST_MASTER_VOLUME_MUTE:
+                    if (mUseFixedVolume) {
+                        return;
+                    }
                     Settings.System.putIntForUser(mContentResolver,
                                                  Settings.System.VOLUME_MASTER_MUTE,
                                                  msg.arg1,
@@ -3534,12 +3439,25 @@
                     AudioSystem.setParameters("restarting=false");
                     break;
 
+                case MSG_UNLOAD_SOUND_EFFECTS:
+                    onUnloadSoundEffects();
+                    break;
+
                 case MSG_LOAD_SOUND_EFFECTS:
-                    loadSoundEffects();
+                    //FIXME: onLoadSoundEffects() should be executed in a separate thread as it
+                    // can take several dozens of milliseconds to complete
+                    boolean loaded = onLoadSoundEffects();
+                    if (msg.obj != null) {
+                        LoadSoundEffectReply reply = (LoadSoundEffectReply)msg.obj;
+                        synchronized (reply) {
+                            reply.mStatus = loaded ? 0 : -1;
+                            reply.notify();
+                        }
+                    }
                     break;
 
                 case MSG_PLAY_SOUND_EFFECT:
-                    playSoundEffect(msg.arg1, msg.arg2);
+                    onPlaySoundEffect(msg.arg1, msg.arg2);
                     break;
 
                 case MSG_BTA2DP_DOCK_TIMEOUT:
@@ -3613,6 +3531,10 @@
                     onRegisterVolumeObserverForRcc(msg.arg1 /* rccId */,
                             (IRemoteVolumeObserver)msg.obj /* rvo */);
                     break;
+                case MSG_RCC_NEW_PLAYBACK_STATE:
+                    onNewPlaybackStateForRcc(msg.arg1 /* rccId */, msg.arg2 /* state */,
+                            (RccPlaybackState)msg.obj /* newState */);
+                    break;
 
                 case MSG_SET_RSX_CONNECTION_STATE:
                     onSetRsxConnectionState(msg.arg1/*available*/, msg.arg2/*address*/);
@@ -4087,7 +4009,7 @@
                 }
             } else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
                 mBootCompleted = true;
-                sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SENDMSG_NOOP,
+                sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SENDMSG_QUEUE,
                         0, 0, null, 0);
 
                 mKeyguardManager =
@@ -4127,7 +4049,7 @@
                 AudioSystem.setParameters("screen_state=on");
             } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
                 AudioSystem.setParameters("screen_state=off");
-            } else if (action.equalsIgnoreCase(Intent.ACTION_CONFIGURATION_CHANGED)) {
+            } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
                 handleConfigurationChanged(context);
             } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
                 // attempt to stop music playback for background user
@@ -4296,7 +4218,7 @@
      * Helper function:
      * Called synchronized on mAudioFocusLock
      * Remove a focus listener from the focus stack.
-     * @param focusListenerToRemove the focus listener
+     * @param clientToRemove the focus listener
      * @param signal if true and the listener was at the top of the focus stack, i.e. it was holding
      *   focus, notify the next item in the stack it gained focus.
      */
@@ -4402,7 +4324,7 @@
     }
 
 
-    /** @see AudioManager#requestAudioFocus(IAudioFocusDispatcher, int, int) */
+    /** @see AudioManager#requestAudioFocus(AudioManager.OnAudioFocusChangeListener, int, int)  */
     public int requestAudioFocus(int mainStreamType, int focusChangeHint, IBinder cb,
             IAudioFocusDispatcher fd, String clientId, String callingPackageName) {
         Log.i(TAG, " AudioFocus  requestAudioFocus() from " + clientId);
@@ -4475,7 +4397,7 @@
         return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
     }
 
-    /** @see AudioManager#abandonAudioFocus(IAudioFocusDispatcher) */
+    /** @see AudioManager#abandonAudioFocus(AudioManager.OnAudioFocusChangeListener)  */
     public int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId) {
         Log.i(TAG, " AudioFocus  abandonAudioFocus() from " + clientId);
         try {
@@ -4813,8 +4735,8 @@
      * remote control stack if necessary.
      */
     private class RcClientDeathHandler implements IBinder.DeathRecipient {
-        private IBinder mCb; // To be notified of client's death
-        private PendingIntent mMediaIntent;
+        final private IBinder mCb; // To be notified of client's death
+        final private PendingIntent mMediaIntent;
 
         RcClientDeathHandler(IBinder cb, PendingIntent pi) {
             mCb = cb;
@@ -4873,18 +4795,71 @@
      */
     private boolean mHasRemotePlayback;
 
+    private static class RccPlaybackState {
+        public int mState;
+        public long mPositionMs;
+        public float mSpeed;
+
+        public RccPlaybackState(int state, long positionMs, float speed) {
+            mState = state;
+            mPositionMs = positionMs;
+            mSpeed = speed;
+        }
+
+        public void reset() {
+            mState = RemoteControlClient.PLAYSTATE_STOPPED;
+            mPositionMs = RemoteControlClient.PLAYBACK_POSITION_INVALID;
+            mSpeed = RemoteControlClient.PLAYBACK_SPEED_1X;
+        }
+
+        @Override
+        public String toString() {
+            return stateToString() + ", "
+                    + ((mPositionMs == RemoteControlClient.PLAYBACK_POSITION_INVALID) ?
+                            "PLAYBACK_POSITION_INVALID ," : String.valueOf(mPositionMs)) + "ms ,"
+                    + mSpeed + "X";
+        }
+
+        private String stateToString() {
+            switch (mState) {
+                case RemoteControlClient.PLAYSTATE_NONE:
+                    return "PLAYSTATE_NONE";
+                case RemoteControlClient.PLAYSTATE_STOPPED:
+                    return "PLAYSTATE_STOPPED";
+                case RemoteControlClient.PLAYSTATE_PAUSED:
+                    return "PLAYSTATE_PAUSED";
+                case RemoteControlClient.PLAYSTATE_PLAYING:
+                    return "PLAYSTATE_PLAYING";
+                case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
+                    return "PLAYSTATE_FAST_FORWARDING";
+                case RemoteControlClient.PLAYSTATE_REWINDING:
+                    return "PLAYSTATE_REWINDING";
+                case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
+                    return "PLAYSTATE_SKIPPING_FORWARDS";
+                case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
+                    return "PLAYSTATE_SKIPPING_BACKWARDS";
+                case RemoteControlClient.PLAYSTATE_BUFFERING:
+                    return "PLAYSTATE_BUFFERING";
+                case RemoteControlClient.PLAYSTATE_ERROR:
+                    return "PLAYSTATE_ERROR";
+                default:
+                    return "[invalid playstate]";
+            }
+        }
+    }
+
     private static class RemoteControlStackEntry {
         public int mRccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
         /**
          * The target for the ACTION_MEDIA_BUTTON events.
          * Always non null.
          */
-        public PendingIntent mMediaIntent;
+        final public PendingIntent mMediaIntent;
         /**
          * The registered media button event receiver.
          * Always non null.
          */
-        public ComponentName mReceiverComponent;
+        final public ComponentName mReceiverComponent;
         public String mCallingPackageName;
         public int mCallingUid;
         /**
@@ -4901,7 +4876,7 @@
         public int mPlaybackVolumeMax;
         public int mPlaybackVolumeHandling;
         public int mPlaybackStream;
-        public int mPlaybackState;
+        public RccPlaybackState mPlaybackState;
         public IRemoteVolumeObserver mRemoteVolumeObs;
 
         public void resetPlaybackInfo() {
@@ -4910,17 +4885,21 @@
             mPlaybackVolumeMax = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
             mPlaybackVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
             mPlaybackStream = AudioManager.STREAM_MUSIC;
-            mPlaybackState = RemoteControlClient.PLAYSTATE_STOPPED;
+            mPlaybackState.reset();
             mRemoteVolumeObs = null;
         }
 
-        /** precondition: mediaIntent != null, eventReceiver != null */
+        /** precondition: mediaIntent != null */
         public RemoteControlStackEntry(PendingIntent mediaIntent, ComponentName eventReceiver) {
             mMediaIntent = mediaIntent;
             mReceiverComponent = eventReceiver;
             mCallingUid = -1;
             mRcClient = null;
             mRccId = ++sLastRccId;
+            mPlaybackState = new RccPlaybackState(
+                    RemoteControlClient.PLAYSTATE_STOPPED,
+                    RemoteControlClient.PLAYBACK_POSITION_INVALID,
+                    RemoteControlClient.PLAYBACK_SPEED_1X);
 
             resetPlaybackInfo();
         }
@@ -5048,7 +5027,7 @@
                 //  evaluated it, traversal order doesn't matter here)
                 while(stackIterator.hasNext()) {
                     RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
-                    if (packageName.equalsIgnoreCase(rcse.mReceiverComponent.getPackageName())) {
+                    if (packageName.equals(rcse.mMediaIntent.getCreatorPackage())) {
                         // a stack entry is from the package being removed, remove it from the stack
                         stackIterator.remove();
                         rcse.unlinkToRcClientDeath();
@@ -5061,10 +5040,14 @@
                                     null));
                 } else if (oldTop != mRCStack.peek()) {
                     // the top of the stack has changed, save it in the system settings
-                    // by posting a message to persist it
-                    mAudioHandler.sendMessage(
-                            mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
-                                    mRCStack.peek().mReceiverComponent));
+                    // by posting a message to persist it; only do this however if it has
+                    // a concrete component name (is not a transient registration)
+                    RemoteControlStackEntry rcse = mRCStack.peek();
+                    if (rcse.mReceiverComponent != null) {
+                        mAudioHandler.sendMessage(
+                                mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
+                                        rcse.mReceiverComponent));
+                    }
                 }
             }
         }
@@ -5079,6 +5062,10 @@
                 Settings.System.MEDIA_BUTTON_RECEIVER, UserHandle.USER_CURRENT);
         if ((null != receiverName) && !receiverName.isEmpty()) {
             ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName);
+            if (eventReceiver == null) {
+                // an invalid name was persisted
+                return;
+            }
             // construct a PendingIntent targeted to the restored component name
             // for the media button and register it
             Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
@@ -5094,7 +5081,7 @@
      * Helper function:
      * Set the new remote control receiver at the top of the RC focus stack.
      * Called synchronized on mAudioFocusLock, then mRCStack
-     * precondition: mediaIntent != null, target != null
+     * precondition: mediaIntent != null
      */
     private void pushMediaButtonReceiver_syncAfRcs(PendingIntent mediaIntent, ComponentName target) {
         // already at top of stack?
@@ -5123,8 +5110,10 @@
         mRCStack.push(rcse); // rcse is never null
 
         // post message to persist the default media button receiver
-        mAudioHandler.sendMessage( mAudioHandler.obtainMessage(
-                MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, target/*obj*/) );
+        if (target != null) {
+            mAudioHandler.sendMessage( mAudioHandler.obtainMessage(
+                    MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, target/*obj*/) );
+        }
     }
 
     /**
@@ -5211,7 +5200,7 @@
     /**
      * Update the displays and clients with the new "focused" client generation and name
      * @param newClientGeneration the new generation value matching a client update
-     * @param newClientEventReceiver the media button event receiver associated with the client.
+     * @param newMediaIntent the media button event receiver associated with the client.
      *    May be null, which implies there is no registered media button event receiver.
      * @param clearing true if the new client generation value maps to a remote control update
      *    where the display should be cleared.
@@ -5424,7 +5413,7 @@
 
     /**
      * see AudioManager.registerMediaButtonIntent(PendingIntent pi, ComponentName c)
-     * precondition: mediaIntent != null, target != null
+     * precondition: mediaIntent != null
      */
     public void registerMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver) {
         Log.i(TAG, "  Remote Control   registerMediaButtonIntent() for " + mediaIntent);
@@ -5442,7 +5431,7 @@
      * see AudioManager.unregisterMediaButtonIntent(PendingIntent mediaIntent)
      * precondition: mediaIntent != null, eventReceiver != null
      */
-    public void unregisterMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver)
+    public void unregisterMediaButtonIntent(PendingIntent mediaIntent)
     {
         Log.i(TAG, "  Remote Control   unregisterMediaButtonIntent() for " + mediaIntent);
 
@@ -5875,21 +5864,6 @@
                             case RemoteControlClient.PLAYBACKINFO_USES_STREAM:
                                 rcse.mPlaybackStream = value;
                                 break;
-                            case RemoteControlClient.PLAYBACKINFO_PLAYSTATE:
-                                rcse.mPlaybackState = value;
-                                synchronized (mMainRemote) {
-                                    if (rccId == mMainRemote.mRccId) {
-                                        mMainRemoteIsActive = isPlaystateActive(value);
-                                        postReevaluateRemote();
-                                    }
-                                }
-                                // an RCC moving to a "playing" state should become the media button
-                                //   event receiver so it can be controlled, without requiring the
-                                //   app to re-register its receiver
-                                if (isPlaystateActive(value)) {
-                                    postPromoteRcc(rccId);
-                                }
-                                break;
                             default:
                                 Log.e(TAG, "unhandled key " + key + " for RCC " + rccId);
                                 break;
@@ -5899,7 +5873,45 @@
                 }//for
             } catch (ArrayIndexOutOfBoundsException e) {
                 // not expected to happen, indicates improper concurrent modification
-                Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e);
+                Log.e(TAG, "Wrong index mRCStack on onNewPlaybackInfoForRcc, lock error? ", e);
+            }
+        }
+    }
+
+    public void setPlaybackStateForRcc(int rccId, int state, long timeMs, float speed) {
+        sendMsg(mAudioHandler, MSG_RCC_NEW_PLAYBACK_STATE, SENDMSG_QUEUE,
+                rccId /* arg1 */, state /* arg2 */,
+                new RccPlaybackState(state, timeMs, speed) /* obj */, 0 /* delay */);
+    }
+
+    public void onNewPlaybackStateForRcc(int rccId, int state, RccPlaybackState newState) {
+        if(DEBUG_RC) Log.d(TAG, "onNewPlaybackStateForRcc(id=" + rccId + ", state=" + state
+                + ", time=" + newState.mPositionMs + ", speed=" + newState.mSpeed + ")");
+        synchronized(mRCStack) {
+            // iterating from top of stack as playback information changes are more likely
+            //   on entries at the top of the remote control stack
+            try {
+                for (int index = mRCStack.size()-1; index >= 0; index--) {
+                    final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
+                    if (rcse.mRccId == rccId) {
+                        rcse.mPlaybackState = newState;
+                        synchronized (mMainRemote) {
+                            if (rccId == mMainRemote.mRccId) {
+                                mMainRemoteIsActive = isPlaystateActive(state);
+                                postReevaluateRemote();
+                            }
+                        }
+                        // an RCC moving to a "playing" state should become the media button
+                        //   event receiver so it can be controlled, without requiring the
+                        //   app to re-register its receiver
+                        if (isPlaystateActive(state)) {
+                            postPromoteRcc(rccId);
+                        }
+                    }
+                }//for
+            } catch (ArrayIndexOutOfBoundsException e) {
+                // not expected to happen, indicates improper concurrent modification
+                Log.e(TAG, "Wrong index on mRCStack in onNewPlaybackStateForRcc, lock error? ", e);
             }
         }
     }
@@ -5943,7 +5955,7 @@
                 for (int index = mRCStack.size()-1; index >= 0; index--) {
                     final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
                     if ((rcse.mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE)
-                            && isPlaystateActive(rcse.mPlaybackState)
+                            && isPlaystateActive(rcse.mPlaybackState.mState)
                             && (rcse.mPlaybackStream == streamType)) {
                         if (DEBUG_RC) Log.d(TAG, "remote playback active on stream " + streamType
                                 + ", vol =" + rcse.mPlaybackVolume);
@@ -6174,10 +6186,7 @@
                             mRingerModeAffectedStreams &=
                                     ~(1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
                         } else {
-                            s.setAllIndexes(mStreamStates[AudioSystem.STREAM_SYSTEM],
-                                            false /*lastAudible*/);
-                            s.setAllIndexes(mStreamStates[AudioSystem.STREAM_SYSTEM],
-                                            true /*lastAudible*/);
+                            s.setAllIndexes(mStreamStates[AudioSystem.STREAM_SYSTEM]);
                             mRingerModeAffectedStreams |=
                                     (1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
                         }
@@ -6323,7 +6332,6 @@
 
     private void enforceSafeMediaVolume() {
         VolumeStreamState streamState = mStreamStates[AudioSystem.STREAM_MUSIC];
-        boolean lastAudible = (streamState.muteCount() != 0);
         int devices = mSafeMediaVolumeDevices;
         int i = 0;
 
@@ -6332,27 +6340,16 @@
             if ((device & devices) == 0) {
                 continue;
             }
-            int index = streamState.getIndex(device, lastAudible);
+            int index = streamState.getIndex(device);
             if (index > mSafeMediaVolumeIndex) {
-                if (lastAudible) {
-                    streamState.setLastAudibleIndex(mSafeMediaVolumeIndex, device);
-                    sendMsg(mAudioHandler,
-                            MSG_PERSIST_VOLUME,
-                            SENDMSG_QUEUE,
-                            PERSIST_LAST_AUDIBLE,
-                            device,
-                            streamState,
-                            PERSIST_DELAY);
-                } else {
-                    streamState.setIndex(mSafeMediaVolumeIndex, device, true);
-                    sendMsg(mAudioHandler,
-                            MSG_SET_DEVICE_VOLUME,
-                            SENDMSG_QUEUE,
-                            device,
-                            0,
-                            streamState,
-                            0);
-                }
+                streamState.setIndex(mSafeMediaVolumeIndex, device);
+                sendMsg(mAudioHandler,
+                        MSG_SET_DEVICE_VOLUME,
+                        SENDMSG_QUEUE,
+                        device,
+                        0,
+                        streamState,
+                        0);
             }
             devices &= ~device;
         }
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index efa8089..270c26d 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -121,7 +121,7 @@
     void dispatchMediaKeyEventUnderWakelock(in KeyEvent keyEvent);
 
            void registerMediaButtonIntent(in PendingIntent pi, in ComponentName c);
-    oneway void unregisterMediaButtonIntent(in PendingIntent pi,  in ComponentName c);
+    oneway void unregisterMediaButtonIntent(in PendingIntent pi);
 
     oneway void registerMediaButtonEventReceiverForCalls(in ComponentName c);
     oneway void unregisterMediaButtonEventReceiverForCalls();
@@ -159,6 +159,7 @@
     oneway void remoteControlDisplayUsesBitmapSize(in IRemoteControlDisplay rcd, int w, int h);
 
     oneway void setPlaybackInfoForRcc(int rccId, int what, int value);
+           void setPlaybackStateForRcc(int rccId, int state, long timeMs, float speed);
            int  getRemoteStreamMaxVolume();
            int  getRemoteStreamVolume();
     oneway void registerRemoteVolumeObserverForRcc(int rccId, in IRemoteVolumeObserver rvo);
diff --git a/media/java/android/media/IRemoteControlDisplay.aidl b/media/java/android/media/IRemoteControlDisplay.aidl
index 204de3c..095cf80 100644
--- a/media/java/android/media/IRemoteControlDisplay.aidl
+++ b/media/java/android/media/IRemoteControlDisplay.aidl
@@ -40,7 +40,8 @@
     void setCurrentClientId(int clientGeneration, in PendingIntent clientMediaIntent,
             boolean clearing);
 
-    void setPlaybackState(int generationId, int state, long stateChangeTimeMs);
+    void setPlaybackState(int generationId, int state, long stateChangeTimeMs, long currentPosMs,
+            float speed);
 
     void setTransportControlFlags(int generationId, int transportControlFlags);
 
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index cc59d02..376bb2d 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -367,7 +367,7 @@
      * counterparts in include/media/mediametadataretriever.h!
      */
     /**
-     * The metadata key to retrieve the numberic string describing the
+     * The metadata key to retrieve the numeric string describing the
      * order of the audio data source on its original recording.
      */
     public static final int METADATA_KEY_CD_TRACK_NUMBER = 0;
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 11f4180..85a32ca 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -328,8 +328,8 @@
  *         the state. Calling this method in an invalid state transfers the
  *         object to the <em>Error</em> state. </p></td></tr>
  * <tr><td>pause </p></td>
- *     <td>{Started, Paused}</p></td>
- *     <td>{Idle, Initialized, Prepared, Stopped, PlaybackCompleted, Error}</p></td>
+ *     <td>{Started, Paused, PlaybackCompleted}</p></td>
+ *     <td>{Idle, Initialized, Prepared, Stopped, Error}</p></td>
  *     <td>Successful invoke of this method in a valid state transfers the
  *         object to the <em>Paused</em> state. Calling this method in an
  *         invalid state transfers the object to the <em>Error</em> state.</p></td></tr>
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
index 9a0ecdf..f186000 100644
--- a/media/java/android/media/RemoteControlClient.java
+++ b/media/java/android/media/RemoteControlClient.java
@@ -172,6 +172,17 @@
      */
     public final static int PLAYBACKINFO_INVALID_VALUE = Integer.MIN_VALUE;
 
+    /**
+     * @hide
+     * An unknown or invalid playback position value.
+     */
+    public final static long PLAYBACK_POSITION_INVALID = -1;
+    /**
+     * @hide
+     * The default playback speed, 1x.
+     */
+    public final static float PLAYBACK_SPEED_1X = 1.0f;
+
     //==========================================
     // Public keys for playback information
     /**
@@ -208,15 +219,7 @@
     public final static int PLAYBACKINFO_USES_STREAM = 5;
 
     //==========================================
-    // Private keys for playback information
-    /**
-     * @hide
-     * Used internally to relay playback state (set by the application with
-     * {@link #setPlaybackState(int)}) to AudioService
-     */
-    public final static int PLAYBACKINFO_PLAYSTATE = 255;
-
-
+    // Public flags for the supported transport control capabililities
     /**
      * Flag indicating a RemoteControlClient makes use of the "previous" media key.
      *
@@ -273,6 +276,15 @@
      * @see android.view.KeyEvent#KEYCODE_MEDIA_NEXT
      */
     public final static int FLAG_KEY_MEDIA_NEXT = 1 << 7;
+    /**
+     * @hide
+     * (to be un-hidden and added in javadoc of setTransportControlFlags(int))
+     * Flag indicating a RemoteControlClient can receive changes in the media playback position
+     * through the {@link #OnPlaybackPositionUpdateListener} interface.
+     *
+     * @see #setTransportControlFlags(int)
+     */
+    public final static int FLAG_KEY_MEDIA_POSITION_UPDATE = 1 << 8;
 
     /**
      * @hide
@@ -588,17 +600,54 @@
      *       {@link #PLAYSTATE_ERROR}.
      */
     public void setPlaybackState(int state) {
+        setPlaybackState(state, PLAYBACK_POSITION_INVALID, PLAYBACK_SPEED_1X);
+    }
+
+    /**
+     * @hide
+     * (to be un-hidden)
+     * Sets the current playback state and the matching media position for the current playback
+     *   speed.
+     * @param state The current playback state, one of the following values:
+     *       {@link #PLAYSTATE_STOPPED},
+     *       {@link #PLAYSTATE_PAUSED},
+     *       {@link #PLAYSTATE_PLAYING},
+     *       {@link #PLAYSTATE_FAST_FORWARDING},
+     *       {@link #PLAYSTATE_REWINDING},
+     *       {@link #PLAYSTATE_SKIPPING_FORWARDS},
+     *       {@link #PLAYSTATE_SKIPPING_BACKWARDS},
+     *       {@link #PLAYSTATE_BUFFERING},
+     *       {@link #PLAYSTATE_ERROR}.
+     * @param timeInMs a 0 or positive value for the current media position expressed in ms
+     *    (same unit as for when sending the media duration, if applicable, with
+     *    {@link android.media.MediaMetadataRetriever#METADATA_KEY_DURATION} in the
+     *    {@link RemoteControlClient.MetadataEditor}). Negative values imply that position is not
+     *    known (e.g. listening to a live stream of a radio) or not applicable (e.g. when state
+     *    is {@link #PLAYSTATE_BUFFERING} and nothing had played yet).
+     * @param playbackSpeed a value expressed as a ratio of 1x playback: 1.0f is normal playback,
+     *    2.0f is 2x, 0.5f is half-speed, -2.0f is rewind at 2x speed. 0.0f means nothing is
+     *    playing (e.g. when state is {@link #PLAYSTATE_ERROR}).
+     */
+    public void setPlaybackState(int state, long timeInMs, float playbackSpeed) {
         synchronized(mCacheLock) {
-            if (mPlaybackState != state) {
+            if (timeInMs != PLAYBACK_POSITION_INVALID) {
+                mPlaybackPositionCapabilities |= MEDIA_POSITION_READABLE;
+            } else {
+                mPlaybackPositionCapabilities &= ~MEDIA_POSITION_READABLE;
+            }
+            if ((mPlaybackState != state) || (mPlaybackPositionMs != timeInMs)
+                    || (mPlaybackSpeed != playbackSpeed)) {
                 // store locally
                 mPlaybackState = state;
+                mPlaybackPositionMs = timeInMs;
+                mPlaybackSpeed = playbackSpeed;
                 // keep track of when the state change occurred
                 mPlaybackStateChangeTimeMs = SystemClock.elapsedRealtime();
 
                 // send to remote control display if conditions are met
                 sendPlaybackState_syncCacheLock();
                 // update AudioService
-                sendAudioServiceNewPlaybackInfo_syncCacheLock(PLAYBACKINFO_PLAYSTATE, state);
+                sendAudioServiceNewPlaybackState_syncCacheLock();
             }
         }
     }
@@ -625,6 +674,65 @@
         }
     }
 
+    /**
+     * @hide
+     * (to be un-hidden)
+     * Interface definition for a callback to be invoked when the media playback position is
+     * requested to be updated.
+     */
+    public interface OnPlaybackPositionUpdateListener {
+        /**
+         * Called on the listener to notify it that the playback head should be set at the given
+         * position. If the position can be changed from its current value, the implementor of
+         * the interface should also update the playback position using
+         * {@link RemoteControlClient#setPlaybackState(int, long, int)} to reflect the actual new
+         * position being used, regardless of whether it differs from the requested position.
+         * @param newPositionMs the new requested position in the current media, expressed in ms.
+         */
+        void onPlaybackPositionUpdate(long newPositionMs);
+    }
+
+    /**
+     * @hide
+     * (to be un-hidden)
+     * Sets the listener RemoteControlClient calls whenever the media playback position is requested
+     * to be updated.
+     * Notifications will be received in the same thread as the one in which RemoteControlClient
+     * was created.
+     * @param l
+     */
+    public void setPlaybackPositionUpdateListener(OnPlaybackPositionUpdateListener l) {
+        synchronized(mCacheLock) {
+            if ((mPositionUpdateListener == null) && (l != null)) {
+                mPlaybackPositionCapabilities |= MEDIA_POSITION_WRITABLE;
+                // tell RCDs and AudioService this RCC accepts position updates
+                // TODO implement
+            } else if ((mPositionUpdateListener != null) && (l == null)) {
+                mPlaybackPositionCapabilities &= ~MEDIA_POSITION_WRITABLE;
+                // tell RCDs and AudioService this RCC doesn't handle position updates
+                // TODO implement
+            }
+            mPositionUpdateListener = l;
+        }
+    }
+
+    /**
+     * @hide
+     * Flag to reflect that the application controlling this RemoteControlClient sends playback
+     * position updates. The playback position being "readable" is considered from the application's
+     * point of view.
+     */
+    public static int MEDIA_POSITION_READABLE = 1 << 0;
+    /**
+     * @hide
+     * Flag to reflect that the application controlling this RemoteControlClient can receive
+     * playback position updates. The playback position being "writable"
+     * is considered from the application's point of view.
+     */
+    public static int MEDIA_POSITION_WRITABLE = 1 << 1;
+
+    private int mPlaybackPositionCapabilities = 0;
+
     /** @hide */
     public final static int DEFAULT_PLAYBACK_VOLUME_HANDLING = PLAYBACK_VOLUME_VARIABLE;
     /** @hide */
@@ -756,6 +864,14 @@
      */
     private long mPlaybackStateChangeTimeMs = 0;
     /**
+     * Last playback position in ms reported by the user
+     */
+    private long mPlaybackPositionMs = PLAYBACK_POSITION_INVALID;
+    /**
+     * Last playback speed reported by the user
+     */
+    private float mPlaybackSpeed = PLAYBACK_SPEED_1X;
+    /**
      * Cache for the artwork bitmap.
      * Access synchronized on mCacheLock
      * Artwork and metadata are not kept in one Bundle because the bitmap sometimes needs to be
@@ -774,9 +890,13 @@
      * This is re-initialized in apply() and so cannot be final.
      */
     private Bundle mMetadata = new Bundle();
-
     /**
-     * The current remote control client generation ID across the system
+     * Listener registered by user of RemoteControlClient to receive requests for playback position
+     * update requests.
+     */
+    private OnPlaybackPositionUpdateListener mPositionUpdateListener;
+    /**
+     * The current remote control client generation ID across the system, as known by this object
      */
     private int mCurrentClientGenId = -1;
     /**
@@ -789,7 +909,8 @@
 
     /**
      * The media button intent description associated with this remote control client
-     * (can / should include target component for intent handling)
+     * (can / should include target component for intent handling, used when persisting media
+     *    button event receiver across reboots).
      */
     private final PendingIntent mRcMediaIntent;
 
@@ -990,7 +1111,8 @@
                 final DisplayInfoForClient di = (DisplayInfoForClient) displayIterator.next();
                 try {
                     di.mRcDisplay.setPlaybackState(mInternalClientGenId,
-                            mPlaybackState, mPlaybackStateChangeTimeMs);
+                            mPlaybackState, mPlaybackStateChangeTimeMs, mPlaybackPositionMs,
+                            mPlaybackSpeed);
                 } catch (RemoteException e) {
                     Log.e(TAG, "Error in setPlaybackState(), dead display " + di.mRcDisplay, e);
                     displayIterator.remove();
@@ -1109,7 +1231,20 @@
         try {
             service.setPlaybackInfoForRcc(mRcseId, what, value);
         } catch (RemoteException e) {
-            Log.e(TAG, "Dead object in sendAudioServiceNewPlaybackInfo_syncCacheLock", e);
+            Log.e(TAG, "Dead object in setPlaybackInfoForRcc", e);
+        }
+    }
+
+    private void sendAudioServiceNewPlaybackState_syncCacheLock() {
+        if (mRcseId == RCSE_ID_UNREGISTERED) {
+            return;
+        }
+        IAudioService service = getService();
+        try {
+            service.setPlaybackStateForRcc(mRcseId,
+                    mPlaybackState, mPlaybackPositionMs, mPlaybackSpeed);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Dead object in setPlaybackStateForRcc", e);
         }
     }
 
diff --git a/media/java/android/media/audiofx/AudioEffect.java b/media/java/android/media/audiofx/AudioEffect.java
index 68a09de..031326e 100644
--- a/media/java/android/media/audiofx/AudioEffect.java
+++ b/media/java/android/media/audiofx/AudioEffect.java
@@ -72,55 +72,49 @@
      * specification. The definitions match the corresponding interface IDs in
      * OpenSLES_IID.h
      */
-
     /**
-     * UUID for environmental reverb effect
-     * @hide
+     * UUID for environmental reverberation effect
      */
     public static final UUID EFFECT_TYPE_ENV_REVERB = UUID
             .fromString("c2e5d5f0-94bd-4763-9cac-4e234d06839e");
     /**
-     * UUID for preset reverb effect
-     * @hide
+     * UUID for preset reverberation effect
      */
     public static final UUID EFFECT_TYPE_PRESET_REVERB = UUID
             .fromString("47382d60-ddd8-11db-bf3a-0002a5d5c51b");
     /**
      * UUID for equalizer effect
-     * @hide
      */
     public static final UUID EFFECT_TYPE_EQUALIZER = UUID
             .fromString("0bed4300-ddd6-11db-8f34-0002a5d5c51b");
     /**
      * UUID for bass boost effect
-     * @hide
      */
     public static final UUID EFFECT_TYPE_BASS_BOOST = UUID
             .fromString("0634f220-ddd4-11db-a0fc-0002a5d5c51b");
     /**
      * UUID for virtualizer effect
-     * @hide
      */
     public static final UUID EFFECT_TYPE_VIRTUALIZER = UUID
             .fromString("37cc2c00-dddd-11db-8577-0002a5d5c51b");
 
     /**
-     * UUID for Automatic Gain Control (AGC) audio pre-processing
-     * @hide
+     * UUIDs for effect types not covered by OpenSL ES.
+     */
+    /**
+     * UUID for Automatic Gain Control (AGC)
      */
     public static final UUID EFFECT_TYPE_AGC = UUID
             .fromString("0a8abfe0-654c-11e0-ba26-0002a5d5c51b");
 
     /**
-     * UUID for Acoustic Echo Canceler (AEC) audio pre-processing
-     * @hide
+     * UUID for Acoustic Echo Canceler (AEC)
      */
     public static final UUID EFFECT_TYPE_AEC = UUID
             .fromString("7b491460-8d4d-11e0-bd61-0002a5d5c51b");
 
     /**
-     * UUID for Noise Suppressor (NS) audio pre-processing
-     * @hide
+     * UUID for Noise Suppressor (NS)
      */
     public static final UUID EFFECT_TYPE_NS = UUID
             .fromString("58b4b260-8e06-11e0-aa8e-0002a5d5c51b");
@@ -199,7 +193,7 @@
      * The effect descriptor contains information on a particular effect implemented in the
      * audio framework:<br>
      * <ul>
-     *  <li>type: UUID corresponding to the OpenSL ES interface implemented by this effect</li>
+     *  <li>type: UUID identifying the effect type</li>
      *  <li>uuid: UUID for this particular implementation</li>
      *  <li>connectMode: {@link #EFFECT_INSERT}, {@link #EFFECT_AUXILIARY} or
      *  {at_link #EFFECT_PRE_PROCESSING}</li>
@@ -224,8 +218,14 @@
         }
 
         /**
-         *  Indicates the generic type of the effect (Equalizer, Bass boost ...). The UUID
-         *  corresponds to the OpenSL ES Interface ID for this type of effect.
+         *  Indicates the generic type of the effect (Equalizer, Bass boost ...).
+         *  One of {@link AudioEffect#EFFECT_TYPE_AEC},
+         *  {@link AudioEffect#EFFECT_TYPE_AGC}, {@link AudioEffect#EFFECT_TYPE_BASS_BOOST},
+         *  {@link AudioEffect#EFFECT_TYPE_ENV_REVERB}, {@link AudioEffect#EFFECT_TYPE_EQUALIZER},
+         *  {@link AudioEffect#EFFECT_TYPE_NS}, {@link AudioEffect#EFFECT_TYPE_PRESET_REVERB}
+         *   or {@link AudioEffect#EFFECT_TYPE_VIRTUALIZER}.<br>
+         *  For reverberation, bass boost, EQ and virtualizer, the UUID
+         *  corresponds to the OpenSL ES Interface ID.
          */
         public UUID type;
         /**
@@ -440,7 +440,7 @@
     }
 
     /**
-     * Query all audio pre processing effects applied to the AudioRecord with the supplied
+     * Query all audio pre-processing effects applied to the AudioRecord with the supplied
      * audio session ID. Returns an array of {@link android.media.audiofx.AudioEffect.Descriptor}
      * objects.
      * @param audioSession system wide unique audio session identifier.
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index ea12803..632334b 100644
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -96,7 +96,8 @@
             Files.FileColumns.FORMAT, // 2
             Files.FileColumns.PARENT, // 3
             Files.FileColumns.DATA, // 4
-            Files.FileColumns.DATE_MODIFIED, // 5
+            Files.FileColumns.DATE_ADDED, // 5
+            Files.FileColumns.DATE_MODIFIED, // 6
     };
     private static final String ID_WHERE = Files.FileColumns._ID + "=?";
     private static final String PATH_WHERE = Files.FileColumns.DATA + "=?";
@@ -840,7 +841,7 @@
     }
 
     private boolean getObjectInfo(int handle, int[] outStorageFormatParent,
-                        char[] outName, long[] outModified) {
+                        char[] outName, long[] outCreatedModified) {
         Cursor c = null;
         try {
             c = mMediaProvider.query(mPackageName, mObjectsUri, OBJECT_INFO_PROJECTION,
@@ -861,7 +862,12 @@
                 path.getChars(start, end, outName, 0);
                 outName[end - start] = 0;
 
-                outModified[0] = c.getLong(5);
+                outCreatedModified[0] = c.getLong(5);
+                outCreatedModified[1] = c.getLong(6);
+                // use modification date as creation date if date added is not set
+                if (outCreatedModified[0] == 0) {
+                    outCreatedModified[0] = outCreatedModified[1];
+                }
                 return true;
             }
         } catch (RemoteException e) {
diff --git a/media/jni/android_media_MediaCrypto.cpp b/media/jni/android_media_MediaCrypto.cpp
index 517a293..d0f56ea 100644
--- a/media/jni/android_media_MediaCrypto.cpp
+++ b/media/jni/android_media_MediaCrypto.cpp
@@ -74,7 +74,7 @@
 
     sp<ICrypto> crypto = service->makeCrypto();
 
-    if (crypto == NULL || crypto->initCheck() != OK) {
+    if (crypto == NULL || (crypto->initCheck() != OK && crypto->initCheck() != NO_INIT)) {
         return NULL;
     }
 
diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp
index bc65de5..fbd5d21 100644
--- a/media/jni/android_mtp_MtpDatabase.cpp
+++ b/media/jni/android_mtp_MtpDatabase.cpp
@@ -775,7 +775,8 @@
     env->ReleaseIntArrayElements(mIntBuffer, intValues, 0);
 
     jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
-    info.mDateModified = longValues[0];
+    info.mDateCreated = longValues[0];
+    info.mDateModified = longValues[1];
     env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
 
 //    info.mAssociationType = (format == MTP_FORMAT_ASSOCIATION ?
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaAudioManagerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaAudioManagerTest.java
index 7967ce7..e232338 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaAudioManagerTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaAudioManagerTest.java
@@ -42,6 +42,7 @@
     private final static int WAIT_FOR_LOOPER_TO_INITIALIZE_MS = 60000;  // 60s
     private int[] ringtoneMode = {AudioManager.RINGER_MODE_NORMAL,
              AudioManager.RINGER_MODE_SILENT, AudioManager.RINGER_MODE_VIBRATE};
+    private boolean mUseFixedVolume;
 
     public MediaAudioManagerTest() {
         super("com.android.mediaframeworktest", MediaFrameworkTest.class);
@@ -65,6 +66,10 @@
     @Override
     protected void setUp() throws Exception {
         super.setUp();
+
+        mUseFixedVolume = getActivity().getResources().getBoolean(
+                com.android.internal.R.bool.config_useFixedVolume);
+
         synchronized(mLooperLock) {
             initializeAudioManagerWithLooper();
             try {
@@ -91,10 +96,12 @@
 
      public boolean validateSetRingTone(int i) {
          int getRingtone = mAudioManager.getRingerMode();
-         if (i != getRingtone)
-             return false;
-         else
-             return true;
+
+         if (mUseFixedVolume) {
+             return (getRingtone == AudioManager.RINGER_MODE_NORMAL);
+         } else {
+             return (getRingtone == i);
+         }
      }
 
      // Test case 1: Simple test case to validate the set ringtone mode
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
index 4a67997..a446e40 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java
@@ -98,11 +98,11 @@
 
     private void setGpsLocation(String value) {
         UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
-        if (! um.isLocationSharingToggleAllowed()) {
+        if (um.hasUserRestriction(UserManager.DISALLOW_SHARE_LOCATION)) {
             return;
         }
         final String GPS = LocationManager.GPS_PROVIDER;
-        boolean enabled = 
+        boolean enabled =
                 GPS.equals(value) ||
                 value.startsWith(GPS + ",") ||
                 value.endsWith("," + GPS) ||
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 45319a8..659651b 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -322,7 +322,7 @@
     @Override
     public boolean onCreate() {
         mBackupManager = new BackupManager(getContext());
-        mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE);
+        mUserManager = UserManager.get(getContext());
 
         setAppOps(AppOpsManager.OP_NONE, AppOpsManager.OP_WRITE_SETTINGS);
         establishDbTracking(UserHandle.USER_OWNER);
diff --git a/packages/SystemUI/Android.mk b/packages/SystemUI/Android.mk
index 262000e..015c0cc 100644
--- a/packages/SystemUI/Android.mk
+++ b/packages/SystemUI/Android.mk
@@ -4,13 +4,10 @@
 LOCAL_MODULE_TAGS := optional
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src) \
-    ../../../ex/carousel/java/com/android/ex/carousel/carousel.rs \
     src/com/android/systemui/EventLogTags.logtags
 
 LOCAL_JAVA_LIBRARIES := services telephony-common
 
-LOCAL_STATIC_JAVA_LIBRARIES := android-common-carousel
-
 LOCAL_PACKAGE_NAME := SystemUI
 LOCAL_CERTIFICATE := platform
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index b498368..5041617 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -76,11 +76,13 @@
     int result;
 
     void clearImage() {
-        context = null;
         image = null;
         imageUri = null;
         iconSize = 0;
     }
+    void clearContext() {
+        context = null;
+    }
 }
 
 /**
@@ -172,8 +174,6 @@
         mNotificationBuilder.setLargeIcon(croppedIcon);
         // But we still don't set it for the expanded view, allowing the smallIcon to show here.
         mNotificationStyle.bigLargeIcon(null);
-
-        Log.d(TAG, "SaveImageInBackgroundTask constructor");
     }
 
     @Override
@@ -181,7 +181,7 @@
         if (params.length != 1) return null;
         if (isCancelled()) {
             params[0].clearImage();
-            Log.d(TAG, "doInBackground cancelled");
+            params[0].clearContext();
             return null;
         }
 
@@ -246,7 +246,6 @@
             // mounted
             params[0].clearImage();
             params[0].result = 1;
-            Log.d(TAG, "doInBackground failed");
         }
 
         // Recycle the bitmap data
@@ -254,7 +253,6 @@
             image.recycle();
         }
 
-        Log.d(TAG, "doInBackground complete");
         return params[0];
     }
 
@@ -263,7 +261,7 @@
         if (isCancelled()) {
             params.finisher.run();
             params.clearImage();
-            Log.d(TAG, "onPostExecute cancelled");
+            params.clearContext();
             return;
         }
 
@@ -291,7 +289,7 @@
             mNotificationManager.notify(mNotificationId, n);
         }
         params.finisher.run();
-        Log.d(TAG, "onPostExecute complete");
+        params.clearContext();
     }
 }
 
@@ -395,15 +393,12 @@
         // Setup the Camera shutter sound
         mCameraSound = new MediaActionSound();
         mCameraSound.load(MediaActionSound.SHUTTER_CLICK);
-
-        Log.d(TAG, "GlobalScreenshot constructor");
     }
 
     /**
      * Creates a new worker thread and saves the screenshot to the media store.
      */
     private void saveScreenshotInWorkerThread(Runnable finisher) {
-        Log.d(TAG, "saveScreenshotInWorkerThread");
         SaveImageInBackgroundData data = new SaveImageInBackgroundData();
         data.context = mContext;
         data.image = mScreenBitmap;
@@ -411,7 +406,6 @@
         data.finisher = finisher;
         if (mSaveInBgTask != null) {
             mSaveInBgTask.cancel(false);
-            Log.d(TAG, "saveScreenshotInWorkerThread cancel");
         }
         mSaveInBgTask = new SaveImageInBackgroundTask(mContext, data, mNotificationManager,
                 SCREENSHOT_NOTIFICATION_ID).execute(data);
@@ -436,8 +430,6 @@
      * Takes a screenshot of the current display and shows an animation.
      */
     void takeScreenshot(Runnable finisher, boolean statusBarVisible, boolean navBarVisible) {
-        Log.d(TAG, "takeScreenshot");
-
         // We need to orient the screenshot correctly (and the Surface api seems to take screenshots
         // only in the natural orientation of the device :!)
         mDisplay.getRealMetrics(mDisplayMetrics);
@@ -451,8 +443,6 @@
             mDisplayMatrix.mapPoints(dims);
             dims[0] = Math.abs(dims[0]);
             dims[1] = Math.abs(dims[1]);
-
-            Log.d(TAG, "takeScreenshot requiresRotation");
         }
 
         // Take the screenshot
@@ -460,7 +450,6 @@
         if (mScreenBitmap == null) {
             notifyScreenshotError(mContext, mNotificationManager);
             finisher.run();
-            Log.d(TAG, "takeScreenshot null bitmap");
             return;
         }
 
@@ -477,7 +466,6 @@
             // Recycle the previous bitmap
             mScreenBitmap.recycle();
             mScreenBitmap = ss;
-            Log.d(TAG, "takeScreenshot rotation bitmap created");
         }
 
         // Optimizations
@@ -487,7 +475,6 @@
         // Start the post-screenshot animation
         startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,
                 statusBarVisible, navBarVisible);
-        Log.d(TAG, "takeScreenshot startedAnimation");
     }
 
 
@@ -496,7 +483,6 @@
      */
     private void startAnimation(final Runnable finisher, int w, int h, boolean statusBarVisible,
             boolean navBarVisible) {
-        Log.d(TAG, "startAnimation");
         // Add the view for the animation
         mScreenshotView.setImageBitmap(mScreenBitmap);
         mScreenshotLayout.requestFocus();
@@ -505,11 +491,9 @@
         if (mScreenshotAnimation != null) {
             mScreenshotAnimation.end();
             mScreenshotAnimation.removeAllListeners();
-            Log.d(TAG, "startAnimation reset previous animations");
         }
 
         mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
-        Log.d(TAG, "startAnimation layout added to WM");
         ValueAnimator screenshotDropInAnim = createScreenshotDropInAnimation();
         ValueAnimator screenshotFadeOutAnim = createScreenshotDropOutAnimation(w, h,
                 statusBarVisible, navBarVisible);
@@ -525,7 +509,6 @@
                 // Clear any references to the bitmap
                 mScreenBitmap = null;
                 mScreenshotView.setImageBitmap(null);
-                Log.d(TAG, "startAnimation onAnimationEnd");
             }
         });
         mScreenshotLayout.post(new Runnable() {
@@ -537,7 +520,6 @@
                 mScreenshotView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
                 mScreenshotView.buildLayer();
                 mScreenshotAnimation.start();
-                Log.d(TAG, "startAnimation post runnable");
             }
         });
     }
@@ -675,7 +657,6 @@
     }
 
     static void notifyScreenshotError(Context context, NotificationManager nManager) {
-        Log.d(TAG, "notifyScreenshotError");
         Resources r = context.getResources();
 
         // Clear all existing notification, compose the new notification and show it
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 1954af8..6a0fe47 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -38,15 +38,12 @@
                     final Messenger callback = msg.replyTo;
                     if (mScreenshot == null) {
                         mScreenshot = new GlobalScreenshot(TakeScreenshotService.this);
-                        Log.d(TAG, "Global screenshot initialized");
                     }
-                    Log.d(TAG, "Global screenshot captured");
                     mScreenshot.takeScreenshot(new Runnable() {
                         @Override public void run() {
                             Message reply = Message.obtain(null, 1);
                             try {
                                 callback.send(reply);
-                                Log.d(TAG, "Global screenshot completed");
                             } catch (RemoteException e) {
                             }
                         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java b/packages/SystemUI/src/com/android/systemui/statusbar/GestureRecorder.java
old mode 100755
new mode 100644
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
index 60e22c5..a7c7fba 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
@@ -206,8 +206,7 @@
         mUserInfoTask = new AsyncTask<Void, Void, Pair<String, Drawable>>() {
             @Override
             protected Pair<String, Drawable> doInBackground(Void... params) {
-                final UserManager um =
-                        (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+                final UserManager um = UserManager.get(mContext);
 
                 // Fall back to the UserManager nickname if we can't read the name from the local
                 // profile below.
@@ -292,8 +291,7 @@
             @Override
             public void onClick(View v) {
                 mBar.collapseAllPanels(true);
-                final UserManager um =
-                        (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+                final UserManager um = UserManager.get(mContext);
                 if (um.getUsers(true).size() > 1) {
                     try {
                         WindowManagerGlobal.getWindowManagerService().lockNow(null);
diff --git a/policy/src/com/android/internal/policy/impl/GlobalActions.java b/policy/src/com/android/internal/policy/impl/GlobalActions.java
old mode 100755
new mode 100644
index 761eb2d..a2ac8fe
--- a/policy/src/com/android/internal/policy/impl/GlobalActions.java
+++ b/policy/src/com/android/internal/policy/impl/GlobalActions.java
@@ -102,6 +102,7 @@
     private boolean mIsWaitingForEcmExit = false;
     private boolean mHasTelephony;
     private boolean mHasVibrator;
+    private final boolean mShowSilentToggle;
 
     /**
      * @param context everything needs a context :(
@@ -132,6 +133,9 @@
                 mAirplaneModeObserver);
         Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
         mHasVibrator = vibrator != null && vibrator.hasVibrator();
+
+        mShowSilentToggle = SHOW_SILENT_TOGGLE && !mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_useFixedVolume);
     }
 
     /**
@@ -309,7 +313,7 @@
         }
 
         // last: silent mode
-        if (SHOW_SILENT_TOGGLE) {
+        if (mShowSilentToggle) {
             mItems.add(mSilentModeAction);
         }
 
@@ -390,7 +394,7 @@
         mAirplaneModeOn.updateState(mAirplaneState);
         mAdapter.notifyDataSetChanged();
         mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
-        if (SHOW_SILENT_TOGGLE) {
+        if (mShowSilentToggle) {
             IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
             mContext.registerReceiver(mRingerModeReceiver, filter);
         }
@@ -407,7 +411,7 @@
 
     /** {@inheritDoc} */
     public void onDismiss(DialogInterface dialog) {
-        if (SHOW_SILENT_TOGGLE) {
+        if (mShowSilentToggle) {
             try {
                 mContext.unregisterReceiver(mRingerModeReceiver);
             } catch (IllegalArgumentException ie) {
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 5ad305c..ad5e20b 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -989,6 +989,13 @@
             final Callback cb = getCallback();
             if (!mActionBar.isOverflowMenuShowing() || !toggleMenuMode) {
                 if (cb != null && !isDestroyed() && mActionBar.getVisibility() == View.VISIBLE) {
+                    // If we have a menu invalidation pending, do it now.
+                    if (mInvalidatePanelMenuPosted &&
+                            (mInvalidatePanelMenuFeatures & (1 << FEATURE_OPTIONS_PANEL)) != 0) {
+                        mDecor.removeCallbacks(mInvalidatePanelMenuRunnable);
+                        mInvalidatePanelMenuRunnable.run();
+                    }
+
                     final PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, true);
 
                     // If we don't have a menu or we're waiting for a full content refresh,
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java
index 9712ea8..d5798d7 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardTransportControlView.java
@@ -132,7 +132,8 @@
             mLocalHandler = new WeakReference<Handler>(handler);
         }
 
-        public void setPlaybackState(int generationId, int state, long stateChangeTimeMs) {
+        public void setPlaybackState(int generationId, int state, long stateChangeTimeMs,
+                long currentPosMs, float speed) {
             Handler handler = mLocalHandler.get();
             if (handler != null) {
                 handler.obtainMessage(MSG_UPDATE_STATE, generationId, state).sendToTarget();
diff --git a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
index 27d816e..e958e9a 100644
--- a/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
+++ b/policy/src/com/android/internal/policy/impl/keyguard/KeyguardUpdateMonitor.java
@@ -200,7 +200,8 @@
     private final IRemoteControlDisplay.Stub mRemoteControlDisplay =
                 new IRemoteControlDisplay.Stub() {
 
-        public void setPlaybackState(int generationId, int state, long stateChangeTimeMs) {
+        public void setPlaybackState(int generationId, int state, long stateChangeTimeMs,
+                long currentPosMs, float speed) {
             Message msg = mHandler.obtainMessage(MSG_SET_PLAYBACK_STATE,
                     generationId, state, stateChangeTimeMs);
             mHandler.sendMessage(msg);
diff --git a/services/input/InputDispatcher.h b/services/input/InputDispatcher.h
index d4f932e..430721e 100644
--- a/services/input/InputDispatcher.h
+++ b/services/input/InputDispatcher.h
@@ -165,6 +165,8 @@
  * Input dispatcher configuration.
  *
  * Specifies various options that modify the behavior of the input dispatcher.
+ * The values provided here are merely defaults. The actual values will come from ViewConfiguration
+ * and are passed into the dispatcher during initialization.
  */
 struct InputDispatcherConfiguration {
     // The key repeat initial timeout.
diff --git a/services/java/com/android/server/AppWidgetServiceImpl.java b/services/java/com/android/server/AppWidgetServiceImpl.java
index 6eea928..fb2828b 100644
--- a/services/java/com/android/server/AppWidgetServiceImpl.java
+++ b/services/java/com/android/server/AppWidgetServiceImpl.java
@@ -177,18 +177,20 @@
     // Manages persistent references to RemoteViewsServices from different App Widgets
     private final HashMap<FilterComparison, HashSet<Integer>> mRemoteViewsServicesAppWidgets = new HashMap<FilterComparison, HashSet<Integer>>();
 
-    Context mContext;
+    final Context mContext;
+    final IPackageManager mPm;
+    final AlarmManager mAlarmManager;
+    final ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
+    final int mUserId;
+    final boolean mHasFeature;
+
     Locale mLocale;
-    IPackageManager mPm;
-    AlarmManager mAlarmManager;
-    ArrayList<Provider> mInstalledProviders = new ArrayList<Provider>();
     int mNextAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID + 1;
     final ArrayList<AppWidgetId> mAppWidgetIds = new ArrayList<AppWidgetId>();
-    ArrayList<Host> mHosts = new ArrayList<Host>();
+    final ArrayList<Host> mHosts = new ArrayList<Host>();
     // set of package names
-    HashSet<String> mPackagesWithBindWidgetPermission = new HashSet<String>();
+    final HashSet<String> mPackagesWithBindWidgetPermission = new HashSet<String>();
     boolean mSafeMode;
-    int mUserId;
     boolean mStateLoaded;
     int mMaxWidgetBitmapMemory;
 
@@ -204,6 +206,8 @@
         mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
         mUserId = userId;
         mSaveStateHandler = saveStateHandler;
+        mHasFeature = context.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_APP_WIDGETS);
         computeMaximumWidgetBitmapMemory();
     }
 
@@ -426,6 +430,9 @@
 
     private void ensureStateLoadedLocked() {
         if (!mStateLoaded) {
+            if (!mHasFeature) {
+                return;
+            }
             loadAppWidgetListLocked();
             loadStateLocked();
             mStateLoaded = true;
@@ -435,6 +442,9 @@
     public int allocateAppWidgetId(String packageName, int hostId) {
         int callingUid = enforceSystemOrCallingUid(packageName);
         synchronized (mAppWidgetIds) {
+            if (!mHasFeature) {
+                return -1;
+            }
             ensureStateLoadedLocked();
             int appWidgetId = mNextAppWidgetId++;
 
@@ -456,6 +466,9 @@
 
     public void deleteAppWidgetId(int appWidgetId) {
         synchronized (mAppWidgetIds) {
+            if (!mHasFeature) {
+                return;
+            }
             ensureStateLoadedLocked();
             AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
             if (id != null) {
@@ -467,6 +480,9 @@
 
     public void deleteHost(int hostId) {
         synchronized (mAppWidgetIds) {
+            if (!mHasFeature) {
+                return;
+            }
             ensureStateLoadedLocked();
             int callingUid = Binder.getCallingUid();
             Host host = lookupHostLocked(callingUid, hostId);
@@ -479,6 +495,9 @@
 
     public void deleteAllHosts() {
         synchronized (mAppWidgetIds) {
+            if (!mHasFeature) {
+                return;
+            }
             ensureStateLoadedLocked();
             int callingUid = Binder.getCallingUid();
             final int N = mHosts.size();
@@ -561,6 +580,9 @@
         final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mAppWidgetIds) {
+                if (!mHasFeature) {
+                    return;
+                }
                 options = cloneIfLocalBinder(options);
                 ensureStateLoadedLocked();
                 AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
@@ -622,6 +644,9 @@
 
     public boolean bindAppWidgetIdIfAllowed(
             String packageName, int appWidgetId, ComponentName provider, Bundle options) {
+        if (!mHasFeature) {
+            return false;
+        }
         try {
             mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BIND_APPWIDGET, null);
         } catch (SecurityException se) {
@@ -649,6 +674,9 @@
     }
 
     public boolean hasBindAppWidgetPermission(String packageName) {
+        if (!mHasFeature) {
+            return false;
+        }
         mContext.enforceCallingPermission(
                 android.Manifest.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS,
                 "hasBindAppWidgetPermission packageName=" + packageName);
@@ -660,6 +688,9 @@
     }
 
     public void setBindAppWidgetPermission(String packageName, boolean permission) {
+        if (!mHasFeature) {
+            return;
+        }
         mContext.enforceCallingPermission(
                 android.Manifest.permission.MODIFY_APPWIDGET_BIND_PERMISSIONS,
                 "setBindAppWidgetPermission packageName=" + packageName);
@@ -678,6 +709,9 @@
     // Binds to a specific RemoteViewsService
     public void bindRemoteViewsService(int appWidgetId, Intent intent, IBinder connection) {
         synchronized (mAppWidgetIds) {
+            if (!mHasFeature) {
+                return;
+            }
             ensureStateLoadedLocked();
             AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
             if (id == null) {
@@ -735,6 +769,9 @@
     // Unbinds from a specific RemoteViewsService
     public void unbindRemoteViewsService(int appWidgetId, Intent intent) {
         synchronized (mAppWidgetIds) {
+            if (!mHasFeature) {
+                return;
+            }
             ensureStateLoadedLocked();
             // Unbind from the RemoteViewsService (which will trigger a callback to the bound
             // RemoteViewsAdapter)
@@ -846,6 +883,9 @@
 
     public AppWidgetProviderInfo getAppWidgetInfo(int appWidgetId) {
         synchronized (mAppWidgetIds) {
+            if (!mHasFeature) {
+                return null;
+            }
             ensureStateLoadedLocked();
             AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
             if (id != null && id.provider != null && !id.provider.zombie) {
@@ -858,6 +898,9 @@
     public RemoteViews getAppWidgetViews(int appWidgetId) {
         if (DBG) log("getAppWidgetViews id=" + appWidgetId);
         synchronized (mAppWidgetIds) {
+            if (!mHasFeature) {
+                return null;
+            }
             ensureStateLoadedLocked();
             AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
             if (id != null) {
@@ -870,6 +913,9 @@
 
     public List<AppWidgetProviderInfo> getInstalledProviders(int categoryFilter) {
         synchronized (mAppWidgetIds) {
+            if (!mHasFeature) {
+                return new ArrayList<AppWidgetProviderInfo>(0);
+            }
             ensureStateLoadedLocked();
             final int N = mInstalledProviders.size();
             ArrayList<AppWidgetProviderInfo> result = new ArrayList<AppWidgetProviderInfo>(N);
@@ -884,6 +930,9 @@
     }
 
     public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
+        if (!mHasFeature) {
+            return;
+        }
         if (appWidgetIds == null) {
             return;
         }
@@ -929,6 +978,9 @@
 
     public void updateAppWidgetOptions(int appWidgetId, Bundle options) {
         synchronized (mAppWidgetIds) {
+            if (!mHasFeature) {
+                return;
+            }
             options = cloneIfLocalBinder(options);
             ensureStateLoadedLocked();
             AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
@@ -953,6 +1005,9 @@
 
     public Bundle getAppWidgetOptions(int appWidgetId) {
         synchronized (mAppWidgetIds) {
+            if (!mHasFeature) {
+                return Bundle.EMPTY;
+            }
             ensureStateLoadedLocked();
             AppWidgetId id = lookupAppWidgetIdLocked(appWidgetId);
             if (id != null && id.options != null) {
@@ -964,6 +1019,9 @@
     }
 
     public void partiallyUpdateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
+        if (!mHasFeature) {
+            return;
+        }
         if (appWidgetIds == null) {
             return;
         }
@@ -987,6 +1045,9 @@
     }
 
     public void notifyAppWidgetViewDataChanged(int[] appWidgetIds, int viewId) {
+        if (!mHasFeature) {
+            return;
+        }
         if (appWidgetIds == null) {
             return;
         }
@@ -1005,6 +1066,9 @@
     }
 
     public void updateAppWidgetProvider(ComponentName provider, RemoteViews views) {
+        if (!mHasFeature) {
+            return;
+        }
         synchronized (mAppWidgetIds) {
             ensureStateLoadedLocked();
             Provider p = lookupProviderLocked(provider);
@@ -1147,6 +1211,9 @@
 
     public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
             List<RemoteViews> updatedViews) {
+        if (!mHasFeature) {
+            return new int[0];
+        }
         int callingUid = enforceCallingUid(packageName);
         synchronized (mAppWidgetIds) {
             ensureStateLoadedLocked();
@@ -1169,6 +1236,9 @@
 
     public void stopListening(int hostId) {
         synchronized (mAppWidgetIds) {
+            if (!mHasFeature) {
+                return;
+            }
             ensureStateLoadedLocked();
             Host host = lookupHostLocked(Binder.getCallingUid(), hostId);
             if (host != null) {
@@ -1558,6 +1628,9 @@
     }
 
     void saveStateLocked() {
+        if (!mHasFeature) {
+            return;
+        }
         AtomicFile file = savedStateFile();
         FileOutputStream stream;
         try {
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index a45c0ff..2a3c87e 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -63,6 +63,7 @@
 import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
+import android.os.SELinux;
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
@@ -746,6 +747,9 @@
         // correct directory.
         mBaseStateDir = new File(Environment.getSecureDataDirectory(), "backup");
         mBaseStateDir.mkdirs();
+        if (!SELinux.restorecon(mBaseStateDir)) {
+            Slog.e(TAG, "SELinux restorecon failed on " + mBaseStateDir);
+        }
         mDataDir = Environment.getDownloadCacheDirectory();
 
         mPasswordHashFile = new File(mBaseStateDir, "pwhash");
@@ -2136,6 +2140,10 @@
                         ParcelFileDescriptor.MODE_CREATE |
                         ParcelFileDescriptor.MODE_TRUNCATE);
 
+                if (!SELinux.restorecon(mBackupDataName)) {
+                    Slog.e(TAG, "SELinux restorecon failed on " + mBackupDataName);
+                }
+
                 mNewState = ParcelFileDescriptor.open(mNewStateName,
                         ParcelFileDescriptor.MODE_READ_WRITE |
                         ParcelFileDescriptor.MODE_CREATE |
@@ -4697,6 +4705,10 @@
                             ParcelFileDescriptor.MODE_CREATE |
                             ParcelFileDescriptor.MODE_TRUNCATE);
 
+                if (!SELinux.restorecon(mBackupDataName)) {
+                    Slog.e(TAG, "SElinux restorecon failed for " + mBackupDataName);
+                }
+
                 if (mTransport.getRestoreData(mBackupData) != BackupConstants.TRANSPORT_OK) {
                     // Transport-level failure, so we wind everything up and
                     // terminate the restore operation.
diff --git a/services/java/com/android/server/BluetoothManagerService.java b/services/java/com/android/server/BluetoothManagerService.java
old mode 100755
new mode 100644
index 33e712a..ea7b696
--- a/services/java/com/android/server/BluetoothManagerService.java
+++ b/services/java/com/android/server/BluetoothManagerService.java
@@ -19,6 +19,7 @@
 import android.app.ActivityManager;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.IBluetooth;
+import android.bluetooth.IBluetoothGatt;
 import android.bluetooth.IBluetoothCallback;
 import android.bluetooth.IBluetoothManager;
 import android.bluetooth.IBluetoothManagerCallback;
@@ -87,6 +88,9 @@
     // and Airplane mode will have higher priority.
     private static final int BLUETOOTH_ON_AIRPLANE=2;
 
+    private static final int SERVICE_IBLUETOOTH = 1;
+    private static final int SERVICE_IBLUETOOTHGATT = 2;
+
     private final Context mContext;
 
     // Locks are not provided for mName and mAddress.
@@ -97,6 +101,7 @@
     private final RemoteCallbackList<IBluetoothManagerCallback> mCallbacks;
     private final RemoteCallbackList<IBluetoothStateChangeCallback> mStateChangeCallbacks;
     private IBluetooth mBluetooth;
+    private IBluetoothGatt mBluetoothGatt;
     private boolean mBinding;
     private boolean mUnbinding;
     // used inside handler thread
@@ -463,6 +468,11 @@
         }
     }
 
+    public IBluetoothGatt getBluetoothGatt() {
+        // sync protection
+        return mBluetoothGatt;
+    }
+
     private void sendBluetoothStateCallback(boolean isUp) {
         int n = mStateChangeCallbacks.beginBroadcast();
         if (DBG) Log.d(TAG,"Broadcasting onBluetoothStateChange("+isUp+") to " + n + " receivers.");
@@ -575,16 +585,35 @@
         }
 
         public void onServiceConnected(ComponentName className, IBinder service) {
-            if (DBG) Log.d(TAG, "BluetoothServiceConnection: connected to AdapterService");
+            if (DBG) Log.d(TAG, "BluetoothServiceConnection: " + className.getClassName());
             Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_CONNECTED);
+            // TBD if (className.getClassName().equals(IBluetooth.class.getName())) {
+            if (className.getClassName().equals("com.android.bluetooth.btservice.AdapterService")) {
+                msg.arg1 = SERVICE_IBLUETOOTH;
+                // } else if (className.getClassName().equals(IBluetoothGatt.class.getName())) {
+            } else if (className.getClassName().equals("com.android.bluetooth.gatt.GattService")) {
+                msg.arg1 = SERVICE_IBLUETOOTHGATT;
+            } else {
+                Log.e(TAG, "Unknown service connected: " + className.getClassName());
+                return;
+            }
             msg.obj = service;
             mHandler.sendMessage(msg);
         }
 
         public void onServiceDisconnected(ComponentName className) {
             // Called if we unexpected disconnected.
-            if (DBG) Log.d(TAG, "BluetoothServiceConnection: disconnected from AdapterService");
+            if (DBG) Log.d(TAG, "BluetoothServiceConnection, disconnected: " +
+                           className.getClassName());
             Message msg = mHandler.obtainMessage(MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED);
+            if (className.getClassName().equals("com.android.bluetooth.btservice.AdapterService")) {
+                msg.arg1 = SERVICE_IBLUETOOTH;
+            } else if (className.getClassName().equals("com.android.bluetooth.gatt.GattService")) {
+                msg.arg1 = SERVICE_IBLUETOOTHGATT;
+            } else {
+                Log.e(TAG, "Unknown service disconnected: " + className.getClassName());
+                return;
+            }
             mHandler.sendMessage(msg);
         }
     }
@@ -746,13 +775,18 @@
                 }
                 case MESSAGE_BLUETOOTH_SERVICE_CONNECTED:
                 {
-                    if (DBG) Log.d(TAG,"MESSAGE_BLUETOOTH_SERVICE_CONNECTED");
-
-                    //Remove timeout
-                    mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
+                    if (DBG) Log.d(TAG,"MESSAGE_BLUETOOTH_SERVICE_CONNECTED: " + msg.arg1);
 
                     IBinder service = (IBinder) msg.obj;
                     synchronized(mConnection) {
+                        if (msg.arg1 == SERVICE_IBLUETOOTHGATT) {
+                            mBluetoothGatt = IBluetoothGatt.Stub.asInterface(service);
+                            break;
+                        } // else must be SERVICE_IBLUETOOTH
+
+                        //Remove timeout
+                            mHandler.removeMessages(MESSAGE_TIMEOUT_BIND);
+
                         mBinding = false;
                         mBluetooth = IBluetooth.Stub.asInterface(service);
 
@@ -816,11 +850,19 @@
                 }
                 case MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED:
                 {
-                    Log.e(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED");
+                    Log.e(TAG, "MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED: " + msg.arg1);
                     synchronized(mConnection) {
-                        // if service is unbinded already, do nothing and return
-                        if (mBluetooth == null) return;
-                        mBluetooth = null;
+                        if (msg.arg1 == SERVICE_IBLUETOOTH) {
+                            // if service is unbinded already, do nothing and return
+                            if (mBluetooth == null) break;
+                            mBluetooth = null;
+                        } else if (msg.arg1 == SERVICE_IBLUETOOTHGATT) {
+                            mBluetoothGatt = null;
+                            break;
+                        } else {
+                            Log.e(TAG, "Bad msg.arg1: " + msg.arg1);
+                            break;
+                        }
                     }
 
                     if (mEnable) {
@@ -1048,10 +1090,19 @@
                 boolean isUp = (newState==BluetoothAdapter.STATE_ON);
                 sendBluetoothStateCallback(isUp);
 
-                //If Bluetooth is off, send service down event to proxy objects, and unbind
-                if (!isUp && canUnbindBluetoothService()) {
-                    sendBluetoothServiceDownCallback();
-                    unbindAndFinish();
+                if (isUp) {
+                    // connect to GattService
+                    Intent i = new Intent(IBluetoothGatt.class.getName());
+                    if (!mContext.bindServiceAsUser(i, mConnection, Context.BIND_AUTO_CREATE,
+                                                    UserHandle.CURRENT)) {
+                        Log.e(TAG, "Fail to bind to: " + IBluetoothGatt.class.getName());
+                    }
+                } else {
+                    //If Bluetooth is off, send service down event to proxy objects, and unbind
+                    if (!isUp && canUnbindBluetoothService()) {
+                        sendBluetoothServiceDownCallback();
+                        unbindAndFinish();
+                    }
                 }
             }
 
@@ -1081,9 +1132,9 @@
                         if (mBluetooth.getState() == BluetoothAdapter.STATE_ON) return true;
                     } else if (off) {
                         if (mBluetooth.getState() == BluetoothAdapter.STATE_OFF) return true;
-		    } else {
+                    } else {
                         if (mBluetooth.getState() != BluetoothAdapter.STATE_ON) return true;
-		    }
+                    }
                 } catch (RemoteException e) {
                     Log.e(TAG, "getState()", e);
                     break;
@@ -1091,9 +1142,9 @@
             }
             if (on || off) {
                 SystemClock.sleep(300);
-	    } else {
+            } else {
                 SystemClock.sleep(50);
-	    }
+            }
             i++;
         }
         Log.e(TAG,"waitForOnOff time out");
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 6dcb403..9e06db8 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -74,6 +74,7 @@
 import android.os.INetworkManagementService;
 import android.os.Looper;
 import android.os.Message;
+import android.os.Messenger;
 import android.os.ParcelFileDescriptor;
 import android.os.PowerManager;
 import android.os.Process;
@@ -2284,9 +2285,17 @@
         }
 
         // Update 464xlat state.
-        // TODO: Move to handleConnect()
         NetworkStateTracker tracker = mNetTrackers[netType];
         if (mClat.requiresClat(netType, tracker)) {
+            // If the connection was previously using clat, but is not using it now, stop the clat
+            // daemon. Normally, this happens automatically when the connection disconnects, but if
+            // the disconnect is not reported, or if the connection's LinkProperties changed for
+            // some other reason (e.g., handoff changes the IP addresses on the link), it would
+            // still be running. If it's not running, then stopping it is a no-op.
+            if (Nat464Xlat.isRunningClat(curLp) && !Nat464Xlat.isRunningClat(newLp)) {
+                mClat.stopClat();
+            }
+            // If the link requires clat to be running, then start the daemon now.
             if (mNetTrackers[netType].getNetworkInfo().isConnected()) {
                 mClat.startClat(tracker);
             } else {
@@ -3220,7 +3229,7 @@
         throwIfLockdownEnabled();
         try {
             int type = mActiveDefaultNetwork;
-            if (ConnectivityManager.isNetworkTypeValid(type)) {
+            if (ConnectivityManager.isNetworkTypeValid(type) && mNetTrackers[type] != null) {
                 mVpn.protect(socket, mNetTrackers[type].getLinkProperties().getInterfaceName());
                 return true;
             }
@@ -3425,4 +3434,12 @@
             throw new IllegalStateException("Unavailable in lockdown mode");
         }
     }
+
+    public void supplyMessenger(int networkType, Messenger messenger) {
+        enforceConnectivityInternalPermission();
+
+        if (isNetworkTypeValid(networkType) && mNetTrackers[networkType] != null) {
+            mNetTrackers[networkType].supplyMessenger(messenger);
+        }
+    }
 }
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
index 18b4ec1..ab70e6f 100644
--- a/services/java/com/android/server/DevicePolicyManagerService.java
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -41,10 +41,14 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.Signature;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
+import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Environment;
@@ -62,6 +66,7 @@
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.Settings;
+import android.util.AtomicFile;
 import android.util.PrintWriterPrinter;
 import android.util.Printer;
 import android.util.Slog;
@@ -88,10 +93,11 @@
  * Implementation of the device policy APIs.
  */
 public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
-    private static final String DEVICE_POLICIES_XML = "device_policies.xml";
 
     private static final String TAG = "DevicePolicyManagerService";
 
+    private static final String DEVICE_POLICIES_XML = "device_policies.xml";
+
     private static final int REQUEST_EXPIRE_PASSWORD = 5571;
 
     private static final long MS_PER_DAY = 86400 * 1000;
@@ -109,6 +115,8 @@
     IPowerManager mIPowerManager;
     IWindowManager mIWindowManager;
 
+    private DeviceOwner mDeviceOwner;
+
     public static class DevicePolicyData {
         int mActivePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
         int mActivePasswordLength = 0;
@@ -507,6 +515,7 @@
         filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
         filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
         filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
+        filter.addAction(Intent.ACTION_PACKAGE_ADDED);
         filter.addDataScheme("package");
         context.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler);
     }
@@ -545,6 +554,14 @@
         }
     }
 
+    void loadDeviceOwner() {
+        synchronized (this) {
+            if (DeviceOwner.isRegistered()) {
+                mDeviceOwner = new DeviceOwner();
+            }
+        }
+    }
+
     /**
      * Set an alarm for an upcoming event - expiration warning, expiration, or post-expiration
      * reminders.  Clears alarm if no expirations are configured.
@@ -709,7 +726,9 @@
         Intent resolveIntent = new Intent();
         resolveIntent.setComponent(adminName);
         List<ResolveInfo> infos = mContext.getPackageManager().queryBroadcastReceivers(
-                resolveIntent, PackageManager.GET_META_DATA, userHandle);
+                resolveIntent,
+                PackageManager.GET_META_DATA | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS,
+                userHandle);
         if (infos == null || infos.size() <= 0) {
             throw new IllegalArgumentException("Unknown admin: " + adminName);
         }
@@ -994,6 +1013,7 @@
     public void systemReady() {
         synchronized (this) {
             loadSettingsLocked(getUserData(UserHandle.USER_OWNER), UserHandle.USER_OWNER);
+            loadDeviceOwner();
         }
     }
 
@@ -1052,6 +1072,7 @@
                 }
                 if (replaceIndex == -1) {
                     policy.mAdminList.add(newAdmin);
+                    enableIfNecessary(info.getPackageName(), userHandle);
                 } else {
                     policy.mAdminList.set(replaceIndex, newAdmin);
                 }
@@ -1119,6 +1140,11 @@
                 return;
             }
             if (admin.getUid() != Binder.getCallingUid()) {
+                // If trying to remove device owner, refuse when the caller is not the owner.
+                if (mDeviceOwner != null
+                        && adminReceiver.getPackageName().equals(mDeviceOwner.getPackageName())) {
+                    return;
+                }
                 mContext.enforceCallingOrSelfPermission(
                         android.Manifest.permission.BIND_DEVICE_ADMIN, null);
             }
@@ -2351,6 +2377,49 @@
         }
     }
 
+    @Override
+    public boolean setDeviceOwner(String packageName) {
+        if (packageName == null
+                || !DeviceOwner.isInstalled(packageName, mContext.getPackageManager())) {
+            throw new IllegalArgumentException("Invalid package name " + packageName
+                    + " for device owner");
+        }
+        synchronized (this) {
+            if (mDeviceOwner == null && !isDeviceProvisioned()) {
+                mDeviceOwner = new DeviceOwner(packageName);
+                mDeviceOwner.writeOwnerFile();
+                return true;
+            } else {
+                throw new IllegalStateException("Trying to set device owner to " + packageName
+                        + ", owner=" + mDeviceOwner.getPackageName()
+                        + ", device_provisioned=" + isDeviceProvisioned());
+            }
+        }
+    }
+
+    @Override
+    public boolean isDeviceOwner(String packageName) {
+        synchronized (this) {
+            return mDeviceOwner != null
+                    && mDeviceOwner.getPackageName().equals(packageName);
+        }
+    }
+
+    @Override
+    public String getDeviceOwner() {
+        synchronized (this) {
+            if (mDeviceOwner != null) {
+                return mDeviceOwner.getPackageName();
+            }
+        }
+        return null;
+    }
+
+    private boolean isDeviceProvisioned() {
+        return Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.DEVICE_PROVISIONED, 0) > 0;
+    }
+
     private void enforceCrossUserPermission(int userHandle) {
         if (userHandle < 0) {
             throw new IllegalArgumentException("Invalid userId " + userHandle);
@@ -2364,6 +2433,22 @@
         }
     }
 
+    private void enableIfNecessary(String packageName, int userId) {
+        try {
+            IPackageManager ipm = AppGlobals.getPackageManager();
+            ApplicationInfo ai = ipm.getApplicationInfo(packageName,
+                    PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS,
+                    userId);
+            if (ai.enabledSetting
+                    == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
+                ipm.setApplicationEnabledSetting(packageName,
+                        PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
+                        PackageManager.DONT_KILL_APP, userId, "DevicePolicyManager");
+            }
+        } catch (RemoteException e) {
+        }
+    }
+
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
@@ -2399,4 +2484,92 @@
             }
         }
     }
+
+    static class DeviceOwner {
+        private static final String DEVICE_OWNER_XML = "device_owner.xml";
+        private static final String TAG_DEVICE_OWNER = "device-owner";
+        private static final String ATTR_PACKAGE = "package";
+        private String mPackageName;
+
+        DeviceOwner() {
+            readOwnerFile();
+        }
+
+        DeviceOwner(String packageName) {
+            this.mPackageName = packageName;
+        }
+
+        static boolean isRegistered() {
+            return new File(Environment.getSystemSecureDirectory(),
+                    DEVICE_OWNER_XML).exists();
+        }
+
+        String getPackageName() {
+            return mPackageName;
+        }
+
+        static boolean isInstalled(String packageName, PackageManager pm) {
+            try {
+                PackageInfo pi;
+                if ((pi = pm.getPackageInfo(packageName, 0)) != null) {
+                    if ((pi.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+                        return true;
+                    }
+                }
+            } catch (NameNotFoundException nnfe) {
+                Slog.w(TAG, "Device Owner package " + packageName + " not installed.");
+            }
+            return false;
+        }
+
+        void readOwnerFile() {
+            AtomicFile file = new AtomicFile(new File(Environment.getSystemSecureDirectory(),
+                    DEVICE_OWNER_XML));
+            try {
+                FileInputStream input = file.openRead();
+                XmlPullParser parser = Xml.newPullParser();
+                parser.setInput(input, null);
+                int type;
+                while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+                        && type != XmlPullParser.START_TAG) {
+                }
+                String tag = parser.getName();
+                if (!TAG_DEVICE_OWNER.equals(tag)) {
+                    throw new XmlPullParserException(
+                            "Device Owner file does not start with device-owner tag: found " + tag);
+                }
+                mPackageName = parser.getAttributeValue(null, ATTR_PACKAGE);
+                input.close();
+            } catch (XmlPullParserException xppe) {
+                Slog.e(TAG, "Error parsing device-owner file\n" + xppe);
+            } catch (IOException ioe) {
+                Slog.e(TAG, "IO Exception when reading device-owner file\n" + ioe);
+            }
+        }
+
+        void writeOwnerFile() {
+            synchronized (this) {
+                writeOwnerFileLocked();
+            }
+        }
+
+        private void writeOwnerFileLocked() {
+            AtomicFile file = new AtomicFile(new File(Environment.getSystemSecureDirectory(),
+                    DEVICE_OWNER_XML));
+            try {
+                FileOutputStream output = file.startWrite();
+                XmlSerializer out = new FastXmlSerializer();
+                out.setOutput(output, "utf-8");
+                out.startDocument(null, true);
+                out.startTag(null, TAG_DEVICE_OWNER);
+                out.attribute(null, ATTR_PACKAGE, mPackageName);
+                out.endTag(null, TAG_DEVICE_OWNER);
+                out.endDocument();
+                out.flush();
+                file.finishWrite(output);
+            } catch (IOException ioe) {
+                Slog.e(TAG, "IO Exception when writing device-owner file\n" + ioe);
+            }
+        }
+    }
 }
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 0863bd6..3b541ec 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -88,6 +88,7 @@
 import android.util.Slog;
 import android.util.Xml;
 import android.view.IWindowManager;
+import android.view.InputChannel;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
@@ -164,12 +165,13 @@
     final SettingsObserver mSettingsObserver;
     final IWindowManager mIWindowManager;
     final HandlerCaller mCaller;
+    final boolean mHasFeature;
     private InputMethodFileManager mFileManager;
     private InputMethodAndSubtypeListManager mImListManager;
     private final HardKeyboardListener mHardKeyboardListener;
     private final WindowManagerService mWindowManagerService;
 
-    final InputBindResult mNoBinding = new InputBindResult(null, null, -1);
+    final InputBindResult mNoBinding = new InputBindResult(null, null, null, -1);
 
     // All known input methods.  mMethodMap also serves as the global
     // lock for this class.
@@ -201,7 +203,9 @@
     class SessionState {
         final ClientState client;
         final IInputMethod method;
-        final IInputMethodSession session;
+
+        IInputMethodSession session;
+        InputChannel channel;
 
         @Override
         public String toString() {
@@ -210,18 +214,20 @@
                             System.identityHashCode(method))
                     + " session " + Integer.toHexString(
                             System.identityHashCode(session))
+                    + " channel " + channel
                     + "}";
         }
 
         SessionState(ClientState _client, IInputMethod _method,
-                IInputMethodSession _session) {
+                IInputMethodSession _session, InputChannel _channel) {
             client = _client;
             method = _method;
             session = _session;
+            channel = _channel;
         }
     }
 
-    class ClientState {
+    static final class ClientState {
         final IInputMethodClient client;
         final IInputContext inputContext;
         final int uid;
@@ -554,18 +560,21 @@
         }
     }
 
-    private static class MethodCallback extends IInputSessionCallback.Stub {
-        private final IInputMethod mMethod;
+    private static final class MethodCallback extends IInputSessionCallback.Stub {
         private final InputMethodManagerService mParentIMMS;
+        private final IInputMethod mMethod;
+        private final InputChannel mChannel;
 
-        MethodCallback(final IInputMethod method, final InputMethodManagerService imms) {
-            mMethod = method;
+        MethodCallback(InputMethodManagerService imms, IInputMethod method,
+                InputChannel channel) {
             mParentIMMS = imms;
+            mMethod = method;
+            mChannel = channel;
         }
 
         @Override
-        public void sessionCreated(IInputMethodSession session) throws RemoteException {
-            mParentIMMS.onSessionCreated(mMethod, session);
+        public void sessionCreated(IInputMethodSession session) {
+            mParentIMMS.onSessionCreated(mMethod, session, mChannel);
         }
     }
 
@@ -608,6 +617,8 @@
         }, true /*asyncHandler*/);
         mWindowManagerService = windowManager;
         mHardKeyboardListener = new HardKeyboardListener();
+        mHasFeature = context.getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_INPUT_METHODS);
 
         mImeSwitcherNotification = new Notification();
         mImeSwitcherNotification.icon = com.android.internal.R.drawable.ic_notification_ime_default;
@@ -981,7 +992,10 @@
             return;
         }
         synchronized (mMethodMap) {
-            mClients.remove(client.asBinder());
+            ClientState cs = mClients.remove(client.asBinder());
+            if (cs != null) {
+                clearClientSessionLocked(cs);
+            }
         }
     }
 
@@ -1056,7 +1070,7 @@
             if (DEBUG) Slog.v(TAG, "Attach new input asks to show input");
             showCurrentInputLocked(getAppShowFlags(), null);
         }
-        return new InputBindResult(session.session, mCurId, mCurSeq);
+        return new InputBindResult(session.session, session.channel, mCurId, mCurSeq);
     }
 
     InputBindResult startInputLocked(IInputMethodClient client,
@@ -1134,16 +1148,10 @@
             }
             if (mHaveConnection) {
                 if (mCurMethod != null) {
-                    if (!cs.sessionRequested) {
-                        cs.sessionRequested = true;
-                        if (DEBUG) Slog.v(TAG, "Creating new session for client " + cs);
-                        executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
-                                MSG_CREATE_SESSION, mCurMethod,
-                                new MethodCallback(mCurMethod, this)));
-                    }
                     // Return to client, and we will get back with it when
                     // we have had a session made for it.
-                    return new InputBindResult(null, mCurId, mCurSeq);
+                    requestClientSessionLocked(cs);
+                    return new InputBindResult(null, null, mCurId, mCurSeq);
                 } else if (SystemClock.uptimeMillis()
                         < (mLastBindTime+TIME_TO_RECONNECT)) {
                     // In this case we have connected to the service, but
@@ -1153,7 +1161,7 @@
                     // we can report back.  If it has been too long, we want
                     // to fall through so we can try a disconnect/reconnect
                     // to see if we can get back in touch with the service.
-                    return new InputBindResult(null, mCurId, mCurSeq);
+                    return new InputBindResult(null, null, mCurId, mCurSeq);
                 } else {
                     EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME,
                             mCurMethodId, SystemClock.uptimeMillis()-mLastBindTime, 0);
@@ -1172,7 +1180,7 @@
         if (!mSystemReady) {
             // If the system is not yet ready, we shouldn't be running third
             // party code.
-            return new InputBindResult(null, mCurMethodId, mCurSeq);
+            return new InputBindResult(null, null, mCurMethodId, mCurSeq);
         }
 
         InputMethodInfo info = mMethodMap.get(mCurMethodId);
@@ -1200,7 +1208,7 @@
                         WindowManager.LayoutParams.TYPE_INPUT_METHOD);
             } catch (RemoteException e) {
             }
-            return new InputBindResult(null, mCurId, mCurSeq);
+            return new InputBindResult(null, null, mCurId, mCurSeq);
         } else {
             mCurIntent = null;
             Slog.w(TAG, "Failure connecting to input method service: "
@@ -1243,32 +1251,34 @@
                 executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
                         MSG_ATTACH_TOKEN, mCurMethod, mCurToken));
                 if (mCurClient != null) {
-                    if (DEBUG) Slog.v(TAG, "Creating first session while with client "
-                            + mCurClient);
-                    executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
-                            MSG_CREATE_SESSION, mCurMethod,
-                            new MethodCallback(mCurMethod, this)));
+                    clearClientSessionLocked(mCurClient);
+                    requestClientSessionLocked(mCurClient);
                 }
             }
         }
     }
 
-    void onSessionCreated(IInputMethod method, IInputMethodSession session) {
+    void onSessionCreated(IInputMethod method, IInputMethodSession session,
+            InputChannel channel) {
         synchronized (mMethodMap) {
             if (mCurMethod != null && method != null
                     && mCurMethod.asBinder() == method.asBinder()) {
                 if (mCurClient != null) {
+                    clearClientSessionLocked(mCurClient);
                     mCurClient.curSession = new SessionState(mCurClient,
-                            method, session);
-                    mCurClient.sessionRequested = false;
+                            method, session, channel);
                     InputBindResult res = attachNewInputLocked(true);
                     if (res.method != null) {
                         executeOrSendMessage(mCurClient.client, mCaller.obtainMessageOO(
                                 MSG_BIND_METHOD, mCurClient.client, res));
                     }
+                    return;
                 }
             }
         }
+
+        // Session abandoned.  Close its associated input channel.
+        channel.dispose();
     }
 
     void unbindCurrentMethodLocked(boolean reportToClient, boolean savePosition) {
@@ -1303,14 +1313,38 @@
                     MSG_UNBIND_METHOD, mCurSeq, mCurClient.client));
         }
     }
-    
-    private void finishSession(SessionState sessionState) {
-        if (sessionState != null && sessionState.session != null) {
-            try {
-                sessionState.session.finishSession();
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Session failed to close due to remote exception", e);
-                setImeWindowVisibilityStatusHiddenLocked();
+
+    void requestClientSessionLocked(ClientState cs) {
+        if (!cs.sessionRequested) {
+            if (DEBUG) Slog.v(TAG, "Creating new session for client " + cs);
+            InputChannel[] channels = InputChannel.openInputChannelPair(cs.toString());
+            cs.sessionRequested = true;
+            executeOrSendMessage(mCurMethod, mCaller.obtainMessageOOO(
+                    MSG_CREATE_SESSION, mCurMethod, channels[1],
+                    new MethodCallback(this, mCurMethod, channels[0])));
+        }
+    }
+
+    void clearClientSessionLocked(ClientState cs) {
+        finishSessionLocked(cs.curSession);
+        cs.curSession = null;
+        cs.sessionRequested = false;
+    }
+
+    private void finishSessionLocked(SessionState sessionState) {
+        if (sessionState != null) {
+            if (sessionState.session != null) {
+                try {
+                    sessionState.session.finishSession();
+                } catch (RemoteException e) {
+                    Slog.w(TAG, "Session failed to close due to remote exception", e);
+                    setImeWindowVisibilityStatusHiddenLocked();
+                }
+                sessionState.session = null;
+            }
+            if (sessionState.channel != null) {
+                sessionState.channel.dispose();
+                sessionState.channel = null;
             }
         }
     }
@@ -1318,12 +1352,10 @@
     void clearCurMethodLocked() {
         if (mCurMethod != null) {
             for (ClientState cs : mClients.values()) {
-                cs.sessionRequested = false;
-                finishSession(cs.curSession);
-                cs.curSession = null;
+                clearClientSessionLocked(cs);
             }
 
-            finishSession(mEnabledSession);
+            finishSessionLocked(mEnabledSession);
             mEnabledSession = null;
             mCurMethod = null;
         }
@@ -1566,7 +1598,8 @@
                             == PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED) {
                         mIPackageManager.setApplicationEnabledSetting(imm.getPackageName(),
                                 PackageManager.COMPONENT_ENABLED_STATE_DEFAULT,
-                                PackageManager.DONT_KILL_APP, mSettings.getCurrentUserId());
+                                PackageManager.DONT_KILL_APP, mSettings.getCurrentUserId(),
+                                mContext.getBasePackageName());
                     }
                 } catch (RemoteException e) {
                 }
@@ -2322,15 +2355,21 @@
                 }
                 args.recycle();
                 return true;
-            case MSG_CREATE_SESSION:
+            case MSG_CREATE_SESSION: {
                 args = (SomeArgs)msg.obj;
+                InputChannel channel = (InputChannel)args.arg2;
                 try {
-                    ((IInputMethod)args.arg1).createSession(
-                            (IInputSessionCallback)args.arg2);
+                    ((IInputMethod)args.arg1).createSession(channel,
+                            (IInputSessionCallback)args.arg3);
                 } catch (RemoteException e) {
+                } finally {
+                    if (channel != null) {
+                        channel.dispose();
+                    }
                 }
                 args.recycle();
                 return true;
+            }
             // ---------------------------------------------------------
 
             case MSG_START_INPUT:
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index c6aca2f..e8d7882 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -1603,7 +1603,7 @@
             boolean mounted = false;
             try {
                 mounted = Environment.MEDIA_MOUNTED.equals(getVolumeState(primary.getPath()));
-            } catch (IllegalStateException e) {
+            } catch (IllegalArgumentException e) {
             }
 
             if (!mounted) {
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index 2210a18..d2acb40 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -839,33 +839,6 @@
         return event.getMessage().endsWith("started");
     }
 
-    // TODO(BT) Remove
-    @Override
-    public void startReverseTethering(String iface) {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
-        // cmd is "tether start first_start first_stop second_start second_stop ..."
-        // an odd number of addrs will fail
-        try {
-            mConnector.execute("tether", "start-reverse", iface);
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
-        }
-        BluetoothTetheringDataTracker.getInstance().startReverseTether(iface);
-
-    }
-
-    // TODO(BT) Remove
-    @Override
-    public void stopReverseTethering() {
-        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
-        try {
-            mConnector.execute("tether", "stop-reverse");
-        } catch (NativeDaemonConnectorException e) {
-            throw e.rethrowAsParcelableException();
-        }
-        BluetoothTetheringDataTracker.getInstance().stopReverseTether();
-    }
-
     @Override
     public void tetherInterface(String iface) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 4631395..a30fc3b 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -862,6 +862,11 @@
             public void run() {
                 Slog.i(TAG, "Making services ready");
 
+                try {
+                    ActivityManagerService.self().startObservingNativeCrashes();
+                } catch (Throwable e) {
+                    reportWtf("observing native crashes", e);
+                }
                 if (!headless) startSystemUi(contextF);
                 try {
                     if (mountServiceF != null) mountServiceF.systemReady();
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index d84018f..527e891 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1070,14 +1070,6 @@
                 if (userState.mBindingServices.contains(componentName)) {
                     continue;
                 }
-                // No enabled installed services => disable accessibility to avoid
-                // sending accessibility events with no recipient across processes.
-                if (userState.mEnabledServices.isEmpty()) {
-                    userState.mIsAccessibilityEnabled = false;
-                    Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                            Settings.Secure.ACCESSIBILITY_ENABLED, 0, userState.mUserId);
-                    return;
-                }
                 if (userState.mEnabledServices.contains(componentName)) {
                     if (service == null) {
                         service = new Service(userState.mUserId, componentName, installedService);
@@ -1098,6 +1090,14 @@
                 }
             }
         }
+
+        // No enabled installed services => disable accessibility to avoid
+        // sending accessibility events with no recipient across processes.
+        if (isEnabled && userState.mEnabledServices.isEmpty()) {
+            userState.mIsAccessibilityEnabled = false;
+            Settings.Secure.putIntForUser(mContext.getContentResolver(),
+                    Settings.Secure.ACCESSIBILITY_ENABLED, 0, userState.mUserId);
+        }
     }
 
     private void scheduleUpdateClientsIfNeededLocked(UserState userState) {
diff --git a/services/java/com/android/server/accounts/AccountManagerService.java b/services/java/com/android/server/accounts/AccountManagerService.java
index 09daf56..14d808f 100644
--- a/services/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/java/com/android/server/accounts/AccountManagerService.java
@@ -58,6 +58,7 @@
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
+import android.os.Process;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
@@ -115,6 +116,7 @@
 
     // Messages that can be sent on mHandler
     private static final int MESSAGE_TIMED_OUT = 3;
+    private static final int MESSAGE_COPY_SHARED_ACCOUNT = 4;
 
     private final IAccountAuthenticatorCache mAuthenticatorCache;
 
@@ -270,7 +272,7 @@
 
     private UserManager getUserManager() {
         if (mUserManager == null) {
-            mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+            mUserManager = UserManager.get(mContext);
         }
         return mUserManager;
     }
@@ -541,17 +543,22 @@
         }
     }
 
-    public boolean addAccount(Account account, String password, Bundle extras) {
+    @Override
+    public boolean addAccountExplicitly(Account account, String password, Bundle extras) {
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
-            Log.v(TAG, "addAccount: " + account
+            Log.v(TAG, "addAccountExplicitly: " + account
                     + ", caller's uid " + Binder.getCallingUid()
                     + ", pid " + Binder.getCallingPid());
         }
         if (account == null) throw new IllegalArgumentException("account is null");
         checkAuthenticateAccountsPermission(account);
-        if (!canUserModifyAccounts(Binder.getCallingUid())) {
-            return false;
-        }
+        /*
+         * Child users are not allowed to add accounts. Only the accounts that are
+         * shared by the parent profile can be added to child profile.
+         *
+         * TODO: Only allow accounts that were shared to be added by
+         *     a limited user.
+         */
 
         UserAccounts accounts = getUserAccountsForCaller();
         // fails if the account already exists
@@ -588,12 +595,9 @@
                         if (result.getBoolean(AccountManager.KEY_BOOLEAN_RESULT, false)) {
                             // Create a Session for the target user and pass in the bundle
                             completeCloningAccount(result, account, toAccounts);
-                        } else {
-                            clonePassword(fromAccounts, toAccounts, account);
                         }
                         return;
                     } else {
-                        clonePassword(fromAccounts, toAccounts, account);
                         super.onResult(result);
                     }
                 }
@@ -604,23 +608,6 @@
         return true;
     }
 
-    // TODO: Remove fallback - move to authenticator
-    private void clonePassword(UserAccounts fromAccounts, UserAccounts toAccounts,
-            Account account) {
-        long id = clearCallingIdentity();
-        try {
-            String password = readPasswordInternal(fromAccounts, account);
-            String extraFlags = readUserDataInternal(fromAccounts, account, "flags");
-            String extraServices = readUserDataInternal(fromAccounts, account, "services");
-            Bundle extras = new Bundle();
-            extras.putString("flags", extraFlags);
-            extras.putString("services", extraServices);
-            addAccountInternal(toAccounts, account, password, extras, true);
-        } finally {
-            restoreCallingIdentity(id);
-        }
-    }
-
     void completeCloningAccount(final Bundle result, final Account account,
             final UserAccounts targetUser) {
         long id = clearCallingIdentity();
@@ -633,7 +620,17 @@
                 }
 
                 public void run() throws RemoteException {
-                    mAuthenticator.addAccountFromCredentials(this, account, result);
+                    // Confirm that the owner's account still exists before this step.
+                    UserAccounts owner = getUserAccounts(UserHandle.USER_OWNER);
+                    synchronized (owner.cacheLock) {
+                        Account[] ownerAccounts = getAccounts(UserHandle.USER_OWNER);
+                        for (Account acc : ownerAccounts) {
+                            if (acc.equals(account)) {
+                                mAuthenticator.addAccountFromCredentials(this, account, result);
+                                break;
+                            }
+                        }
+                    }
                 }
 
                 public void onResult(Bundle result) {
@@ -706,7 +703,33 @@
                 db.endTransaction();
             }
             sendAccountsChangedBroadcast(accounts.userId);
-            return true;
+        }
+        if (accounts.userId == UserHandle.USER_OWNER) {
+            addAccountToLimitedUsers(account);
+        }
+        return true;
+    }
+
+    /**
+     * Adds the account to all limited users as shared accounts. If the user is currently
+     * running, then clone the account too.
+     * @param account the account to share with limited users
+     */
+    private void addAccountToLimitedUsers(Account account) {
+        List<UserInfo> users = getUserManager().getUsers();
+        for (UserInfo user : users) {
+            if (user.isRestricted()) {
+                addSharedAccountAsUser(account, user.id);
+                try {
+                    if (ActivityManagerNative.getDefault().isUserRunning(user.id, false)) {
+                        mMessageHandler.sendMessage(mMessageHandler.obtainMessage(
+                                MESSAGE_COPY_SHARED_ACCOUNT, UserHandle.USER_OWNER, user.id,
+                                account));
+                    }
+                } catch (RemoteException re) {
+                    // Shouldn't happen
+                }
+            }
         }
     }
 
@@ -906,6 +929,7 @@
         }
     }
 
+    @Override
     public void invalidateAuthToken(String accountType, String authToken) {
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
             Log.v(TAG, "invalidateAuthToken: accountType " + accountType
@@ -1181,7 +1205,7 @@
 
         final int callingUid = getCallingUid();
         clearCallingIdentity();
-        if (callingUid != android.os.Process.SYSTEM_UID) {
+        if (callingUid != Process.SYSTEM_UID) {
             throw new SecurityException("can only call from system");
         }
         UserAccounts accounts = getUserAccounts(UserHandle.getUserId(callingUid));
@@ -1351,7 +1375,7 @@
         String subtitle = "";
         if (index > 0) {
             title = titleAndSubtitle.substring(0, index);
-            subtitle = titleAndSubtitle.substring(index + 1);            
+            subtitle = titleAndSubtitle.substring(index + 1);
         }
         UserHandle user = new UserHandle(userId);
         n.setLatestEventInfo(mContext, title, subtitle,
@@ -1409,7 +1433,7 @@
         return id;
     }
 
-    public void addAcount(final IAccountManagerResponse response, final String accountType,
+    public void addAccount(final IAccountManagerResponse response, final String accountType,
             final String authTokenType, final String[] requiredFeatures,
             final boolean expectActivityLaunch, final Bundle optionsIn) {
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
@@ -1425,9 +1449,8 @@
         if (accountType == null) throw new IllegalArgumentException("accountType is null");
         checkManageAccountsPermission();
 
-        // Is user allowed to modify accounts?
-        if (!getUserManager().getUserRestrictions(Binder.getCallingUserHandle())
-                .getBoolean(UserManager.ALLOW_MODIFY_ACCOUNTS)) {
+        // Is user disallowed from modifying accounts?
+        if (!canUserModifyAccounts(Binder.getCallingUid())) {
             try {
                 response.onError(AccountManager.ERROR_CODE_USER_RESTRICTED,
                         "User is not allowed to add an account!");
@@ -1472,7 +1495,7 @@
             int userId) {
         // Only allow the system process to read accounts of other users
         if (userId != UserHandle.getCallingUserId()
-                && Binder.getCallingUid() != android.os.Process.myUid()) {
+                && Binder.getCallingUid() != Process.myUid()) {
             throw new SecurityException("User " + UserHandle.getCallingUserId()
                     + " trying to confirm account credentials for " + userId);
         }
@@ -1588,7 +1611,8 @@
 
         public void run() throws RemoteException {
             synchronized (mAccounts.cacheLock) {
-                mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType, mCallingUid);
+                mAccountsOfType = getAccountsFromCacheLocked(mAccounts, mAccountType, mCallingUid,
+                        null);
             }
             // check whether each account matches the requested features
             mAccountsWithFeatures = new ArrayList<Account>(mAccountsOfType.length);
@@ -1677,7 +1701,7 @@
         long identityToken = clearCallingIdentity();
         try {
             synchronized (accounts.cacheLock) {
-                return getAccountsFromCacheLocked(accounts, null, callingUid);
+                return getAccountsFromCacheLocked(accounts, null, callingUid, null);
             }
         } finally {
             restoreCallingIdentity(identityToken);
@@ -1718,7 +1742,7 @@
                 if (userAccounts == null) continue;
                 synchronized (userAccounts.cacheLock) {
                     Account[] accounts = getAccountsFromCacheLocked(userAccounts, null,
-                            Binder.getCallingUid());
+                            Binder.getCallingUid(), null);
                     for (int a = 0; a < accounts.length; a++) {
                         runningAccounts.add(new AccountAndUser(accounts[a], userId));
                     }
@@ -1732,10 +1756,15 @@
 
     @Override
     public Account[] getAccountsAsUser(String type, int userId) {
-        final int callingUid = Binder.getCallingUid();
+        return getAccountsAsUser(type, userId, null, -1);
+    }
+
+    private Account[] getAccountsAsUser(String type, int userId, String callingPackage,
+            int packageUid) {
+        int callingUid = Binder.getCallingUid();
         // Only allow the system process to read accounts of other users
         if (userId != UserHandle.getCallingUserId()
-                && callingUid != android.os.Process.myUid()) {
+                && callingUid != Process.myUid()) {
             throw new SecurityException("User " + UserHandle.getCallingUserId()
                     + " trying to get account for " + userId);
         }
@@ -1745,12 +1774,17 @@
                     + ", caller's uid " + Binder.getCallingUid()
                     + ", pid " + Binder.getCallingPid());
         }
+        // If the original calling app was using the framework account chooser activity, we'll
+        // be passed in the original caller's uid here, which is what should be used for filtering.
+        if (packageUid != -1 && UserHandle.isSameApp(callingUid, Process.myUid())) {
+            callingUid = packageUid;
+        }
         checkReadAccountsPermission();
         UserAccounts accounts = getUserAccounts(userId);
         long identityToken = clearCallingIdentity();
         try {
             synchronized (accounts.cacheLock) {
-                return getAccountsFromCacheLocked(accounts, type, callingUid);
+                return getAccountsFromCacheLocked(accounts, type, callingUid, callingPackage);
             }
         } finally {
             restoreCallingIdentity(identityToken);
@@ -1821,6 +1855,16 @@
         return getAccountsAsUser(type, UserHandle.getCallingUserId());
     }
 
+    @Override
+    public Account[] getAccountsForPackage(String packageName, int uid) {
+        int callingUid = Binder.getCallingUid();
+        if (!UserHandle.isSameApp(callingUid, Process.myUid())) {
+            throw new SecurityException("getAccountsForPackage() called from unauthorized uid "
+                    + callingUid + " with uid=" + uid);
+        }
+        return getAccountsAsUser(null, UserHandle.getCallingUserId(), packageName, uid);
+    }
+
     public void getAccountsByFeatures(IAccountManagerResponse response,
             String type, String[] features) {
         if (Log.isLoggable(TAG, Log.VERBOSE)) {
@@ -1840,7 +1884,7 @@
             if (features == null || features.length == 0) {
                 Account[] accounts;
                 synchronized (userAccounts.cacheLock) {
-                    accounts = getAccountsFromCacheLocked(userAccounts, type, callingUid);
+                    accounts = getAccountsFromCacheLocked(userAccounts, type, callingUid, null);
                 }
                 Bundle result = new Bundle();
                 result.putParcelableArray(AccountManager.KEY_ACCOUNTS, accounts);
@@ -2154,6 +2198,10 @@
                     session.onTimedOut();
                     break;
 
+                case MESSAGE_COPY_SHARED_ACCOUNT:
+                    copyAccountToUser((Account) msg.obj, msg.arg1, msg.arg2);
+                    break;
+
                 default:
                     throw new IllegalStateException("unhandled message: " + msg.what);
             }
@@ -2363,7 +2411,7 @@
                 }
             } else {
                 Account[] accounts = getAccountsFromCacheLocked(userAccounts, null /* type */,
-                        android.os.Process.myUid());
+                        Process.myUid(), null);
                 fout.println("Accounts: " + accounts.length);
                 for (Account account : accounts) {
                     fout.println("  " + account);
@@ -2516,7 +2564,7 @@
 
     private boolean hasExplicitlyGrantedPermission(Account account, String authTokenType,
             int callerUid) {
-        if (callerUid == android.os.Process.SYSTEM_UID) {
+        if (callerUid == Process.SYSTEM_UID) {
             return true;
         }
         UserAccounts accounts = getUserAccountsForCaller();
@@ -2569,10 +2617,10 @@
     }
 
     private boolean canUserModifyAccounts(int callingUid) {
-        if (callingUid != android.os.Process.myUid()) {
-            Bundle restrictions = getUserManager().getUserRestrictions(
-                    new UserHandle(UserHandle.getUserId(callingUid)));
-            if (!restrictions.getBoolean(UserManager.ALLOW_MODIFY_ACCOUNTS)) {
+        if (callingUid != Process.myUid()) {
+            if (getUserManager().getUserRestrictions(
+                    new UserHandle(UserHandle.getUserId(callingUid)))
+                    .getBoolean(UserManager.DISALLOW_MODIFY_ACCOUNTS)) {
                 return false;
             }
         }
@@ -2583,7 +2631,7 @@
             throws RemoteException {
         final int callingUid = getCallingUid();
 
-        if (callingUid != android.os.Process.SYSTEM_UID) {
+        if (callingUid != Process.SYSTEM_UID) {
             throw new SecurityException();
         }
 
@@ -2703,14 +2751,14 @@
     }
 
     private Account[] filterSharedAccounts(UserAccounts userAccounts, Account[] unfiltered,
-            int callingUid) {
+            int callingUid, String callingPackage) {
         if (getUserManager() == null || userAccounts == null || userAccounts.userId < 0
-                || callingUid == android.os.Process.myUid()) {
+                || callingUid == Process.myUid()) {
             return unfiltered;
         }
         if (mUserManager.getUserInfo(userAccounts.userId).isRestricted()) {
             String[] packages = mPackageManager.getPackagesForUid(callingUid);
-            // If any of the packages includes a white listed package, return the full set,
+            // If any of the packages is a white listed package, return the full set,
             // otherwise return non-shared accounts only.
             // This might be a temporary way to specify a whitelist
             String whiteList = mContext.getResources().getString(
@@ -2723,16 +2771,34 @@
             ArrayList<Account> allowed = new ArrayList<Account>();
             Account[] sharedAccounts = getSharedAccountsAsUser(userAccounts.userId);
             if (sharedAccounts == null || sharedAccounts.length == 0) return unfiltered;
-            for (Account account : unfiltered) {
-                boolean found = false;
-                for (Account shared : sharedAccounts) {
-                    if (shared.equals(account)) {
-                        found = true;
-                        break;
+            String requiredAccountType = "";
+            try {
+                for (String packageName : packages) {
+                    PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0);
+                    if (pi != null && pi.restrictedAccountType != null) {
+                        requiredAccountType = pi.restrictedAccountType;
+                        // If it matches the package name of the original caller, use this choice.
+                        if (callingPackage != null && packageName.equals(callingPackage)) {
+                            break;
+                        }
                     }
                 }
-                if (!found) {
+            } catch (NameNotFoundException nnfe) {
+            }
+            for (Account account : unfiltered) {
+                if (account.type.equals(requiredAccountType)) {
                     allowed.add(account);
+                } else {
+                    boolean found = false;
+                    for (Account shared : sharedAccounts) {
+                        if (shared.equals(account)) {
+                            found = true;
+                            break;
+                        }
+                    }
+                    if (!found) {
+                        allowed.add(account);
+                    }
                 }
             }
             Account[] filtered = new Account[allowed.size()];
@@ -2743,15 +2809,19 @@
         }
     }
 
+    /*
+     * packageName can be null. If not null, it should be used to filter out restricted accounts
+     * that the package is not allowed to access.
+     */
     protected Account[] getAccountsFromCacheLocked(UserAccounts userAccounts, String accountType,
-            int callingUid) {
+            int callingUid, String callingPackage) {
         if (accountType != null) {
             final Account[] accounts = userAccounts.accountCache.get(accountType);
             if (accounts == null) {
                 return EMPTY_ACCOUNT_ARRAY;
             } else {
                 return filterSharedAccounts(userAccounts, Arrays.copyOf(accounts, accounts.length),
-                        callingUid);
+                        callingUid, callingPackage);
             }
         } else {
             int totalLength = 0;
@@ -2768,7 +2838,7 @@
                         accountsOfType.length);
                 totalLength += accountsOfType.length;
             }
-            return filterSharedAccounts(userAccounts, accounts, callingUid);
+            return filterSharedAccounts(userAccounts, accounts, callingUid, callingPackage);
         }
     }
 
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 6f092bf..97fbb9c 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -19,6 +19,7 @@
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 
 import android.app.AppOpsManager;
+import android.appwidget.AppWidgetManager;
 import com.android.internal.R;
 import com.android.internal.os.BatteryStatsImpl;
 import com.android.internal.os.ProcessStats;
@@ -1406,7 +1407,7 @@
     public static void setSystemProcess() {
         try {
             ActivityManagerService m = mSelf;
-            
+
             ServiceManager.addService("activity", m, true);
             ServiceManager.addService("meminfo", new MemBinder(m));
             ServiceManager.addService("gfxinfo", new GraphicsBinder(m));
@@ -1444,6 +1445,11 @@
         mWindowManager = wm;
     }
 
+    public void startObservingNativeCrashes() {
+        final NativeCrashListener ncl = new NativeCrashListener();
+        ncl.start();
+    }
+
     public static final Context main(int factoryTest) {
         AThread thr = new AThread();
         thr.start();
@@ -2194,7 +2200,7 @@
             // the PID of the new process, or else throw a RuntimeException.
             Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread",
                     app.processName, uid, uid, gids, debugFlags, mountExternal,
-                    app.info.targetSdkVersion, null, null);
+                    app.info.targetSdkVersion, app.info.seinfo, null);
 
             BatteryStatsImpl bs = app.batteryStats.getBatteryStats();
             synchronized (bs) {
@@ -8332,6 +8338,14 @@
         final String processName = app == null ? "system_server"
                 : (r == null ? "unknown" : r.processName);
 
+        handleApplicationCrashInner(r, processName, crashInfo);
+    }
+
+    /* Native crash reporting uses this inner version because it needs to be somewhat
+     * decoupled from the AM-managed cleanup lifecycle
+     */
+    void handleApplicationCrashInner(ProcessRecord r, String processName,
+            ApplicationErrorReport.CrashInfo crashInfo) {
         EventLog.writeEvent(EventLogTags.AM_CRASH, Binder.getCallingPid(),
                 UserHandle.getUserId(Binder.getCallingUid()), processName,
                 r == null ? -1 : r.info.flags,
@@ -8845,7 +8859,7 @@
             return null;
         }
 
-        if (!r.crashing && !r.notResponding) {
+        if (!r.crashing && !r.notResponding && !r.forceCrashReport) {
             return null;
         }
 
@@ -8856,7 +8870,7 @@
         report.time = timeMillis;
         report.systemApp = (r.info.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
 
-        if (r.crashing) {
+        if (r.crashing || r.forceCrashReport) {
             report.type = ApplicationErrorReport.TYPE_CRASH;
             report.crashInfo = crashInfo;
         } else if (r.notResponding) {
@@ -10866,7 +10880,7 @@
         mProcessesToGc.remove(app);
         
         // Dismiss any open dialogs.
-        if (app.crashDialog != null) {
+        if (app.crashDialog != null && !app.forceCrashReport) {
             app.crashDialog.dismiss();
             app.crashDialog = null;
         }
@@ -11791,6 +11805,32 @@
                             + callingPid + ", uid=" + callingUid;
                     Slog.w(TAG, msg);
                     throw new SecurityException(msg);
+                } else if (AppWidgetManager.ACTION_APPWIDGET_CONFIGURE.equals(intent.getAction())) {
+                    // Special case for compatibility: we don't want apps to send this,
+                    // but historically it has not been protected and apps may be using it
+                    // to poke their own app widget.  So, instead of making it protected,
+                    // just limit it to the caller.
+                    if (callerApp == null) {
+                        String msg = "Permission Denial: not allowed to send broadcast "
+                                + intent.getAction() + " from unknown caller.";
+                        Slog.w(TAG, msg);
+                        throw new SecurityException(msg);
+                    } else if (intent.getComponent() != null) {
+                        // They are good enough to send to an explicit component...  verify
+                        // it is being sent to the calling app.
+                        if (!intent.getComponent().getPackageName().equals(
+                                callerApp.info.packageName)) {
+                            String msg = "Permission Denial: not allowed to send broadcast "
+                                    + intent.getAction() + " to "
+                                    + intent.getComponent().getPackageName() + " from "
+                                    + callerApp.info.packageName;
+                            Slog.w(TAG, msg);
+                            throw new SecurityException(msg);
+                        }
+                    } else {
+                        // Limit broadcast to their own package.
+                        intent.setPackage(callerApp.info.packageName);
+                    }
                 }
             } catch (RemoteException e) {
                 Slog.w(TAG, "Remote exception", e);
diff --git a/services/java/com/android/server/am/BroadcastQueue.java b/services/java/com/android/server/am/BroadcastQueue.java
index e8e8f25..ac7eb89 100644
--- a/services/java/com/android/server/am/BroadcastQueue.java
+++ b/services/java/com/android/server/am/BroadcastQueue.java
@@ -29,7 +29,6 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -1080,6 +1079,9 @@
                 pw.print("  #"); pw.print(i); pw.print(": "); pw.println(r);
                 pw.print("    ");
                 pw.println(r.intent.toShortString(false, true, true, false));
+                if (r.targetComp != null && r.targetComp != r.intent.getComponent()) {
+                    pw.print("    targetComp: "); pw.println(r.targetComp.toShortString());
+                }
                 Bundle bundle = r.intent.getExtras();
                 if (bundle != null) {
                     pw.print("    extras: "); pw.println(bundle.toString());
diff --git a/services/java/com/android/server/am/BroadcastRecord.java b/services/java/com/android/server/am/BroadcastRecord.java
index a98afb6..83cc0ea 100644
--- a/services/java/com/android/server/am/BroadcastRecord.java
+++ b/services/java/com/android/server/am/BroadcastRecord.java
@@ -38,6 +38,7 @@
  */
 class BroadcastRecord extends Binder {
     final Intent intent;    // the original intent that generated us
+    final ComponentName targetComp; // original component name set on the intent
     final ProcessRecord callerApp; // process that sent this
     final String callerPackage; // who sent this
     final int callingPid;   // the pid of who sent this
@@ -84,9 +85,12 @@
 
         pw.print(prefix); pw.print(this); pw.print(" to user "); pw.println(userId);
         pw.print(prefix); pw.println(intent.toInsecureString());
+        if (targetComp != null && targetComp != intent.getComponent()) {
+            pw.print(prefix); pw.print("  targetComp: "); pw.println(targetComp.toShortString());
+        }
         Bundle bundle = intent.getExtras();
         if (bundle != null) {
-            pw.print(prefix); pw.print("extras: "); pw.println(bundle.toString());
+            pw.print(prefix); pw.print("  extras: "); pw.println(bundle.toString());
         }
         pw.print(prefix); pw.print("caller="); pw.print(callerPackage); pw.print(" ");
                 pw.print(callerApp != null ? callerApp.toShortString() : "null");
@@ -174,6 +178,7 @@
             int _userId) {
         queue = _queue;
         intent = _intent;
+        targetComp = _intent.getComponent();
         callerApp = _callerApp;
         callerPackage = _callerPackage;
         callingPid = _callingPid;
diff --git a/services/java/com/android/server/am/NativeCrashListener.java b/services/java/com/android/server/am/NativeCrashListener.java
new file mode 100644
index 0000000..e83433f
--- /dev/null
+++ b/services/java/com/android/server/am/NativeCrashListener.java
@@ -0,0 +1,264 @@
+/*
+ * 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.am;
+
+import android.app.ApplicationErrorReport.CrashInfo;
+import android.util.Slog;
+
+import libcore.io.ErrnoException;
+import libcore.io.Libcore;
+import libcore.io.StructTimeval;
+import libcore.io.StructUcred;
+
+import static libcore.io.OsConstants.*;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileDescriptor;
+import java.net.InetSocketAddress;
+import java.net.InetUnixAddress;
+
+/**
+ * Set up a Unix domain socket that debuggerd will connect() to in
+ * order to write a description of a native crash.  The crash info is
+ * then parsed and forwarded to the ActivityManagerService's normal
+ * crash handling code.
+ *
+ * Note that this component runs in a separate thread.
+ */
+class NativeCrashListener extends Thread {
+    static final String TAG = "NativeCrashListener";
+    static final boolean DEBUG = false;
+
+    // Must match the path defined in debuggerd.c.
+    static final String DEBUGGERD_SOCKET_PATH = "/data/system/ndebugsocket";
+
+    // Use a short timeout on socket operations and abandon the connection
+    // on hard errors
+    static final long SOCKET_TIMEOUT_MILLIS = 1000;  // 1 second
+
+    final ActivityManagerService mAm;
+
+    /*
+     * Spin the actual work of handling a debuggerd crash report into a
+     * separate thread so that the listener can go immediately back to
+     * accepting incoming connections.
+     */
+    class NativeCrashReporter extends Thread {
+        ProcessRecord mApp;
+        int mSignal;
+        String mCrashReport;
+
+        NativeCrashReporter(ProcessRecord app, int signal, String report) {
+            super("NativeCrashReport");
+            mApp = app;
+            mSignal = signal;
+            mCrashReport = report;
+        }
+
+        @Override
+        public void run() {
+            try {
+                CrashInfo ci = new CrashInfo();
+                ci.exceptionClassName = "Native crash";
+                ci.exceptionMessage = Libcore.os.strsignal(mSignal);
+                ci.throwFileName = "unknown";
+                ci.throwClassName = "unknown";
+                ci.throwMethodName = "unknown";
+                ci.stackTrace = mCrashReport;
+
+                if (DEBUG) Slog.v(TAG, "Calling handleApplicationCrash()");
+                mAm.handleApplicationCrashInner(mApp, mApp.processName, ci);
+                if (DEBUG) Slog.v(TAG, "<-- handleApplicationCrash() returned");
+            } catch (Exception e) {
+                Slog.e(TAG, "Unable to report native crash", e);
+            }
+        }
+    }
+
+    /*
+     * Daemon thread that accept()s incoming domain socket connections from debuggerd
+     * and processes the crash dump that is passed through.
+     */
+    NativeCrashListener() {
+        mAm = ActivityManagerService.self();
+    }
+
+    @Override
+    public void run() {
+        final byte[] ackSignal = new byte[1];
+
+        if (DEBUG) Slog.i(TAG, "Starting up");
+
+        // The file system entity for this socket is created with 0700 perms, owned
+        // by system:system.  debuggerd runs as root, so is capable of connecting to
+        // it, but 3rd party apps cannot.
+        {
+            File socketFile = new File(DEBUGGERD_SOCKET_PATH);
+            if (socketFile.exists()) {
+                socketFile.delete();
+            }
+        }
+
+        try {
+            FileDescriptor serverFd = Libcore.os.socket(AF_UNIX, SOCK_STREAM, 0);
+            final InetUnixAddress sockAddr = new InetUnixAddress(DEBUGGERD_SOCKET_PATH);
+            Libcore.os.bind(serverFd, sockAddr, 0);
+            Libcore.os.listen(serverFd, 1);
+
+            while (true) {
+                InetSocketAddress peer = new InetSocketAddress();
+                FileDescriptor peerFd = null;
+                try {
+                    if (DEBUG) Slog.v(TAG, "Waiting for debuggerd connection");
+                    peerFd = Libcore.os.accept(serverFd, peer);
+                    if (DEBUG) Slog.v(TAG, "Got debuggerd socket " + peerFd);
+                    if (peerFd != null) {
+                        // Only the superuser is allowed to talk to us over this socket
+                        StructUcred credentials =
+                                Libcore.os.getsockoptUcred(peerFd, SOL_SOCKET, SO_PEERCRED);
+                        if (credentials.uid == 0) {
+                            // the reporting thread may take responsibility for
+                            // acking the debugger; make sure we play along.
+                            consumeNativeCrashData(peerFd);
+                        }
+                    }
+                } catch (Exception e) {
+                    Slog.w(TAG, "Error handling connection", e);
+                } finally {
+                    // Always ack debuggerd's connection to us.  The actual
+                    // byte written is irrelevant.
+                    if (peerFd != null) {
+                        try {
+                            Libcore.os.write(peerFd, ackSignal, 0, 1);
+                        } catch (Exception e) { /* we don't care about failures here */ }
+                    }
+                }
+            }
+        } catch (Exception e) {
+            Slog.e(TAG, "Unable to init native debug socket!", e);
+        }
+    }
+
+    static int unpackInt(byte[] buf, int offset) {
+        int b0, b1, b2, b3;
+
+        b0 = ((int) buf[offset]) & 0xFF; // mask against sign extension
+        b1 = ((int) buf[offset+1]) & 0xFF;
+        b2 = ((int) buf[offset+2]) & 0xFF;
+        b3 = ((int) buf[offset+3]) & 0xFF;
+        return (b0 << 24) | (b1 << 16) | (b2 << 8) | b3;
+    }
+
+    static int readExactly(FileDescriptor fd, byte[] buffer, int offset, int numBytes)
+            throws ErrnoException {
+        int totalRead = 0;
+        while (numBytes > 0) {
+            int n = Libcore.os.read(fd, buffer, offset + totalRead, numBytes);
+            if (n <= 0) {
+                if (DEBUG) {
+                    Slog.w(TAG, "Needed " + numBytes + " but saw " + n);
+                }
+                return -1;  // premature EOF or timeout
+            }
+            numBytes -= n;
+            totalRead += n;
+        }
+        return totalRead;
+    }
+
+    // Read the crash report from the debuggerd connection
+    void consumeNativeCrashData(FileDescriptor fd) {
+        if (DEBUG) Slog.i(TAG, "debuggerd connected");
+        final byte[] buf = new byte[4096];
+        final ByteArrayOutputStream os = new ByteArrayOutputStream(4096);
+
+        try {
+            StructTimeval timeout = StructTimeval.fromMillis(SOCKET_TIMEOUT_MILLIS);
+            Libcore.os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, timeout);
+            Libcore.os.setsockoptTimeval(fd, SOL_SOCKET, SO_SNDTIMEO, timeout);
+
+            // first, the pid and signal number
+            int headerBytes = readExactly(fd, buf, 0, 8);
+            if (headerBytes != 8) {
+                // protocol failure; give up
+                Slog.e(TAG, "Unable to read from debuggerd");
+                return;
+            }
+
+            int pid = unpackInt(buf, 0);
+            int signal = unpackInt(buf, 4);
+            if (DEBUG) {
+                Slog.v(TAG, "Read pid=" + pid + " signal=" + signal);
+            }
+
+            // now the text of the dump
+            if (pid > 0) {
+                final ProcessRecord pr;
+                synchronized (mAm.mPidsSelfLocked) {
+                    pr = mAm.mPidsSelfLocked.get(pid);
+                }
+                if (pr != null) {
+                    int bytes;
+                    do {
+                        // get some data
+                        bytes = Libcore.os.read(fd, buf, 0, buf.length);
+                        if (bytes > 0) {
+                            if (DEBUG) {
+                                String s = new String(buf, 0, bytes, "UTF-8");
+                                Slog.v(TAG, "READ=" + bytes + "> " + s);
+                            }
+                            // did we just get the EOD null byte?
+                            if (buf[bytes-1] == 0) {
+                                os.write(buf, 0, bytes-1);  // exclude the EOD token
+                                break;
+                            }
+                            // no EOD, so collect it and read more
+                            os.write(buf, 0, bytes);
+                        }
+                    } while (bytes > 0);
+
+                    // Okay, we've got the report.
+                    if (DEBUG) Slog.v(TAG, "processing");
+
+                    // Mark the process record as being a native crash so that the
+                    // cleanup mechanism knows we're still submitting the report
+                    // even though the process will vanish as soon as we let
+                    // debuggerd proceed.
+                    synchronized (mAm) {
+                        pr.crashing = true;
+                        pr.forceCrashReport = true;
+                    }
+
+                    // Crash reporting is synchronous but we want to let debuggerd
+                    // go about it business right away, so we spin off the actual
+                    // reporting logic on a thread and let it take it's time.
+                    final String reportString = new String(os.toByteArray(), "UTF-8");
+                    (new NativeCrashReporter(pr, signal, reportString)).start();
+                } else {
+                    Slog.w(TAG, "Couldn't find ProcessRecord for pid " + pid);
+                }
+            } else {
+                Slog.e(TAG, "Bogus pid!");
+            }
+        } catch (Exception e) {
+            Slog.e(TAG, "Exception dealing with report", e);
+            // ugh, fail.
+        }
+    }
+
+}
diff --git a/services/java/com/android/server/am/ProcessList.java b/services/java/com/android/server/am/ProcessList.java
index 9e25e30..1a635a9 100644
--- a/services/java/com/android/server/am/ProcessList.java
+++ b/services/java/com/android/server/am/ProcessList.java
@@ -144,8 +144,8 @@
     // These are the high-end OOM level limits.  This is appropriate for a
     // 1280x800 or larger screen with around 1GB RAM.  Values are in KB.
     private final long[] mOomMinFreeHigh = new long[] {
-            32768, 40960, 49152,
-            57344, 65536, 81920
+            49152, 61440, 73728,
+            86016, 98304, 122880
     };
     // The actual OOM killer memory levels we are using.
     private final long[] mOomMinFree = new long[mOomAdj.length];
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index a32af2f..7929f96 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -138,6 +138,7 @@
     boolean persistent;         // always keep this application running?
     boolean crashing;           // are we in the process of crashing?
     Dialog crashDialog;         // dialog being displayed due to crash.
+    boolean forceCrashReport;   // suppress normal auto-dismiss of crash dialog & report UI?
     boolean notResponding;      // does the app have a not responding dialog?
     Dialog anrDialog;           // dialog being displayed due to app not resp.
     boolean removed;            // has app package been removed from device?
diff --git a/services/java/com/android/server/connectivity/Nat464Xlat.java b/services/java/com/android/server/connectivity/Nat464Xlat.java
index 2884eaf..59403c5 100644
--- a/services/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/java/com/android/server/connectivity/Nat464Xlat.java
@@ -87,6 +87,10 @@
         return netType == TYPE_MOBILE && !lp.hasIPv4Address();
     }
 
+    public static boolean isRunningClat(LinkProperties lp) {
+      return lp != null && lp.getAllInterfaceNames().contains(CLAT_INTERFACE_NAME);
+    }
+
     /**
      * Starts the clat daemon.
      * @param lp The link properties of the interface to start clatd on.
diff --git a/services/java/com/android/server/content/ContentService.java b/services/java/com/android/server/content/ContentService.java
index 545ec93..68cf5fc 100644
--- a/services/java/com/android/server/content/ContentService.java
+++ b/services/java/com/android/server/content/ContentService.java
@@ -37,7 +37,6 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
-import android.text.format.DateUtils;
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseIntArray;
@@ -408,10 +407,10 @@
                 "no permission to write the sync settings");
         int userId = UserHandle.getCallingUserId();
 
-        if (pollFrequency <= DateUtils.MINUTE_IN_MILLIS) {
+        if (pollFrequency < 60) {
             Slog.w(TAG, "Requested poll frequency of " + pollFrequency
-                    + "ms being rounded up to 60 seconds.");
-            pollFrequency = DateUtils.MINUTE_IN_MILLIS;
+                    + " seconds being rounded up to 60 seconds.");
+            pollFrequency = 60;
         }
 
         long identityToken = clearCallingIdentity();
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index 3ae652a..a82f421 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -63,13 +63,13 @@
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
 import static com.android.internal.util.ArrayUtils.appendInt;
 import static com.android.internal.util.Preconditions.checkNotNull;
+import static com.android.internal.util.XmlUtils.readBooleanAttribute;
+import static com.android.internal.util.XmlUtils.readIntAttribute;
+import static com.android.internal.util.XmlUtils.readLongAttribute;
+import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
+import static com.android.internal.util.XmlUtils.writeIntAttribute;
+import static com.android.internal.util.XmlUtils.writeLongAttribute;
 import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT;
-import static com.android.server.net.NetworkPolicyManagerService.XmlUtils.readBooleanAttribute;
-import static com.android.server.net.NetworkPolicyManagerService.XmlUtils.readIntAttribute;
-import static com.android.server.net.NetworkPolicyManagerService.XmlUtils.readLongAttribute;
-import static com.android.server.net.NetworkPolicyManagerService.XmlUtils.writeBooleanAttribute;
-import static com.android.server.net.NetworkPolicyManagerService.XmlUtils.writeIntAttribute;
-import static com.android.server.net.NetworkPolicyManagerService.XmlUtils.writeLongAttribute;
 import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_UPDATED;
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
 import static org.xmlpull.v1.XmlPullParser.START_TAG;
@@ -149,7 +149,6 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
-import java.net.ProtocolException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -2088,44 +2087,4 @@
         }
         fout.print("]");
     }
-
-    public static class XmlUtils {
-        public static int readIntAttribute(XmlPullParser in, String name) throws IOException {
-            final String value = in.getAttributeValue(null, name);
-            try {
-                return Integer.parseInt(value);
-            } catch (NumberFormatException e) {
-                throw new ProtocolException("problem parsing " + name + "=" + value + " as int");
-            }
-        }
-
-        public static void writeIntAttribute(XmlSerializer out, String name, int value)
-                throws IOException {
-            out.attribute(null, name, Integer.toString(value));
-        }
-
-        public static long readLongAttribute(XmlPullParser in, String name) throws IOException {
-            final String value = in.getAttributeValue(null, name);
-            try {
-                return Long.parseLong(value);
-            } catch (NumberFormatException e) {
-                throw new ProtocolException("problem parsing " + name + "=" + value + " as long");
-            }
-        }
-
-        public static void writeLongAttribute(XmlSerializer out, String name, long value)
-                throws IOException {
-            out.attribute(null, name, Long.toString(value));
-        }
-
-        public static boolean readBooleanAttribute(XmlPullParser in, String name) {
-            final String value = in.getAttributeValue(null, name);
-            return Boolean.parseBoolean(value);
-        }
-
-        public static void writeBooleanAttribute(XmlSerializer out, String name, boolean value)
-                throws IOException {
-            out.attribute(null, name, Boolean.toString(value));
-        }
-    }
 }
diff --git a/services/java/com/android/server/pm/Installer.java b/services/java/com/android/server/pm/Installer.java
index 02a2c1b..d9c85bf 100644
--- a/services/java/com/android/server/pm/Installer.java
+++ b/services/java/com/android/server/pm/Installer.java
@@ -188,7 +188,7 @@
         }
     }
 
-    public int install(String name, int uid, int gid) {
+    public int install(String name, int uid, int gid, String seinfo) {
         StringBuilder builder = new StringBuilder("install");
         builder.append(' ');
         builder.append(name);
@@ -196,6 +196,8 @@
         builder.append(uid);
         builder.append(' ');
         builder.append(gid);
+        builder.append(' ');
+        builder.append(seinfo != null ? seinfo : "!");
         return execute(builder.toString());
     }
 
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 09d1426..94aea2f 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -362,6 +362,9 @@
     final HashMap<String, FeatureInfo> mAvailableFeatures =
             new HashMap<String, FeatureInfo>();
 
+    // If mac_permissions.xml was found for seinfo labeling.
+    boolean mFoundPolicyFile;
+
     // All available activities, for your resolving pleasure.
     final ActivityIntentResolver mActivities =
             new ActivityIntentResolver();
@@ -1029,8 +1032,11 @@
 
             readPermissions();
 
+            mFoundPolicyFile = SELinuxMMAC.readInstallPolicy();
+
             mRestoredSettings = mSettings.readLPw(this, sUserManager.getUsers(false),
                     mSdkVersion, mOnlyCore);
+
             long startTime = SystemClock.uptimeMillis();
 
             EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
@@ -1317,6 +1323,12 @@
                             ? (UPDATE_PERMISSIONS_REPLACE_PKG|UPDATE_PERMISSIONS_REPLACE_ALL)
                             : 0));
 
+            // If this is the first boot, and it is a normal boot, then
+            // we need to initialize the default preferred apps.
+            if (!mRestoredSettings && !onlyCore) {
+                mSettings.readDefaultPreferredAppsLPw(this, 0);
+            }
+
             // can downgrade to reader
             mSettings.writeLPr();
 
@@ -2236,6 +2248,34 @@
         }
     }
 
+    private static void checkGrantRevokePermissions(PackageParser.Package pkg, BasePermission bp) {
+        int index = pkg.requestedPermissions.indexOf(bp.name);
+        if (index == -1) {
+            throw new SecurityException("Package " + pkg.packageName
+                    + " has not requested permission " + bp.name);
+        }
+        boolean isNormal =
+                ((bp.protectionLevel&PermissionInfo.PROTECTION_MASK_BASE)
+                        == PermissionInfo.PROTECTION_NORMAL);
+        boolean isDangerous =
+                ((bp.protectionLevel&PermissionInfo.PROTECTION_MASK_BASE)
+                        == PermissionInfo.PROTECTION_DANGEROUS);
+        boolean isDevelopment =
+                ((bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0);
+
+        if (!isNormal && !isDangerous && !isDevelopment) {
+            throw new SecurityException("Permission " + bp.name
+                    + " is not a changeable permission type");
+        }
+
+        if (isNormal || isDangerous) {
+            if (pkg.requestedPermissionsRequired.get(index)) {
+                throw new SecurityException("Can't change " + bp.name
+                        + ". It is required by the application");
+            }
+        }
+    }
+
     public void grantPermission(String packageName, String permissionName) {
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.GRANT_REVOKE_PERMISSIONS, null);
@@ -2246,21 +2286,16 @@
             }
             final BasePermission bp = mSettings.mPermissions.get(permissionName);
             if (bp == null) {
-                throw new IllegalArgumentException("Unknown permission: " + packageName);
+                throw new IllegalArgumentException("Unknown permission: " + permissionName);
             }
-            if (!pkg.requestedPermissions.contains(permissionName)) {
-                throw new SecurityException("Package " + packageName
-                        + " has not requested permission " + permissionName);
-            }
-            if ((bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) == 0) {
-                throw new SecurityException("Permission " + permissionName
-                        + " is not a development permission");
-            }
+
+            checkGrantRevokePermissions(pkg, bp);
+
             final PackageSetting ps = (PackageSetting) pkg.mExtras;
             if (ps == null) {
                 return;
             }
-            final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
+            final GrantedPermissions gp = (ps.sharedUser != null) ? ps.sharedUser : ps;
             if (gp.grantedPermissions.add(permissionName)) {
                 if (ps.haveGids) {
                     gp.gids = appendInts(gp.gids, bp.gids);
@@ -2282,21 +2317,16 @@
             }
             final BasePermission bp = mSettings.mPermissions.get(permissionName);
             if (bp == null) {
-                throw new IllegalArgumentException("Unknown permission: " + packageName);
+                throw new IllegalArgumentException("Unknown permission: " + permissionName);
             }
-            if (!pkg.requestedPermissions.contains(permissionName)) {
-                throw new SecurityException("Package " + packageName
-                        + " has not requested permission " + permissionName);
-            }
-            if ((bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) == 0) {
-                throw new SecurityException("Permission " + permissionName
-                        + " is not a development permission");
-            }
+
+            checkGrantRevokePermissions(pkg, bp);
+
             final PackageSetting ps = (PackageSetting) pkg.mExtras;
             if (ps == null) {
                 return;
             }
-            final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps;
+            final GrantedPermissions gp = (ps.sharedUser != null) ? ps.sharedUser : ps;
             if (gp.grantedPermissions.remove(permissionName)) {
                 gp.grantedPermissions.remove(permissionName);
                 if (ps.haveGids) {
@@ -3676,9 +3706,9 @@
         }
     }
 
-    private int createDataDirsLI(String packageName, int uid) {
+    private int createDataDirsLI(String packageName, int uid, String seinfo) {
         int[] users = sUserManager.getUserIds();
-        int res = mInstaller.install(packageName, uid, uid);
+        int res = mInstaller.install(packageName, uid, uid, seinfo);
         if (res < 0) {
             return res;
         }
@@ -4020,6 +4050,10 @@
                 pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
             }
 
+            if (mFoundPolicyFile) {
+                SELinuxMMAC.assignSeinfoValue(pkg);
+            }
+
             pkg.applicationInfo.uid = pkgSetting.appId;
             pkg.mExtras = pkgSetting;
 
@@ -4158,7 +4192,8 @@
                             recovered = true;
 
                             // And now re-install the app.
-                            ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid);
+                            ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid,
+                                                   pkg.applicationInfo.seinfo);
                             if (ret == -1) {
                                 // Ack should not happen!
                                 msg = prefix + pkg.packageName
@@ -4204,7 +4239,8 @@
                         Log.v(TAG, "Want this data dir: " + dataPath);
                 }
                 //invoke installer to do the actual installation
-                int ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid);
+                int ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid,
+                                           pkg.applicationInfo.seinfo);
                 if (ret < 0) {
                     // Error from installer
                     mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
@@ -5915,7 +5951,7 @@
                 null);
 
         final int uid = Binder.getCallingUid();
-        if (!isUserAllowed(UserHandle.getUserId(uid), UserManager.ALLOW_INSTALL_APPS)) {
+        if (isUserRestricted(UserHandle.getUserId(uid), UserManager.DISALLOW_INSTALL_APPS)) {
             try {
                 observer.packageInstalled("", PackageManager.INSTALL_FAILED_USER_RESTRICTED);
             } catch (RemoteException re) {
@@ -5963,7 +5999,7 @@
                     android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
                     "installExistingPackage for user " + userId);
         }
-        if (!isUserAllowed(userId, UserManager.ALLOW_INSTALL_APPS)) {
+        if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
             return PackageManager.INSTALL_FAILED_USER_RESTRICTED;
         }
 
@@ -5997,13 +6033,13 @@
         return PackageManager.INSTALL_SUCCEEDED;
     }
 
-    private boolean isUserAllowed(int userId, String restrictionKey) {
+    private boolean isUserRestricted(int userId, String restrictionKey) {
         Bundle restrictions = sUserManager.getUserRestrictions(userId);
-        if (!restrictions.getBoolean(UserManager.ALLOW_INSTALL_APPS)) {
-            Log.w(TAG, "User does not have permission to: " + restrictionKey);
-            return false;
+        if (restrictions.getBoolean(restrictionKey, false)) {
+            Log.w(TAG, "User is restricted: " + restrictionKey);
+            return true;
         }
-        return true;
+        return false;
     }
 
     @Override
@@ -8400,7 +8436,7 @@
                     android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
                     "deletePackage for user " + userId);
         }
-        if (!isUserAllowed(userId, UserManager.ALLOW_UNINSTALL_APPS)) {
+        if (isUserRestricted(userId, UserManager.DISALLOW_UNINSTALL_APPS)) {
             try {
                 observer.packageDeleted(packageName, PackageManager.DELETE_FAILED_USER_RESTRICTED);
             } catch (RemoteException re) {
@@ -8446,7 +8482,8 @@
         IDevicePolicyManager dpm = IDevicePolicyManager.Stub.asInterface(
                 ServiceManager.getService(Context.DEVICE_POLICY_SERVICE));
         try {
-            if (dpm != null && dpm.packageHasActiveAdmins(packageName, userId)) {
+            if (dpm != null && (dpm.packageHasActiveAdmins(packageName, userId)
+                    || dpm.isDeviceOwner(packageName))) {
                 Slog.w(TAG, "Not removing package " + packageName + ": has active device admin");
                 return PackageManager.DELETE_FAILED_DEVICE_POLICY_MANAGER;
             }
@@ -8702,7 +8739,7 @@
                         false, //installed
                         true,  //stopped
                         true,  //notLaunched
-                        null, null);
+                        null, null, null);
                 if (!isSystemApp(ps)) {
                     if (ps.isAnyInstalled(sUserManager.getUserIds())) {
                         // Other user still have this package installed, so all
@@ -9275,9 +9312,12 @@
 
     @Override
     public void setApplicationEnabledSetting(String appPackageName,
-            int newState, int flags, int userId) {
+            int newState, int flags, int userId, String callingPackage) {
         if (!sUserManager.exists(userId)) return;
-        setEnabledSetting(appPackageName, null, newState, flags, userId);
+        if (callingPackage == null) {
+            callingPackage = Integer.toString(Binder.getCallingUid());
+        }
+        setEnabledSetting(appPackageName, null, newState, flags, userId, callingPackage);
     }
 
     @Override
@@ -9285,11 +9325,11 @@
             int newState, int flags, int userId) {
         if (!sUserManager.exists(userId)) return;
         setEnabledSetting(componentName.getPackageName(),
-                componentName.getClassName(), newState, flags, userId);
+                componentName.getClassName(), newState, flags, userId, null);
     }
 
-    private void setEnabledSetting(
-            final String packageName, String className, int newState, final int flags, int userId) {
+    private void setEnabledSetting(final String packageName, String className, int newState,
+            final int flags, int userId, String callingPackage) {
         if (!(newState == COMPONENT_ENABLED_STATE_DEFAULT
               || newState == COMPONENT_ENABLED_STATE_ENABLED
               || newState == COMPONENT_ENABLED_STATE_DISABLED
@@ -9335,7 +9375,12 @@
                     // Nothing to do
                     return;
                 }
-                pkgSetting.setEnabled(newState, userId);
+                if (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
+                    || newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
+                    // Don't care about who enables an app.
+                    callingPackage = null;
+                }
+                pkgSetting.setEnabled(newState, userId, callingPackage);
                 // pkgSetting.pkg.mSetEnabled = newState;
             } else {
                 // We're dealing with a component level state change
diff --git a/services/java/com/android/server/pm/PackageSettingBase.java b/services/java/com/android/server/pm/PackageSettingBase.java
index ae1b213..e64ec6d 100644
--- a/services/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/java/com/android/server/pm/PackageSettingBase.java
@@ -189,14 +189,20 @@
         return DEFAULT_USER_STATE;
     }
 
-    void setEnabled(int state, int userId) {
-        modifyUserState(userId).enabled = state;
+    void setEnabled(int state, int userId, String callingPackage) {
+        PackageUserState st = modifyUserState(userId);
+        st.enabled = state;
+        st.lastDisableAppCaller = callingPackage;
     }
 
     int getEnabled(int userId) {
         return readUserState(userId).enabled;
     }
 
+    String getLastDisabledAppCaller(int userId) {
+        return readUserState(userId).lastDisableAppCaller;
+    }
+
     void setInstalled(boolean inst, int userId) {
         modifyUserState(userId).installed = inst;
     }
@@ -249,13 +255,14 @@
     }
 
     void setUserState(int userId, int enabled, boolean installed, boolean stopped,
-            boolean notLaunched, HashSet<String> enabledComponents,
+            boolean notLaunched, String lastDisableAppCaller, HashSet<String> enabledComponents,
             HashSet<String> disabledComponents) {
         PackageUserState state = modifyUserState(userId);
         state.enabled = enabled;
         state.installed = installed;
         state.stopped = stopped;
         state.notLaunched = notLaunched;
+        state.lastDisableAppCaller = lastDisableAppCaller;
         state.enabledComponents = enabledComponents;
         state.disabledComponents = disabledComponents;
     }
diff --git a/services/java/com/android/server/pm/SELinuxMMAC.java b/services/java/com/android/server/pm/SELinuxMMAC.java
new file mode 100644
index 0000000..4bbdb5e
--- /dev/null
+++ b/services/java/com/android/server/pm/SELinuxMMAC.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageParser;
+import android.content.pm.Signature;
+import android.os.Environment;
+import android.util.Slog;
+import android.util.Xml;
+
+import com.android.internal.util.XmlUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+
+import java.util.HashMap;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+/**
+ * Centralized access to SELinux MMAC (middleware MAC) implementation.
+ * {@hide}
+ */
+public final class SELinuxMMAC {
+
+    private static final String TAG = "SELinuxMMAC";
+
+    private static final boolean DEBUG_POLICY = false;
+    private static final boolean DEBUG_POLICY_INSTALL = DEBUG_POLICY || false;
+
+    // Signature seinfo values read from policy.
+    private static final HashMap<Signature, String> sSigSeinfo =
+        new HashMap<Signature, String>();
+
+    // Package name seinfo values read from policy.
+    private static final HashMap<String, String> sPackageSeinfo =
+        new HashMap<String, String>();
+
+    // Locations of potential install policy files.
+    private static final File[] INSTALL_POLICY_FILE = {
+        new File(Environment.getDataDirectory(), "system/mac_permissions.xml"),
+        new File(Environment.getRootDirectory(), "etc/security/mac_permissions.xml"),
+        null};
+
+    private static void flushInstallPolicy() {
+        sSigSeinfo.clear();
+        sPackageSeinfo.clear();
+    }
+
+    /**
+     * Parses an MMAC install policy from a predefined list of locations.
+     * @param none
+     * @return boolean indicating whether an install policy was correctly parsed.
+     */
+    public static boolean readInstallPolicy() {
+
+        return readInstallPolicy(INSTALL_POLICY_FILE);
+    }
+
+    /**
+     * Parses an MMAC install policy given as an argument.
+     * @param File object representing the path of the policy.
+     * @return boolean indicating whether the install policy was correctly parsed.
+     */
+    public static boolean readInstallPolicy(File policyFile) {
+
+        return readInstallPolicy(new File[]{policyFile,null});
+    }
+
+    private static boolean readInstallPolicy(File[] policyFiles) {
+
+        FileReader policyFile = null;
+        int i = 0;
+        while (policyFile == null && policyFiles != null && policyFiles[i] != null) {
+            try {
+                policyFile = new FileReader(policyFiles[i]);
+                break;
+            } catch (FileNotFoundException e) {
+                Slog.d(TAG,"Couldn't find install policy " + policyFiles[i].getPath());
+            }
+            i++;
+        }
+
+        if (policyFile == null) {
+            Slog.d(TAG, "No policy file found. All seinfo values will be null.");
+            return false;
+        }
+
+        Slog.d(TAG, "Using install policy file " + policyFiles[i].getPath());
+
+        flushInstallPolicy();
+
+        try {
+            XmlPullParser parser = Xml.newPullParser();
+            parser.setInput(policyFile);
+
+            XmlUtils.beginDocument(parser, "policy");
+            while (true) {
+                XmlUtils.nextElement(parser);
+                if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
+                    break;
+                }
+
+                String tagName = parser.getName();
+                if ("signer".equals(tagName)) {
+                    String cert = parser.getAttributeValue(null, "signature");
+                    if (cert == null) {
+                        Slog.w(TAG, "<signer> without signature at "
+                               + parser.getPositionDescription());
+                        XmlUtils.skipCurrentTag(parser);
+                        continue;
+                    }
+                    Signature signature;
+                    try {
+                        signature = new Signature(cert);
+                    } catch (IllegalArgumentException e) {
+                        Slog.w(TAG, "<signer> with bad signature at "
+                               + parser.getPositionDescription(), e);
+                        XmlUtils.skipCurrentTag(parser);
+                        continue;
+                    }
+                    String seinfo = readSeinfoTag(parser);
+                    if (seinfo != null) {
+                        if (DEBUG_POLICY_INSTALL)
+                            Slog.i(TAG, "<signer> tag: (" + cert + ") assigned seinfo="
+                                   + seinfo);
+
+                        sSigSeinfo.put(signature, seinfo);
+                    }
+                } else if ("default".equals(tagName)) {
+                    String seinfo = readSeinfoTag(parser);
+                    if (seinfo != null) {
+                        if (DEBUG_POLICY_INSTALL)
+                            Slog.i(TAG, "<default> tag assigned seinfo=" + seinfo);
+
+                        // The 'null' signature is the default seinfo value
+                        sSigSeinfo.put(null, seinfo);
+                    }
+                } else if ("package".equals(tagName)) {
+                    String pkgName = parser.getAttributeValue(null, "name");
+                    if (pkgName == null) {
+                        Slog.w(TAG, "<package> without name at "
+                               + parser.getPositionDescription());
+                        XmlUtils.skipCurrentTag(parser);
+                        continue;
+                    }
+                    String seinfo = readSeinfoTag(parser);
+                    if (seinfo != null) {
+                        if (DEBUG_POLICY_INSTALL)
+                            Slog.i(TAG, "<package> tag: (" + pkgName +
+                                   ") assigned seinfo=" + seinfo);
+
+                        sPackageSeinfo.put(pkgName, seinfo);
+                    }
+                } else {
+                    XmlUtils.skipCurrentTag(parser);
+                    continue;
+                }
+            }
+        } catch (XmlPullParserException e) {
+            Slog.w(TAG, "Got execption parsing ", e);
+        } catch (IOException e) {
+            Slog.w(TAG, "Got execption parsing ", e);
+        }
+        try {
+            policyFile.close();
+        } catch (IOException e) {
+            //omit
+        }
+        return true;
+    }
+
+    private static String readSeinfoTag(XmlPullParser parser) throws
+            IOException, XmlPullParserException {
+
+        int type;
+        int outerDepth = parser.getDepth();
+        String seinfo = null;
+        while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+               && (type != XmlPullParser.END_TAG
+                   || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG
+                || type == XmlPullParser.TEXT) {
+                continue;
+            }
+
+            String tagName = parser.getName();
+            if ("seinfo".equals(tagName)) {
+                String seinfoValue = parser.getAttributeValue(null, "value");
+                if (validateValue(seinfoValue)) {
+                    seinfo = seinfoValue;
+                } else {
+                    Slog.w(TAG, "<seinfo> without valid value at "
+                           + parser.getPositionDescription());
+                }
+            }
+            XmlUtils.skipCurrentTag(parser);
+        }
+        return seinfo;
+    }
+
+    /**
+     * General validation routine for tag values.
+     * Returns a boolean indicating if the passed string
+     * contains only letters or underscores.
+     */
+    private static boolean validateValue(String name) {
+        if (name == null)
+            return false;
+
+        final int N = name.length();
+        if (N == 0)
+            return false;
+
+        for (int i = 0; i < N; i++) {
+            final char c = name.charAt(i);
+            if ((c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && (c != '_')) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Labels a package based on an seinfo tag from install policy.
+     * The label is attached to the ApplicationInfo instance of the package.
+     * @param PackageParser.Package object representing the package
+     *         to labeled.
+     * @return String holding the value of the seinfo label that was assigned.
+     *         Value may be null which indicates no seinfo label was assigned.
+     */
+    public static void assignSeinfoValue(PackageParser.Package pkg) {
+
+        /*
+         * Non system installed apps should be treated the same. This
+         * means that any post-loaded apk will be assigned the default
+         * tag, if one exists in the policy, else null, without respect
+         * to the signing key.
+         */
+        if (((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) ||
+            ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0)) {
+
+            // We just want one of the signatures to match.
+            for (Signature s : pkg.mSignatures) {
+                if (s == null)
+                    continue;
+
+                if (sSigSeinfo.containsKey(s)) {
+                    String seinfo = pkg.applicationInfo.seinfo = sSigSeinfo.get(s);
+                    if (DEBUG_POLICY_INSTALL)
+                        Slog.i(TAG, "package (" + pkg.packageName +
+                               ") labeled with seinfo=" + seinfo);
+
+                    return;
+                }
+            }
+
+            // Check for seinfo labeled by package.
+            if (sPackageSeinfo.containsKey(pkg.packageName)) {
+                String seinfo = pkg.applicationInfo.seinfo = sPackageSeinfo.get(pkg.packageName);
+                if (DEBUG_POLICY_INSTALL)
+                    Slog.i(TAG, "package (" + pkg.packageName +
+                           ") labeled with seinfo=" + seinfo);
+                return;
+            }
+        }
+
+        // If we have a default seinfo value then great, otherwise
+        // we set a null object and that is what we started with.
+        String seinfo = pkg.applicationInfo.seinfo = sSigSeinfo.get(null);
+        if (DEBUG_POLICY_INSTALL)
+            Slog.i(TAG, "package (" + pkg.packageName +
+                   ") labeled with seinfo=" + (seinfo == null ? "null" : seinfo));
+    }
+}
diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java
index e645078..2e48074 100644
--- a/services/java/com/android/server/pm/Settings.java
+++ b/services/java/com/android/server/pm/Settings.java
@@ -104,6 +104,7 @@
     private static final String ATTR_CODE = "code";
     private static final String ATTR_NOT_LAUNCHED = "nl";
     private static final String ATTR_ENABLED = "enabled";
+    private static final String ATTR_ENABLED_CALLER = "enabledCaller";
     private static final String ATTR_STOPPED = "stopped";
     private static final String ATTR_INSTALLED = "inst";
 
@@ -453,7 +454,7 @@
                                     installed,
                                     true, // stopped,
                                     true, // notLaunched
-                                    null, null);
+                                    null, null, null);
                             writePackageRestrictionsLPr(user.id);
                         }
                     }
@@ -850,7 +851,7 @@
                                 true,   // installed
                                 false,  // stopped
                                 false,  // notLaunched
-                                null, null);
+                                null, null, null);
                     }
                     return;
                 }
@@ -895,6 +896,8 @@
                     final String enabledStr = parser.getAttributeValue(null, ATTR_ENABLED);
                     final int enabled = enabledStr == null
                             ? COMPONENT_ENABLED_STATE_DEFAULT : Integer.parseInt(enabledStr);
+                    final String enabledCaller = parser.getAttributeValue(null,
+                            ATTR_ENABLED_CALLER);
                     final String installedStr = parser.getAttributeValue(null, ATTR_INSTALLED);
                     final boolean installed = installedStr == null
                             ? true : Boolean.parseBoolean(installedStr);
@@ -925,7 +928,7 @@
                     }
 
                     ps.setUserState(userId, enabled, installed, stopped, notLaunched,
-                            enabledComponents, disabledComponents);
+                            enabledCaller, enabledComponents, disabledComponents);
                 } else if (tagName.equals("preferred-activities")) {
                     readPreferredActivitiesLPw(parser, userId);
                 } else {
@@ -1052,6 +1055,10 @@
                     if (ustate.enabled != COMPONENT_ENABLED_STATE_DEFAULT) {
                         serializer.attribute(null, ATTR_ENABLED,
                                 Integer.toString(ustate.enabled));
+                        if (ustate.lastDisableAppCaller != null) {
+                            serializer.attribute(null, ATTR_ENABLED_CALLER,
+                                    ustate.lastDisableAppCaller);
+                        }
                     }
                     if (ustate.enabledComponents != null
                             && ustate.enabledComponents.size() > 0) {
@@ -1365,6 +1372,7 @@
                     // userId     - application-specific user id
                     // debugFlag  - 0 or 1 if the package is debuggable.
                     // dataPath   - path to package's data path
+                    // seinfo     - seinfo label for the app (assigned at install time)
                     //
                     // NOTE: We prefer not to expose all ApplicationInfo flags for now.
                     //
@@ -1378,6 +1386,8 @@
                     sb.append((int)ai.uid);
                     sb.append(isDebug ? " 1 " : " 0 ");
                     sb.append(dataPath);
+                    sb.append(" ");
+                    sb.append(ai.seinfo);
                     sb.append("\n");
                     str.write(sb.toString().getBytes());
                 }
@@ -1593,9 +1603,6 @@
                     mReadMessages.append("No settings file found\n");
                     PackageManagerService.reportSettingsProblem(Log.INFO,
                             "No settings file; creating initial state");
-                    if (!onlyCore) {
-                        readDefaultPreferredAppsLPw(service, 0);
-                    }
                     mInternalSdkPlatform = mExternalSdkPlatform = sdkVersion;
                     return false;
                 }
@@ -2239,14 +2246,14 @@
             final String enabledStr = parser.getAttributeValue(null, ATTR_ENABLED);
             if (enabledStr != null) {
                 try {
-                    packageSetting.setEnabled(Integer.parseInt(enabledStr), 0 /* userId */);
+                    packageSetting.setEnabled(Integer.parseInt(enabledStr), 0 /* userId */, null);
                 } catch (NumberFormatException e) {
                     if (enabledStr.equalsIgnoreCase("true")) {
-                        packageSetting.setEnabled(COMPONENT_ENABLED_STATE_ENABLED, 0);
+                        packageSetting.setEnabled(COMPONENT_ENABLED_STATE_ENABLED, 0, null);
                     } else if (enabledStr.equalsIgnoreCase("false")) {
-                        packageSetting.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, 0);
+                        packageSetting.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, 0, null);
                     } else if (enabledStr.equalsIgnoreCase("default")) {
-                        packageSetting.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, 0);
+                        packageSetting.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, 0, null);
                     } else {
                         PackageManagerService.reportSettingsProblem(Log.WARN,
                                 "Error in package manager settings: package " + name
@@ -2255,7 +2262,7 @@
                     }
                 }
             } else {
-                packageSetting.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, 0);
+                packageSetting.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, 0, null);
             }
 
             final String installStatusStr = parser.getAttributeValue(null, "installStatus");
@@ -2789,6 +2796,11 @@
             pw.print(ps.getNotLaunched(user.id));
             pw.print(" enabled=");
             pw.println(ps.getEnabled(user.id));
+            String lastDisabledAppCaller = ps.getLastDisabledAppCaller(user.id);
+            if (lastDisabledAppCaller != null) {
+                pw.print(prefix); pw.print("    lastDisabledCaller: ");
+                        pw.println(lastDisabledAppCaller);
+            }
             HashSet<String> cmp = ps.getDisabledComponents(user.id);
             if (cmp != null && cmp.size() > 0) {
                 pw.print(prefix); pw.println("    disabledComponents:");
diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java
index 636b0e5..aa1b2ff 100644
--- a/services/java/com/android/server/pm/UserManagerService.java
+++ b/services/java/com/android/server/pm/UserManagerService.java
@@ -26,7 +26,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.RestrictionEntry;
-import android.content.SharedPreferences;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.UserInfo;
@@ -226,6 +225,13 @@
         }
     }
 
+    @Override
+    public boolean isRestricted() {
+        synchronized (mPackagesLock) {
+            return getUserInfoLocked(UserHandle.getCallingUserId()).isRestricted();
+        }
+    }
+
     /*
      * Should be locked on mUsers before calling this.
      */
@@ -558,7 +564,6 @@
         mNextSerialNumber = MIN_USER_ID;
 
         Bundle restrictions = new Bundle();
-        initRestrictionsToDefaults(restrictions);
         mUserRestrictions.append(UserHandle.USER_OWNER, restrictions);
 
         updateUserIdsLocked();
@@ -608,11 +613,15 @@
             Bundle restrictions = mUserRestrictions.get(userInfo.id);
             if (restrictions != null) {
                 serializer.startTag(null, TAG_RESTRICTIONS);
-                writeBoolean(serializer, restrictions, UserManager.ALLOW_CONFIG_WIFI);
-                writeBoolean(serializer, restrictions, UserManager.ALLOW_MODIFY_ACCOUNTS);
-                writeBoolean(serializer, restrictions, UserManager.ALLOW_INSTALL_APPS);
-                writeBoolean(serializer, restrictions, UserManager.ALLOW_UNINSTALL_APPS);
-                writeBoolean(serializer, restrictions, UserManager.ALLOW_CONFIG_LOCATION_ACCESS);
+                writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_WIFI);
+                writeBoolean(serializer, restrictions, UserManager.DISALLOW_MODIFY_ACCOUNTS);
+                writeBoolean(serializer, restrictions, UserManager.DISALLOW_INSTALL_APPS);
+                writeBoolean(serializer, restrictions, UserManager.DISALLOW_UNINSTALL_APPS);
+                writeBoolean(serializer, restrictions, UserManager.DISALLOW_SHARE_LOCATION);
+                writeBoolean(serializer, restrictions,
+                        UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
+                writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_BLUETOOTH);
+                writeBoolean(serializer, restrictions, UserManager.DISALLOW_USB_FILE_TRANSFER);
                 serializer.endTag(null, TAG_RESTRICTIONS);
             }
             serializer.endTag(null, TAG_USER);
@@ -676,7 +685,6 @@
         long lastLoggedInTime = 0L;
         boolean partial = false;
         Bundle restrictions = new Bundle();
-        initRestrictionsToDefaults(restrictions);
 
         FileInputStream fis = null;
         try {
@@ -725,11 +733,15 @@
                             name = parser.getText();
                         }
                     } else if (TAG_RESTRICTIONS.equals(tag)) {
-                        readBoolean(parser, restrictions, UserManager.ALLOW_CONFIG_WIFI);
-                        readBoolean(parser, restrictions, UserManager.ALLOW_MODIFY_ACCOUNTS);
-                        readBoolean(parser, restrictions, UserManager.ALLOW_INSTALL_APPS);
-                        readBoolean(parser, restrictions, UserManager.ALLOW_UNINSTALL_APPS);
-                        readBoolean(parser, restrictions, UserManager.ALLOW_CONFIG_LOCATION_ACCESS);
+                        readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_WIFI);
+                        readBoolean(parser, restrictions, UserManager.DISALLOW_MODIFY_ACCOUNTS);
+                        readBoolean(parser, restrictions, UserManager.DISALLOW_INSTALL_APPS);
+                        readBoolean(parser, restrictions, UserManager.DISALLOW_UNINSTALL_APPS);
+                        readBoolean(parser, restrictions, UserManager.DISALLOW_SHARE_LOCATION);
+                        readBoolean(parser, restrictions,
+                                UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
+                        readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_BLUETOOTH);
+                        readBoolean(parser, restrictions, UserManager.DISALLOW_USB_FILE_TRANSFER);
                     }
                 }
             }
@@ -758,7 +770,9 @@
     private void readBoolean(XmlPullParser parser, Bundle restrictions,
             String restrictionKey) {
         String value = parser.getAttributeValue(null, restrictionKey);
-        restrictions.putBoolean(restrictionKey, value == null ? true : Boolean.parseBoolean(value));
+        if (value != null) {
+            restrictions.putBoolean(restrictionKey, Boolean.parseBoolean(value));
+        }
     }
 
     private void writeBoolean(XmlSerializer xml, Bundle restrictions, String restrictionKey)
@@ -769,14 +783,6 @@
         }
     }
 
-    private void initRestrictionsToDefaults(Bundle restrictions) {
-        restrictions.putBoolean(UserManager.ALLOW_CONFIG_WIFI, true);
-        restrictions.putBoolean(UserManager.ALLOW_MODIFY_ACCOUNTS, true);
-        restrictions.putBoolean(UserManager.ALLOW_INSTALL_APPS, true);
-        restrictions.putBoolean(UserManager.ALLOW_UNINSTALL_APPS, true);
-        restrictions.putBoolean(UserManager.ALLOW_CONFIG_LOCATION_ACCESS, true);
-    }
-
     private int readIntAttribute(XmlPullParser parser, String attr, int defaultValue) {
         String valueString = parser.getAttributeValue(null, attr);
         if (valueString == null) return defaultValue;
@@ -823,7 +829,6 @@
                     writeUserLocked(userInfo);
                     updateUserIdsLocked();
                     Bundle restrictions = new Bundle();
-                    initRestrictionsToDefaults(restrictions);
                     mUserRestrictions.append(userId, restrictions);
                 }
             }
@@ -1074,12 +1079,12 @@
 
             for (RestrictionEntry entry : entries) {
                 serializer.startTag(null, TAG_ENTRY);
-                serializer.attribute(null, ATTR_KEY, entry.key);
-                if (entry.getStringValue() != null || entry.getMultipleValues() == null) {
-                    String value = entry.getStringValue();
+                serializer.attribute(null, ATTR_KEY, entry.getKey());
+                if (entry.getSelectedString() != null || entry.getAllSelectedStrings() == null) {
+                    String value = entry.getSelectedString();
                     serializer.text(value != null ? value : "");
                 } else {
-                    String[] values = entry.getMultipleValues();
+                    String[] values = entry.getAllSelectedStrings();
                     serializer.attribute(null, ATTR_MULTIPLE, Integer.toString(values.length));
                     for (String value : values) {
                         serializer.startTag(null, TAG_VALUE);
diff --git a/services/java/com/android/server/updates/ConfigUpdateInstallReceiver.java b/services/java/com/android/server/updates/ConfigUpdateInstallReceiver.java
index b065310..d603cfa 100644
--- a/services/java/com/android/server/updates/ConfigUpdateInstallReceiver.java
+++ b/services/java/com/android/server/updates/ConfigUpdateInstallReceiver.java
@@ -102,6 +102,7 @@
                         Slog.i(TAG, "Found new update, installing...");
                         install(altContent, altVersion);
                         Slog.i(TAG, "Installation successful");
+                        postInstall(context, intent);
                     }
                 } catch (Exception e) {
                     Slog.e(TAG, "Could not update content!", e);
@@ -257,4 +258,7 @@
         writeUpdate(updateDir, updateContent, content);
         writeUpdate(updateDir, updateVersion, Long.toString(version).getBytes());
     }
+
+    protected void postInstall(Context context, Intent intent) {
+    }
 }
diff --git a/services/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java b/services/java/com/android/server/updates/SELinuxPolicyInstallReceiver.java
new file mode 100644
index 0000000..748849e
--- /dev/null
+++ b/services/java/com/android/server/updates/SELinuxPolicyInstallReceiver.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 com.android.server.updates;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.SELinux;
+import android.provider.Settings;
+import android.util.Base64;
+import android.util.Slog;
+
+import java.io.IOException;
+
+public class SELinuxPolicyInstallReceiver extends ConfigUpdateInstallReceiver {
+
+    public SELinuxPolicyInstallReceiver() {
+        super("/data/security/", "sepolicy", "metadata/", "version");
+    }
+
+    @Override
+    protected void install(byte[] encodedContent, int version) throws IOException {
+        super.install(Base64.decode(encodedContent, Base64.DEFAULT), version);
+    }
+
+    @Override
+    protected void postInstall(Context context, Intent intent) {
+           boolean mode = Settings.Global.getInt(context.getContentResolver(),
+                                                Settings.Global.SELINUX_STATUS, 0) == 1;
+           SELinux.setSELinuxEnforce(mode);
+    }
+}
diff --git a/services/java/com/android/server/usb/UsbSettingsManager.java b/services/java/com/android/server/usb/UsbSettingsManager.java
index f9aaa17..9b5b312 100644
--- a/services/java/com/android/server/usb/UsbSettingsManager.java
+++ b/services/java/com/android/server/usb/UsbSettingsManager.java
@@ -34,6 +34,7 @@
 import android.hardware.usb.UsbManager;
 import android.os.Binder;
 import android.os.Environment;
+import android.os.Process;
 import android.os.UserHandle;
 import android.util.AtomicFile;
 import android.util.Log;
@@ -853,21 +854,29 @@
 
     public boolean hasPermission(UsbDevice device) {
         synchronized (mLock) {
+            int uid = Binder.getCallingUid();
+            if (uid == Process.SYSTEM_UID) {
+                return true;
+            }
             SparseBooleanArray uidList = mDevicePermissionMap.get(device.getDeviceName());
             if (uidList == null) {
                 return false;
             }
-            return uidList.get(Binder.getCallingUid());
+            return uidList.get(uid);
         }
     }
 
     public boolean hasPermission(UsbAccessory accessory) {
         synchronized (mLock) {
+            int uid = Binder.getCallingUid();
+            if (uid == Process.SYSTEM_UID) {
+                return true;
+            }
             SparseBooleanArray uidList = mAccessoryPermissionMap.get(accessory);
             if (uidList == null) {
                 return false;
             }
-            return uidList.get(Binder.getCallingUid());
+            return uidList.get(uid);
         }
     }
 
diff --git a/services/java/com/android/server/wifi/README.txt b/services/java/com/android/server/wifi/README.txt
index c03bff5..39e1475 100644
--- a/services/java/com/android/server/wifi/README.txt
+++ b/services/java/com/android/server/wifi/README.txt
@@ -10,3 +10,10 @@
 
 WifiStateMachine: Tracks the various states on STA and AP connectivity and handles bring up and shut down.
 
+
+Feature description:
+
+Scan-only mode with Wi-Fi turned off:
+ - Setup wizard opts user into allowing scanning for improved location. We show no further dialogs in setup wizard since the user has just opted into the feature. This is the reason WifiService listens to DEVICE_PROVISIONED setting.
+ - Once the user has his device provisioned, turning off Wi-Fi from settings or from a third party app will show up a dialog reminding the user that scan mode will be on even though Wi-Fi is being turned off. The user has the choice to turn this notification off.
+ - In the scan mode, the device continues to allow scanning from any app with Wi-Fi turned off. This is done by disabling all networks and allowing only scans to be passed.
diff --git a/services/java/com/android/server/wifi/WifiService.java b/services/java/com/android/server/wifi/WifiService.java
index 9dde58f..f8d5d2e 100644
--- a/services/java/com/android/server/wifi/WifiService.java
+++ b/services/java/com/android/server/wifi/WifiService.java
@@ -58,6 +58,7 @@
 import java.net.Inet4Address;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
 
 import com.android.internal.R;
 import com.android.internal.app.IBatteryStats;
@@ -101,6 +102,9 @@
     private int mMulticastEnabled;
     private int mMulticastDisabled;
 
+    private AtomicBoolean mDeviceProvisioned = new AtomicBoolean();
+    private AtomicBoolean mNotifyScanMode = new AtomicBoolean();
+
     private final IBatteryStats mBatteryStats;
     private final AppOpsManager mAppOps;
 
@@ -245,6 +249,8 @@
         mWifiController.start();
 
         registerForScanModeChange();
+        registerForDeviceProvisionedChange();
+        registerForNotifyUserOnScanModeChange();
         mContext.registerReceiver(
                 new BroadcastReceiver() {
                     @Override
@@ -399,10 +405,8 @@
 
             /* Turning off Wi-Fi when scans are still available */
             if (!enable && isScanningAlwaysAvailable()) {
-                /* This can be changed by user in the app to not be notified again */
-                boolean notifyUser = (Settings.Global.getInt(mContext.getContentResolver(),
-                        Settings.Global.WIFI_NOTIFY_SCAN_ALWAYS_AVAILABLE, 1) == 1);
-                if (notifyUser) {
+                /* Notify if device is provisioned and user has not opted out of the notification */
+                if (mNotifyScanMode.get() && mDeviceProvisioned.get()) {
                     Intent intent = new Intent(WifiManager.ACTION_NOTIFY_SCAN_ALWAYS_AVAILABLE);
                     mContext.startActivityAsUser(intent, null, UserHandle.CURRENT);
                 }
@@ -875,6 +879,51 @@
                 false, contentObserver);
     }
 
+    private void getPersistedDeviceProvisioned() {
+        mDeviceProvisioned.set(Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.DEVICE_PROVISIONED, 0) != 0);
+    }
+
+    private void getPersistedNotifyScanMode() {
+        mNotifyScanMode.set(Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.WIFI_NOTIFY_SCAN_ALWAYS_AVAILABLE, 1) == 1);
+    }
+
+    /**
+     * Observes settings changes to notify the user when scan mode is active and
+     * Wi-Fi is turned off
+     */
+    private void registerForNotifyUserOnScanModeChange() {
+            ContentObserver contentObserver = new ContentObserver(null) {
+            @Override
+            public void onChange(boolean selfChange) {
+                getPersistedNotifyScanMode();
+            }
+        };
+
+        getPersistedNotifyScanMode();
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Global.getUriFor(Settings.Global.WIFI_NOTIFY_SCAN_ALWAYS_AVAILABLE),
+                false, contentObserver);
+    }
+
+    /*
+     * Observes settings changes device provisioned status
+     */
+    private void registerForDeviceProvisionedChange() {
+       ContentObserver contentObserver = new ContentObserver(null) {
+            @Override
+            public void onChange(boolean selfChange) {
+                getPersistedDeviceProvisioned();
+            }
+        };
+
+        getPersistedDeviceProvisioned();
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED),
+                false, contentObserver);
+    }
+
     private void registerForBroadcasts() {
         IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(Intent.ACTION_SCREEN_ON);
@@ -899,6 +948,8 @@
         pw.println("Stay-awake conditions: " +
                 Settings.Global.getInt(mContext.getContentResolver(),
                                        Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0));
+        pw.println("mDeviceProvisioned " + mDeviceProvisioned.get());
+        pw.println("mNotifyScanMode " + mNotifyScanMode.get());
         pw.println("mMulticastEnabled " + mMulticastEnabled);
         pw.println("mMulticastDisabled " + mMulticastDisabled);
         mWifiController.dump(fd, pw, args);
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 56f4de5..34052f3 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -76,6 +76,7 @@
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.Matrix;
 import android.graphics.PixelFormat;
 import android.graphics.Point;
@@ -282,6 +283,8 @@
     private static final String SYSTEM_SECURE = "ro.secure";
     private static final String SYSTEM_DEBUGGABLE = "ro.debuggable";
 
+    private static final int MAX_SCREENSHOT_RETRIES = 3;
+
     final private KeyguardDisableHandler mKeyguardDisableHandler;
 
     private final boolean mHeadless;
@@ -5277,134 +5280,191 @@
             throw new SecurityException("Requires READ_FRAME_BUFFER permission");
         }
 
-        Bitmap rawss;
+        Bitmap rawss = null;
 
         int maxLayer = 0;
         final Rect frame = new Rect();
 
-        float scale;
+        float scale = 0;
         int dw, dh;
-        int rot;
+        int rot = Surface.ROTATION_0;
 
-        synchronized(mWindowMap) {
-            long ident = Binder.clearCallingIdentity();
+        boolean screenshotReady;
+        int minLayer;
+        if (appToken == null) {
+            screenshotReady = true;
+            minLayer = 0;
+        } else {
+            screenshotReady = false;
+            minLayer = Integer.MAX_VALUE;
+        }
 
-            final DisplayContent displayContent = getDisplayContentLocked(displayId);
-            if (displayContent == null) {
-                return null;
+        int retryCount = 0;
+        WindowState appWin = null;
+
+        do {
+            if (retryCount++ > 0) {
+                try {
+                    Thread.sleep(100);
+                } catch (InterruptedException e) {
+                }
             }
-            final DisplayInfo displayInfo = displayContent.getDisplayInfo();
-            dw = displayInfo.logicalWidth;
-            dh = displayInfo.logicalHeight;
-
-            int aboveAppLayer = mPolicy.windowTypeToLayerLw(TYPE_APPLICATION)
-                    * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
-            aboveAppLayer += TYPE_LAYER_MULTIPLIER;
-
-            boolean isImeTarget = mInputMethodTarget != null
-                    && mInputMethodTarget.mAppToken != null
-                    && mInputMethodTarget.mAppToken.appToken != null
-                    && mInputMethodTarget.mAppToken.appToken.asBinder() == appToken;
-
-            // Figure out the part of the screen that is actually the app.
-            boolean including = false;
-            final WindowList windows = displayContent.getWindowList();
-            for (int i = windows.size() - 1; i >= 0; i--) {
-                WindowState ws = windows.get(i);
-                if (!ws.mHasSurface) {
-                    continue;
+            synchronized(mWindowMap) {
+                final DisplayContent displayContent = getDisplayContentLocked(displayId);
+                if (displayContent == null) {
+                    return null;
                 }
-                if (ws.mLayer >= aboveAppLayer) {
-                    continue;
-                }
-                // When we will skip windows: when we are not including
-                // ones behind a window we didn't skip, and we are actually
-                // taking a screenshot of a specific app.
-                if (!including && appToken != null) {
-                    // Also, we can possibly skip this window if it is not
-                    // an IME target or the application for the screenshot
-                    // is not the current IME target.
-                    if (!ws.mIsImWindow || !isImeTarget) {
-                        // And finally, this window is of no interest if it
-                        // is not associated with the screenshot app.
-                        if (ws.mAppToken == null || ws.mAppToken.token != appToken) {
-                            continue;
+                final DisplayInfo displayInfo = displayContent.getDisplayInfo();
+                dw = displayInfo.logicalWidth;
+                dh = displayInfo.logicalHeight;
+
+                int aboveAppLayer = mPolicy.windowTypeToLayerLw(TYPE_APPLICATION)
+                        * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
+                aboveAppLayer += TYPE_LAYER_MULTIPLIER;
+
+                boolean isImeTarget = mInputMethodTarget != null
+                        && mInputMethodTarget.mAppToken != null
+                        && mInputMethodTarget.mAppToken.appToken != null
+                        && mInputMethodTarget.mAppToken.appToken.asBinder() == appToken;
+
+                // Figure out the part of the screen that is actually the app.
+                boolean including = false;
+                appWin = null;
+                final WindowList windows = displayContent.getWindowList();
+                for (int i = windows.size() - 1; i >= 0; i--) {
+                    WindowState ws = windows.get(i);
+                    if (!ws.mHasSurface) {
+                        continue;
+                    }
+                    if (ws.mLayer >= aboveAppLayer) {
+                        continue;
+                    }
+                    // When we will skip windows: when we are not including
+                    // ones behind a window we didn't skip, and we are actually
+                    // taking a screenshot of a specific app.
+                    if (!including && appToken != null) {
+                        // Also, we can possibly skip this window if it is not
+                        // an IME target or the application for the screenshot
+                        // is not the current IME target.
+                        if (!ws.mIsImWindow || !isImeTarget) {
+                            // And finally, this window is of no interest if it
+                            // is not associated with the screenshot app.
+                            if (ws.mAppToken == null || ws.mAppToken.token != appToken) {
+                                continue;
+                            }
+                            appWin = ws;
+                        }
+                    }
+
+                    // We keep on including windows until we go past a full-screen
+                    // window.
+                    boolean fullscreen = ws.isFullscreen(dw, dh);
+                    including = !ws.mIsImWindow && !fullscreen;
+
+                    final WindowStateAnimator winAnim = ws.mWinAnimator;
+                    if (maxLayer < winAnim.mSurfaceLayer) {
+                        maxLayer = winAnim.mSurfaceLayer;
+                    }
+
+                    // Don't include wallpaper in bounds calculation
+                    if (!ws.mIsWallpaper) {
+                        final Rect wf = ws.mFrame;
+                        final Rect cr = ws.mContentInsets;
+                        int left = wf.left + cr.left;
+                        int top = wf.top + cr.top;
+                        int right = wf.right - cr.right;
+                        int bottom = wf.bottom - cr.bottom;
+                        frame.union(left, top, right, bottom);
+                    }
+
+                    if (ws.mAppToken != null && ws.mAppToken.token == appToken) {
+                        if (minLayer > ws.mWinAnimator.mSurfaceLayer) {
+                            minLayer = ws.mWinAnimator.mSurfaceLayer;
+                        }
+                        if (ws.isDisplayedLw()) {
+                            screenshotReady = true;
+                        }
+                        if (fullscreen) {
+                            // No point in continuing down through windows.
+                            break;
                         }
                     }
                 }
 
-                // We keep on including windows until we go past a full-screen
-                // window.
-                including = !ws.mIsImWindow && !ws.isFullscreen(dw, dh);
-
-                if (maxLayer < ws.mWinAnimator.mSurfaceLayer) {
-                    maxLayer = ws.mWinAnimator.mSurfaceLayer;
+                if (appToken != null && appWin == null) {
+                    // Can't find a window to snapshot.
+                    if (DEBUG_SCREENSHOT) Slog.i(TAG,
+                            "Screenshot: Couldn't find a surface matching " + appToken);
+                    return null;
+                }
+                if (!screenshotReady) {
+                    // Delay and hope that window gets drawn.
+                    if (DEBUG_SCREENSHOT) Slog.i(TAG, "Screenshot: No image ready for " + appToken
+                            + ", " + appWin + " drawState=" + appWin.mWinAnimator.mDrawState);
+                    continue;
                 }
 
-                // Don't include wallpaper in bounds calculation
-                if (!ws.mIsWallpaper) {
-                    final Rect wf = ws.mFrame;
-                    final Rect cr = ws.mContentInsets;
-                    int left = wf.left + cr.left;
-                    int top = wf.top + cr.top;
-                    int right = wf.right - cr.right;
-                    int bottom = wf.bottom - cr.bottom;
-                    frame.union(left, top, right, bottom);
+                // Constrain frame to the screen size.
+                frame.intersect(0, 0, dw, dh);
+
+                if (frame.isEmpty() || maxLayer == 0) {
+                    if (DEBUG_SCREENSHOT) Slog.i(TAG, "Screenshot of " + appToken
+                            + ": returning null frame=" + frame.toShortString() + " maxLayer="
+                            + maxLayer);
+                    return null;
                 }
-            }
-            Binder.restoreCallingIdentity(ident);
 
-            // Constrain frame to the screen size.
-            frame.intersect(0, 0, dw, dh);
+                // The screenshot API does not apply the current screen rotation.
+                rot = getDefaultDisplayContentLocked().getDisplay().getRotation();
+                int fw = frame.width();
+                int fh = frame.height();
 
-            if (frame.isEmpty() || maxLayer == 0) {
-                return null;
-            }
-
-            // The screenshot API does not apply the current screen rotation.
-            rot = getDefaultDisplayContentLocked().getDisplay().getRotation();
-            int fw = frame.width();
-            int fh = frame.height();
-
-            // Constrain thumbnail to smaller of screen width or height. Assumes aspect
-            // of thumbnail is the same as the screen (in landscape) or square.
-            float targetWidthScale = width / (float) fw;
-            float targetHeightScale = height / (float) fh;
-            if (dw <= dh) {
-                scale = targetWidthScale;
-                // If aspect of thumbnail is the same as the screen (in landscape),
-                // select the slightly larger value so we fill the entire bitmap
-                if (targetHeightScale > scale && (int) (targetHeightScale * fw) == width) {
-                    scale = targetHeightScale;
-                }
-            } else {
-                scale = targetHeightScale;
-                // If aspect of thumbnail is the same as the screen (in landscape),
-                // select the slightly larger value so we fill the entire bitmap
-                if (targetWidthScale > scale && (int) (targetWidthScale * fh) == height) {
+                // Constrain thumbnail to smaller of screen width or height. Assumes aspect
+                // of thumbnail is the same as the screen (in landscape) or square.
+                float targetWidthScale = width / (float) fw;
+                float targetHeightScale = height / (float) fh;
+                if (dw <= dh) {
                     scale = targetWidthScale;
+                    // If aspect of thumbnail is the same as the screen (in landscape),
+                    // select the slightly larger value so we fill the entire bitmap
+                    if (targetHeightScale > scale && (int) (targetHeightScale * fw) == width) {
+                        scale = targetHeightScale;
+                    }
+                } else {
+                    scale = targetHeightScale;
+                    // If aspect of thumbnail is the same as the screen (in landscape),
+                    // select the slightly larger value so we fill the entire bitmap
+                    if (targetWidthScale > scale && (int) (targetWidthScale * fh) == height) {
+                        scale = targetWidthScale;
+                    }
                 }
-            }
 
-            // The screen shot will contain the entire screen.
-            dw = (int)(dw*scale);
-            dh = (int)(dh*scale);
-            if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) {
-                int tmp = dw;
-                dw = dh;
-                dh = tmp;
-                rot = (rot == Surface.ROTATION_90) ? Surface.ROTATION_270 : Surface.ROTATION_90;
-            }
-            if (DEBUG_SCREENSHOT) {
-                Slog.i(TAG, "Screenshot: " + dw + "x" + dh + " from 0 to " + maxLayer);
-                for (int i = 0; i < windows.size(); i++) {
-                    WindowState win = windows.get(i);
-                    Slog.i(TAG, win + ": " + win.mLayer
-                            + " animLayer=" + win.mWinAnimator.mAnimLayer
-                            + " surfaceLayer=" + win.mWinAnimator.mSurfaceLayer);
+                // The screen shot will contain the entire screen.
+                dw = (int)(dw*scale);
+                dh = (int)(dh*scale);
+                if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) {
+                    int tmp = dw;
+                    dw = dh;
+                    dh = tmp;
+                    rot = (rot == Surface.ROTATION_90) ? Surface.ROTATION_270 : Surface.ROTATION_90;
                 }
+                if (DEBUG_SCREENSHOT) {
+                    Slog.i(TAG, "Screenshot: " + dw + "x" + dh + " from " + minLayer + " to "
+                            + maxLayer + " appToken=" + appToken);
+                    for (int i = 0; i < windows.size(); i++) {
+                        WindowState win = windows.get(i);
+                        Slog.i(TAG, win + ": " + win.mLayer
+                                + " animLayer=" + win.mWinAnimator.mAnimLayer
+                                + " surfaceLayer=" + win.mWinAnimator.mSurfaceLayer);
+                    }
+                }
+                rawss = SurfaceControl.screenshot(dw, dh, minLayer, maxLayer);
             }
-            rawss = SurfaceControl.screenshot(dw, dh, 0, maxLayer);
+        } while (!screenshotReady && retryCount <= MAX_SCREENSHOT_RETRIES);
+        if (DEBUG_SCREENSHOT && retryCount > MAX_SCREENSHOT_RETRIES) {
+            Slog.i(TAG, "Screenshot max retries " + retryCount + " of " + appToken + " appWin="
+                    + (appWin == null ? "null" : (appWin + " drawState="
+                            + appWin.mWinAnimator.mDrawState)));
         }
 
         if (rawss == null) {
@@ -5421,6 +5481,23 @@
         canvas.drawBitmap(rawss, matrix, null);
         canvas.setBitmap(null);
 
+        if (DEBUG_SCREENSHOT) {
+            // TEST IF IT's ALL BLACK
+            int[] buffer = new int[bm.getWidth() * bm.getHeight()];
+            bm.getPixels(buffer, 0, bm.getWidth(), 0, 0, bm.getWidth(), bm.getHeight());
+            boolean allBlack = true;
+            for (int i = 0; i < buffer.length; i++) {
+                if (buffer[i] != Color.BLACK) {
+                    allBlack = false;
+                    break;
+                }
+            }
+            if (allBlack) {
+                Slog.i(TAG, "Screenshot " + appWin + " was all black! mSurfaceLayer=" +
+                        (appWin != null ? appWin.mWinAnimator.mSurfaceLayer : "null"));
+            }
+        }
+
         rawss.recycle();
         return bm;
     }
@@ -7214,6 +7291,7 @@
         return false;
     }
 
+    @Override
     public void getInitialDisplaySize(int displayId, Point size) {
         synchronized (mWindowMap) {
             final DisplayContent displayContent = getDisplayContentLocked(displayId);
@@ -7227,6 +7305,19 @@
     }
 
     @Override
+    public void getBaseDisplaySize(int displayId, Point size) {
+        synchronized (mWindowMap) {
+            final DisplayContent displayContent = getDisplayContentLocked(displayId);
+            if (displayContent != null) {
+                synchronized(displayContent.mDisplaySizeLock) {
+                    size.x = displayContent.mBaseDisplayWidth;
+                    size.y = displayContent.mBaseDisplayHeight;
+                }
+            }
+        }
+    }
+
+    @Override
     public void setForcedDisplaySize(int displayId, int width, int height) {
         if (mContext.checkCallingOrSelfPermission(
                 android.Manifest.permission.WRITE_SECURE_SETTINGS) !=
@@ -7329,6 +7420,32 @@
     }
 
     @Override
+    public int getInitialDisplayDensity(int displayId) {
+        synchronized (mWindowMap) {
+            final DisplayContent displayContent = getDisplayContentLocked(displayId);
+            if (displayContent != null) {
+                synchronized(displayContent.mDisplaySizeLock) {
+                    return displayContent.mInitialDisplayDensity;
+                }
+            }
+        }
+        return -1;
+    }
+
+    @Override
+    public int getBaseDisplayDensity(int displayId) {
+        synchronized (mWindowMap) {
+            final DisplayContent displayContent = getDisplayContentLocked(displayId);
+            if (displayContent != null) {
+                synchronized(displayContent.mDisplaySizeLock) {
+                    return displayContent.mBaseDisplayDensity;
+                }
+            }
+        }
+        return -1;
+    }
+
+    @Override
     public void setForcedDisplayDensity(int displayId, int density) {
         if (mContext.checkCallingOrSelfPermission(
                 android.Manifest.permission.WRITE_SECURE_SETTINGS) !=
@@ -7408,6 +7525,7 @@
         performLayoutAndPlaceSurfacesLocked();
     }
 
+    @Override
     public void setOverscan(int displayId, int left, int top, int right, int bottom) {
         if (mContext.checkCallingOrSelfPermission(
                 android.Manifest.permission.WRITE_SECURE_SETTINGS) !=
diff --git a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
index 00c3a67..8ae3824 100644
--- a/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/accounts/AccountManagerServiceTest.java
@@ -75,12 +75,12 @@
         Account a12 = new Account("account1", "type2");
         Account a22 = new Account("account2", "type2");
         Account a32 = new Account("account3", "type2");
-        mAms.addAccount(a11, "p11", null);
-        mAms.addAccount(a12, "p12", null);
-        mAms.addAccount(a21, "p21", null);
-        mAms.addAccount(a22, "p22", null);
-        mAms.addAccount(a31, "p31", null);
-        mAms.addAccount(a32, "p32", null);
+        mAms.addAccountExplicitly(a11, "p11", null);
+        mAms.addAccountExplicitly(a12, "p12", null);
+        mAms.addAccountExplicitly(a21, "p21", null);
+        mAms.addAccountExplicitly(a22, "p22", null);
+        mAms.addAccountExplicitly(a31, "p31", null);
+        mAms.addAccountExplicitly(a32, "p32", null);
 
         Account[] accounts = mAms.getAccounts(null);
         Arrays.sort(accounts, new AccountSorter());
@@ -111,8 +111,8 @@
     public void testPasswords() throws Exception {
         Account a11 = new Account("account1", "type1");
         Account a12 = new Account("account1", "type2");
-        mAms.addAccount(a11, "p11", null);
-        mAms.addAccount(a12, "p12", null);
+        mAms.addAccountExplicitly(a11, "p11", null);
+        mAms.addAccountExplicitly(a12, "p12", null);
 
         assertEquals("p11", mAms.getPassword(a11));
         assertEquals("p12", mAms.getPassword(a12));
@@ -134,8 +134,8 @@
         u12.putString("a", "a_a12");
         u12.putString("b", "b_a12");
         u12.putString("c", "c_a12");
-        mAms.addAccount(a11, "p11", u11);
-        mAms.addAccount(a12, "p12", u12);
+        mAms.addAccountExplicitly(a11, "p11", u11);
+        mAms.addAccountExplicitly(a12, "p12", u12);
 
         assertEquals("a_a11", mAms.getUserData(a11, "a"));
         assertEquals("b_a11", mAms.getUserData(a11, "b"));
@@ -158,8 +158,8 @@
     public void testAuthtokens() throws Exception {
         Account a11 = new Account("account1", "type1");
         Account a12 = new Account("account1", "type2");
-        mAms.addAccount(a11, "p11", null);
-        mAms.addAccount(a12, "p12", null);
+        mAms.addAccountExplicitly(a11, "p11", null);
+        mAms.addAccountExplicitly(a12, "p12", null);
 
         mAms.setAuthToken(a11, "att1", "a11_att1");
         mAms.setAuthToken(a11, "att2", "a11_att2");
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 1ab5f45..a70ebf4 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -111,9 +111,9 @@
 
     private void writePackagesList() {
         writeFile(new File(getContext().getFilesDir(), "system/packages.list"),
-                ( "com.google.app1 11000 0 /data/data/com.google.app1"
-                + "com.google.app2 11001 0 /data/data/com.google.app2"
-                + "com.android.app3 11030 0 /data/data/com.android.app3")
+                ( "com.google.app1 11000 0 /data/data/com.google.app1 seinfo1"
+                + "com.google.app2 11001 0 /data/data/com.google.app2 seinfo2"
+                + "com.android.app3 11030 0 /data/data/com.android.app3 seinfo3")
                 .getBytes());
     }
 
@@ -176,8 +176,8 @@
 
         // Enable/Disable a package
         PackageSetting ps = settings.peekPackageLPr(PACKAGE_NAME_1);
-        ps.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, 0);
-        ps.setEnabled(COMPONENT_ENABLED_STATE_ENABLED, 1);
+        ps.setEnabled(COMPONENT_ENABLED_STATE_DISABLED, 0, null);
+        ps.setEnabled(COMPONENT_ENABLED_STATE_ENABLED, 1, null);
         assertEquals(COMPONENT_ENABLED_STATE_DISABLED, ps.getEnabled(0));
         assertEquals(COMPONENT_ENABLED_STATE_ENABLED, ps.getEnabled(1));
 
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 7ef1485..2e309be 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -66,8 +66,8 @@
                     && !user.isPrimary()) {
                 found = true;
                 Bundle restrictions = mUserManager.getUserRestrictions(user.getUserHandle());
-                assertTrue("New user should have ALLOW_CONFIG_WIFI =true by default",
-                        restrictions.getBoolean(UserManager.ALLOW_CONFIG_WIFI));
+                assertFalse("New user should have DISALLOW_CONFIG_WIFI =false by default",
+                        restrictions.getBoolean(UserManager.DISALLOW_CONFIG_WIFI));
             }
         }
         assertTrue(found);
@@ -147,13 +147,13 @@
         List<UserInfo> users = mUserManager.getUsers();
         if (users.size() > 1) {
             Bundle restrictions = new Bundle();
-            restrictions.putBoolean(UserManager.ALLOW_INSTALL_APPS, false);
-            restrictions.putBoolean(UserManager.ALLOW_CONFIG_WIFI, true);
+            restrictions.putBoolean(UserManager.DISALLOW_INSTALL_APPS, true);
+            restrictions.putBoolean(UserManager.DISALLOW_CONFIG_WIFI, false);
             mUserManager.setUserRestrictions(restrictions, new UserHandle(users.get(1).id));
             Bundle stored = mUserManager.getUserRestrictions(new UserHandle(users.get(1).id));
-            assertEquals(stored.getBoolean(UserManager.ALLOW_CONFIG_WIFI), true);
-            assertEquals(stored.getBoolean(UserManager.ALLOW_UNINSTALL_APPS), true);
-            assertEquals(stored.getBoolean(UserManager.ALLOW_INSTALL_APPS), false);
+            assertEquals(stored.getBoolean(UserManager.DISALLOW_CONFIG_WIFI), false);
+            assertEquals(stored.getBoolean(UserManager.DISALLOW_UNINSTALL_APPS), false);
+            assertEquals(stored.getBoolean(UserManager.DISALLOW_INSTALL_APPS), true);
         }
     }
 
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index 80c985f..9d556c0 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -30,10 +30,11 @@
      * DISCONNECTING: Connection.disconnect() has been called, but PDP
      *                context is not yet deactivated
      * FAILED: data connection fail for all apns settings
+     * RETRYING: data connection failed but we're going to retry.
      *
      * getDataConnectionState() maps State to DataState
      *      FAILED or IDLE : DISCONNECTED
-     *      CONNECTING or SCANNING: CONNECTING
+     *      RETRYING or CONNECTING or SCANNING: CONNECTING
      *      CONNECTED : CONNECTED or DISCONNECTING
      */
     public enum State {
@@ -42,7 +43,8 @@
         SCANNING,
         CONNECTED,
         DISCONNECTING,
-        FAILED
+        FAILED,
+        RETRYING
     }
 
     public enum Activity {
@@ -89,6 +91,8 @@
     public static final int CMD_SET_DEPENDENCY_MET = BASE + 31;
     public static final int CMD_SET_POLICY_DATA_ENABLE = BASE + 32;
     public static final int EVENT_ICC_CHANGED = BASE + 33;
+    public static final int EVENT_DISCONNECT_DC_RETRYING = BASE + 34;
+    public static final int EVENT_DATA_SETUP_COMPLETE_ERROR = BASE + 35;
 
     /***** Constants *****/
 
@@ -107,7 +111,4 @@
     public static final int ENABLED = 1;
 
     public static final String APN_TYPE_KEY = "apnType";
-    public static String ACTION_DATA_CONNECTION_TRACKER_MESSENGER =
-        "com.android.internal.telephony";
-    public static String EXTRA_MESSENGER = "EXTRA_MESSENGER";
 }
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index dffb617..62f6aff 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -15,6 +15,8 @@
  */
 package com.android.tests.applaunch;
 
+import android.accounts.Account;
+import android.accounts.AccountManager;
 import android.app.ActivityManager;
 import android.app.ActivityManager.ProcessErrorStateInfo;
 import android.app.ActivityManagerNative;
@@ -33,9 +35,11 @@
 import android.util.Log;
 
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * This test is intended to measure the time it takes for the apps to start.
@@ -52,6 +56,9 @@
     private static final String TAG = AppLaunch.class.getSimpleName();
     private static final String KEY_APPS = "apps";
     private static final String KEY_LAUNCH_ITERATIONS = "launch_iterations";
+    // optional parameter: comma separated list of required account types before proceeding
+    // with the app launch
+    private static final String KEY_REQUIRED_ACCOUNTS = "required_accounts";
     private static final int INITIAL_LAUNCH_IDLE_TIMEOUT = 7500; //7.5s to allow app to idle
     private static final int POST_LAUNCH_IDLE_TIMEOUT = 750; //750ms idle for non initial launches
     private static final int BETWEEN_LAUNCH_SLEEP_TIMEOUT = 2000; //2s between launching apps
@@ -63,6 +70,7 @@
     private IActivityManager mAm;
     private int mLaunchIterations = 10;
     private Bundle mResult = new Bundle();
+    private Set<String> mRequiredAccounts;
 
     public void testMeasureStartUpTime() throws RemoteException, NameNotFoundException {
         InstrumentationTestRunner instrumentation =
@@ -72,6 +80,7 @@
 
         createMappings();
         parseArgs(args);
+        checkAccountSignIn();
 
         // do initial app launch, without force stopping
         for (String app : mNameToResultKey.keySet()) {
@@ -140,6 +149,13 @@
             mNameToResultKey.put(parts[0], parts[1]);
             mNameToLaunchTime.put(parts[0], 0L);
         }
+        String requiredAccounts = args.getString(KEY_REQUIRED_ACCOUNTS);
+        if (requiredAccounts != null) {
+            mRequiredAccounts = new HashSet<String>();
+            for (String accountType : requiredAccounts.split(",")) {
+                mRequiredAccounts.add(accountType);
+            }
+        }
     }
 
     private void createMappings() {
@@ -204,6 +220,37 @@
         return result.thisTime;
     }
 
+    private void checkAccountSignIn() {
+        // ensure that the device has the required account types before starting test
+        // e.g. device must have a valid Google account sign in to measure a meaningful launch time
+        // for Gmail
+        if (mRequiredAccounts == null || mRequiredAccounts.isEmpty()) {
+            return;
+        }
+        final AccountManager am =
+                (AccountManager) getInstrumentation().getTargetContext().getSystemService(
+                        Context.ACCOUNT_SERVICE);
+        Account[] accounts = am.getAccounts();
+        // use set here in case device has multiple accounts of the same type
+        Set<String> foundAccounts = new HashSet<String>();
+        for (Account account : accounts) {
+            if (mRequiredAccounts.contains(account.type)) {
+                foundAccounts.add(account.type);
+            }
+        }
+        // check if account type matches, if not, fail test with message on what account types
+        // are missing
+        if (mRequiredAccounts.size() != foundAccounts.size()) {
+            mRequiredAccounts.removeAll(foundAccounts);
+            StringBuilder sb = new StringBuilder("Device missing these accounts:");
+            for (String account : mRequiredAccounts) {
+                sb.append(' ');
+                sb.append(account);
+            }
+            fail(sb.toString());
+        }
+    }
+
     private void closeApp(String appName, boolean forceStopApp) {
         Intent homeIntent = new Intent(Intent.ACTION_MAIN);
         homeIntent.addCategory(Intent.CATEGORY_HOME);
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 59ae1a1..6fbe32f 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import android.graphics.Point;
 import com.android.internal.view.IInputContext;
 import com.android.internal.view.IInputMethodClient;
 
@@ -348,11 +349,31 @@
     }
 
     @Override
+    public void getInitialDisplaySize(int displayId, Point size) {
+        // TODO Auto-generated method stub
+    }
+
+    @Override
+    public void getBaseDisplaySize(int displayId, Point size) {
+        // TODO Auto-generated method stub
+    }
+
+    @Override
     public void setForcedDisplaySize(int displayId, int arg0, int arg1) throws RemoteException {
         // TODO Auto-generated method stub
     }
 
     @Override
+    public int getInitialDisplayDensity(int displayId) {
+        return -1;
+    }
+
+    @Override
+    public int getBaseDisplayDensity(int displayId) {
+        return -1;
+    }
+
+    @Override
     public void setForcedDisplayDensity(int displayId, int density) throws RemoteException {
         // TODO Auto-generated method stub
     }
diff --git a/wifi/java/android/net/wifi/WifiConfigStore.java b/wifi/java/android/net/wifi/WifiConfigStore.java
index 2385c24..47f1fbf 100644
--- a/wifi/java/android/net/wifi/WifiConfigStore.java
+++ b/wifi/java/android/net/wifi/WifiConfigStore.java
@@ -1468,6 +1468,8 @@
         if (config.enterpriseConfig.migrateOldEapTlsNative(mWifiNative, netId)) {
             saveConfig();
         }
+
+        config.enterpriseConfig.migrateCerts(mKeyStore);
     }
 
     private String removeDoubleQuotes(String string) {
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index 95ffb1c..f73a13c 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -17,9 +17,9 @@
 
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.Process;
 import android.security.Credentials;
 import android.text.TextUtils;
-import android.util.Log;
 
 import com.android.org.bouncycastle.asn1.ASN1InputStream;
 import com.android.org.bouncycastle.asn1.ASN1Sequence;
@@ -481,7 +481,7 @@
         String caCertName = Credentials.CA_CERTIFICATE + name;
         if (mClientCertificate != null) {
             byte[] privKeyData = mClientPrivateKey.getEncoded();
-            ret = keyStore.importKey(privKeyName, privKeyData);
+            ret = keyStore.importKey(privKeyName, privKeyData, Process.WIFI_UID);
             if (ret == false) {
                 return ret;
             }
@@ -489,7 +489,7 @@
             ret = putCertInKeyStore(keyStore, userCertName, mClientCertificate);
             if (ret == false) {
                 // Remove private key installed
-                keyStore.delKey(privKeyName);
+                keyStore.delKey(privKeyName, Process.WIFI_UID);
                 return ret;
             }
         }
@@ -499,8 +499,8 @@
             if (ret == false) {
                 if (mClientCertificate != null) {
                     // Remove client key+cert
-                    keyStore.delKey(privKeyName);
-                    keyStore.delete(userCertName);
+                    keyStore.delKey(privKeyName, Process.WIFI_UID);
+                    keyStore.delete(userCertName, Process.WIFI_UID);
                 }
                 return ret;
             }
@@ -525,7 +525,7 @@
             Certificate cert) {
         try {
             byte[] certData = Credentials.convertToPem(cert);
-            return keyStore.put(name, certData);
+            return keyStore.put(name, certData, Process.WIFI_UID);
         } catch (IOException e1) {
             return false;
         } catch (CertificateException e2) {
@@ -537,14 +537,14 @@
         String client = getFieldValue(CLIENT_CERT_KEY, CLIENT_CERT_PREFIX);
         // a valid client certificate is configured
         if (!TextUtils.isEmpty(client)) {
-            keyStore.delKey(Credentials.USER_PRIVATE_KEY + client);
-            keyStore.delete(Credentials.USER_CERTIFICATE + client);
+            keyStore.delKey(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID);
+            keyStore.delete(Credentials.USER_CERTIFICATE + client, Process.WIFI_UID);
         }
 
         String ca = getFieldValue(CA_CERT_KEY, CA_CERT_PREFIX);
         // a valid ca certificate is configured
         if (!TextUtils.isEmpty(ca)) {
-            keyStore.delete(Credentials.CA_CERTIFICATE + ca);
+            keyStore.delete(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID);
         }
     }
 
@@ -625,6 +625,29 @@
         return true;
     }
 
+    /** Migrate certs from global pool to wifi UID if not already done */
+    void migrateCerts(android.security.KeyStore keyStore) {
+        String client = getFieldValue(CLIENT_CERT_KEY, CLIENT_CERT_PREFIX);
+        // a valid client certificate is configured
+        if (!TextUtils.isEmpty(client)) {
+            if (!keyStore.contains(Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID)) {
+                keyStore.duplicate(Credentials.USER_PRIVATE_KEY + client, -1,
+                        Credentials.USER_PRIVATE_KEY + client, Process.WIFI_UID);
+                keyStore.duplicate(Credentials.USER_CERTIFICATE + client, -1,
+                        Credentials.USER_CERTIFICATE + client, Process.WIFI_UID);
+            }
+        }
+
+        String ca = getFieldValue(CA_CERT_KEY, CA_CERT_PREFIX);
+        // a valid ca certificate is configured
+        if (!TextUtils.isEmpty(ca)) {
+            if (!keyStore.contains(Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID)) {
+                keyStore.duplicate(Credentials.CA_CERTIFICATE + ca, -1,
+                        Credentials.CA_CERTIFICATE + ca, Process.WIFI_UID);
+            }
+        }
+    }
+
     private String removeDoubleQuotes(String string) {
         int length = string.length();
         if ((length > 1) && (string.charAt(0) == '"')
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index 32cd2f6..f637e89 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -793,4 +793,14 @@
     public boolean p2pServDiscCancelReq(String id) {
         return doBooleanCommand("P2P_SERV_DISC_CANCEL_REQ " + id);
     }
+
+    /* Set the current mode of miracast operation.
+     *  0 = disabled
+     *  1 = operating as source
+     *  2 = operating as sink
+     */
+    public void setMiracastMode(int mode) {
+        // Note: optional feature on the driver. It is ok for this to fail.
+        doBooleanCommand("DRIVER MIRACAST " + mode);
+    }
 }
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index b57910f..c0a3bc1 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -1677,12 +1677,12 @@
         setNetworkDetailedState(DetailedState.DISCONNECTED);
         mWifiConfigStore.updateStatus(mLastNetworkId, DetailedState.DISCONNECTED);
 
-        /* send event to CM & network change broadcast */
-        sendNetworkStateChangeBroadcast(mLastBssid);
-
         /* Clear network properties */
         mLinkProperties.clear();
 
+        /* send event to CM & network change broadcast */
+        sendNetworkStateChangeBroadcast(mLastBssid);
+
         /* Clear IP settings if the network used DHCP */
         if (!mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
             mWifiConfigStore.clearLinkProperties(mLastNetworkId);
diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java
index 81d2e11..cf75381 100644
--- a/wifi/java/android/net/wifi/WifiStateTracker.java
+++ b/wifi/java/android/net/wifi/WifiStateTracker.java
@@ -27,6 +27,7 @@
 import android.net.NetworkStateTracker;
 import android.os.Handler;
 import android.os.Message;
+import android.os.Messenger;
 import android.util.Slog;
 
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -262,4 +263,9 @@
     public void removeStackedLink(LinkProperties link) {
         mLinkProperties.removeStackedLink(link);
     }
+
+    @Override
+    public void supplyMessenger(Messenger messenger) {
+        // not supported on this network
+    }
 }
diff --git a/wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl b/wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl
index 381a450..1c9c40d 100644
--- a/wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl
+++ b/wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl
@@ -26,5 +26,6 @@
 interface IWifiP2pManager
 {
     Messenger getMessenger();
+    void setMiracastMode(int mode);
 }
 
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index 2e80064..737ab91 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -1258,6 +1258,21 @@
         c.mAsyncChannel.sendMessage(REQUEST_PERSISTENT_GROUP_INFO, 0, c.putListener(listener));
     }
 
+    /** @hide */
+    public static final int MIRACAST_DISABLED = 0;
+    /** @hide */
+    public static final int MIRACAST_SOURCE   = 1;
+    /** @hide */
+    public static final int MIRACAST_SINK     = 2;
+    /** Internal use only @hide */
+    public void setMiracastMode(int mode) {
+        try {
+            mService.setMiracastMode(mode);
+        } catch(RemoteException e) {
+           // ignore
+        }
+    }
+
     /**
      * Get a reference to WifiP2pService handler. This is used to establish
      * an AsyncChannel communication with WifiService
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pService.java b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
index e6a1df1..447ddb0 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pService.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
@@ -164,6 +164,8 @@
     public static final int DISCONNECT_WIFI_REQUEST         =   BASE + 12;
     public static final int DISCONNECT_WIFI_RESPONSE        =   BASE + 13;
 
+    public static final int SET_MIRACAST_MODE               =   BASE + 14;
+
     private final boolean mP2pSupported;
 
     private WifiP2pDevice mThisDevice = new WifiP2pDevice();
@@ -310,6 +312,12 @@
                 "WifiP2pService");
     }
 
+    private void enforceConnectivityInternalPermission() {
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.CONNECTIVITY_INTERNAL,
+                "WifiP2pService");
+    }
+
     /**
      * Get a reference to handler. This is used by a client to establish
      * an AsyncChannel communication with WifiP2pService
@@ -320,6 +328,20 @@
         return new Messenger(mP2pStateMachine.getHandler());
     }
 
+    /** This is used to provide information to drivers to optimize performance depending
+     * on the current mode of operation.
+     * 0 - disabled
+     * 1 - source operation
+     * 2 - sink operation
+     *
+     * As an example, the driver could reduce the channel dwell time during scanning
+     * when acting as a source or sink to minimize impact on miracast.
+     */
+    public void setMiracastMode(int mode) {
+        enforceConnectivityInternalPermission();
+        mP2pStateMachine.sendMessage(SET_MIRACAST_MODE, mode);
+    }
+
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
@@ -572,6 +594,7 @@
                 case DhcpStateMachine.CMD_POST_DHCP_ACTION:
                 case DhcpStateMachine.CMD_ON_QUIT:
                 case WifiMonitor.P2P_PROV_DISC_FAILURE_EVENT:
+                case SET_MIRACAST_MODE:
                     break;
                 case WifiStateMachine.CMD_ENABLE_P2P:
                     // Enable is lazy and has no response
@@ -878,7 +901,7 @@
                         sendPeersChangedBroadcast();
                     }
                     break;
-               case WifiP2pManager.ADD_LOCAL_SERVICE:
+                case WifiP2pManager.ADD_LOCAL_SERVICE:
                     if (DBG) logd(getName() + " add service");
                     WifiP2pServiceInfo servInfo = (WifiP2pServiceInfo)message.obj;
                     if (addLocalService(message.replyTo, servInfo)) {
@@ -916,7 +939,7 @@
                     clearServiceRequests(message.replyTo);
                     replyToMessage(message, WifiP2pManager.CLEAR_SERVICE_REQUESTS_SUCCEEDED);
                     break;
-               case WifiMonitor.P2P_SERV_DISC_RESP_EVENT:
+                case WifiMonitor.P2P_SERV_DISC_RESP_EVENT:
                     if (DBG) logd(getName() + " receive service response");
                     List<WifiP2pServiceResponse> sdRespList =
                         (List<WifiP2pServiceResponse>) message.obj;
@@ -927,13 +950,16 @@
                         sendServiceResponse(resp);
                     }
                     break;
-               case WifiP2pManager.DELETE_PERSISTENT_GROUP:
+                case WifiP2pManager.DELETE_PERSISTENT_GROUP:
                    if (DBG) logd(getName() + " delete persistent group");
                    mGroups.remove(message.arg1);
                    replyToMessage(message, WifiP2pManager.DELETE_PERSISTENT_GROUP_SUCCEEDED);
                    break;
+                case SET_MIRACAST_MODE:
+                    mWifiNative.setMiracastMode(message.arg1);
+                    break;
                 default:
-                    return NOT_HANDLED;
+                   return NOT_HANDLED;
             }
             return HANDLED;
         }