resolve merge conflicts of edc625f to m-wireless-dev.

Change-Id: Ibbcd764505ccc38f9f61c96993d979a099f1cc6b
diff --git a/Android.mk b/Android.mk
index 3b1991c..8acd396 100644
--- a/Android.mk
+++ b/Android.mk
@@ -111,6 +111,7 @@
 	core/java/android/bluetooth/IBluetoothManagerCallback.aidl \
 	core/java/android/bluetooth/IBluetoothPbap.aidl \
 	core/java/android/bluetooth/IBluetoothMap.aidl \
+	core/java/android/bluetooth/IBluetoothSap.aidl \
 	core/java/android/bluetooth/IBluetoothStateChangeCallback.aidl \
 	core/java/android/bluetooth/IBluetoothHeadsetClient.aidl \
 	core/java/android/bluetooth/IBluetoothGatt.aidl \
@@ -205,6 +206,7 @@
 	core/java/android/os/IUpdateLock.aidl \
 	core/java/android/os/IUserManager.aidl \
 	core/java/android/os/IVibratorService.aidl \
+	core/java/android/service/carrier/ICarrierConfigService.aidl \
 	core/java/android/service/carrier/ICarrierMessagingCallback.aidl \
 	core/java/android/service/carrier/ICarrierMessagingService.aidl \
 	core/java/android/service/notification/INotificationListener.aidl \
@@ -373,19 +375,21 @@
 	telephony/java/com/android/ims/internal/IImsVideoCallCallback.aidl \
 	telephony/java/com/android/ims/internal/IImsVideoCallProvider.aidl \
 	telephony/java/com/android/ims/ImsConfigListener.aidl \
+	telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl \
+	telephony/java/com/android/internal/telephony/IMms.aidl \
+	telephony/java/com/android/internal/telephony/IOnSubscriptionsChangedListener.aidl \
 	telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl \
 	telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl \
+	telephony/java/com/android/internal/telephony/ISms.aidl \
+	telephony/java/com/android/internal/telephony/ISub.aidl \
 	telephony/java/com/android/internal/telephony/ITelephony.aidl \
 	telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl \
-	telephony/java/com/android/internal/telephony/IOnSubscriptionsChangedListener.aidl \
-	telephony/java/com/android/internal/telephony/ISms.aidl \
 	telephony/java/com/android/internal/telephony/IWapPushManager.aidl \
-	telephony/java/com/android/internal/telephony/ISub.aidl \
-	telephony/java/com/android/internal/telephony/IMms.aidl \
 	wifi/java/android/net/wifi/IWifiManager.aidl \
 	wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl \
 	wifi/java/android/net/wifi/IWifiScanner.aidl \
 	wifi/java/android/net/wifi/IRttManager.aidl \
+	wifi/java/android/net/wifi/passpoint/IWifiPasspointManager.aidl \
 	packages/services/PacProcessor/com/android/net/IProxyService.aidl \
 	packages/services/Proxy/com/android/net/IProxyCallback.aidl \
 	packages/services/Proxy/com/android/net/IProxyPortListener.aidl \
@@ -527,6 +531,7 @@
 	frameworks/base/core/java/android/view/textservice/SpellCheckerInfo.aidl \
 	frameworks/base/core/java/android/view/textservice/SentenceSuggestionsInfo.aidl \
 	frameworks/base/core/java/android/view/textservice/SuggestionsInfo.aidl \
+	frameworks/base/core/java/android/service/carrier/CarrierIdentifier.aidl \
 	frameworks/base/core/java/android/service/carrier/MessagePdu.aidl \
 	frameworks/base/core/java/android/service/notification/StatusBarNotification.aidl \
 	frameworks/base/core/java/android/speech/tts/Voice.aidl \
diff --git a/api/current.txt b/api/current.txt
index 35423fd..2bb55bc 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -20,6 +20,7 @@
     field public static final java.lang.String BATTERY_STATS = "android.permission.BATTERY_STATS";
     field public static final java.lang.String BIND_ACCESSIBILITY_SERVICE = "android.permission.BIND_ACCESSIBILITY_SERVICE";
     field public static final java.lang.String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET";
+    field public static final java.lang.String BIND_CARRIER_CONFIG_SERVICE = "android.permission.BIND_CARRIER_CONFIG_SERVICE";
     field public static final java.lang.String BIND_CARRIER_MESSAGING_SERVICE = "android.permission.BIND_CARRIER_MESSAGING_SERVICE";
     field public static final java.lang.String BIND_DEVICE_ADMIN = "android.permission.BIND_DEVICE_ADMIN";
     field public static final java.lang.String BIND_DREAM_SERVICE = "android.permission.BIND_DREAM_SERVICE";
@@ -6485,6 +6486,7 @@
     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 SAP = 10; // 0xa
     field public static final int STATE_CONNECTED = 2; // 0x2
     field public static final int STATE_CONNECTING = 1; // 0x1
     field public static final int STATE_DISCONNECTED = 0; // 0x0
@@ -6496,6 +6498,25 @@
     method public abstract void onServiceDisconnected(int);
   }
 
+  public final class BluetoothSap implements android.bluetooth.BluetoothProfile {
+    method public synchronized void close();
+    method public boolean connect(android.bluetooth.BluetoothDevice);
+    method public boolean disconnect(android.bluetooth.BluetoothDevice);
+    method public android.bluetooth.BluetoothDevice getClient();
+    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 int getPriority(android.bluetooth.BluetoothDevice);
+    method public int getState();
+    method public boolean isConnected(android.bluetooth.BluetoothDevice);
+    method public boolean setPriority(android.bluetooth.BluetoothDevice, int);
+    field public static final java.lang.String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED";
+    field public static final int RESULT_CANCELED = 2; // 0x2
+    field public static final int RESULT_FAILURE = 0; // 0x0
+    field public static final int RESULT_SUCCESS = 1; // 0x1
+    field public static final int STATE_ERROR = -1; // 0xffffffff
+  }
+
   public final class BluetoothServerSocket implements java.io.Closeable {
     method public android.bluetooth.BluetoothSocket accept() throws java.io.IOException;
     method public android.bluetooth.BluetoothSocket accept(int) throws java.io.IOException;
@@ -6505,10 +6526,16 @@
   public final class BluetoothSocket implements java.io.Closeable {
     method public void close() throws java.io.IOException;
     method public void connect() throws java.io.IOException;
+    method public int getConnectionType();
     method public java.io.InputStream getInputStream() throws java.io.IOException;
+    method public int getMaxReceivePacketSize();
+    method public int getMaxTransmitPacketSize();
     method public java.io.OutputStream getOutputStream() throws java.io.IOException;
     method public android.bluetooth.BluetoothDevice getRemoteDevice();
     method public boolean isConnected();
+    field public static final int TYPE_L2CAP = 3; // 0x3
+    field public static final int TYPE_RFCOMM = 1; // 0x1
+    field public static final int TYPE_SCO = 2; // 0x2
   }
 
 }
@@ -6662,6 +6689,7 @@
     field public static final int SCAN_MODE_BALANCED = 1; // 0x1
     field public static final int SCAN_MODE_LOW_LATENCY = 2; // 0x2
     field public static final int SCAN_MODE_LOW_POWER = 0; // 0x0
+    field public static final int SCAN_MODE_OPPORTUNISTIC = -1; // 0xffffffff
   }
 
   public static final class ScanSettings.Builder {
@@ -7236,6 +7264,7 @@
     field public static final java.lang.String BLUETOOTH_SERVICE = "bluetooth";
     field public static final java.lang.String CAMERA_SERVICE = "camera";
     field public static final java.lang.String CAPTIONING_SERVICE = "captioning";
+    field public static final java.lang.String CARRIER_CONFIG_SERVICE = "carrier_config";
     field public static final java.lang.String CLIPBOARD_SERVICE = "clipboard";
     field public static final java.lang.String CONNECTIVITY_SERVICE = "connectivity";
     field public static final java.lang.String CONSUMER_IR_SERVICE = "consumer_ir";
@@ -10785,6 +10814,8 @@
   public class ImageFormat {
     ctor public ImageFormat();
     method public static int getBitsPerPixel(int);
+    field public static final int DEPTH16 = 1144402265; // 0x44363159
+    field public static final int DEPTH_POINT_CLOUD = 257; // 0x101
     field public static final int JPEG = 256; // 0x100
     field public static final int NV16 = 16; // 0x10
     field public static final int NV21 = 17; // 0x11
@@ -12558,6 +12589,8 @@
     field public static final int CAMERA_DISABLED = 1; // 0x1
     field public static final int CAMERA_DISCONNECTED = 2; // 0x2
     field public static final int CAMERA_ERROR = 3; // 0x3
+    field public static final int CAMERA_IN_USE = 4; // 0x4
+    field public static final int MAX_CAMERAS_IN_USE = 5; // 0x5
   }
 
   public abstract class CameraCaptureSession implements java.lang.AutoCloseable {
@@ -12601,11 +12634,14 @@
     field public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Range<java.lang.Integer>[]> CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Range<java.lang.Integer>> CONTROL_AE_COMPENSATION_RANGE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Rational> CONTROL_AE_COMPENSATION_STEP;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Boolean> CONTROL_AE_LOCK_AVAILABLE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AF_AVAILABLE_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AVAILABLE_EFFECTS;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AVAILABLE_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AVAILABLE_SCENE_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AWB_AVAILABLE_MODES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Boolean> CONTROL_AWB_LOCK_AVAILABLE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> CONTROL_MAX_REGIONS_AE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> CONTROL_MAX_REGIONS_AF;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> CONTROL_MAX_REGIONS_AWB;
@@ -12622,8 +12658,13 @@
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> LENS_INFO_FOCUS_DISTANCE_CALIBRATION;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Float> LENS_INFO_HYPERFOCAL_DISTANCE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Float> LENS_INFO_MINIMUM_FOCUS_DISTANCE;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_INTRINSIC_CALIBRATION;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_POSE_ROTATION;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_POSE_TRANSLATION;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_RADIAL_DISTORTION;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> REQUEST_AVAILABLE_CAPABILITIES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_MAX_NUM_INPUT_STREAMS;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_MAX_NUM_OUTPUT_PROC;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_MAX_NUM_OUTPUT_PROC_STALLING;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_MAX_NUM_OUTPUT_RAW;
@@ -12643,6 +12684,7 @@
     field public static final android.hardware.camera2.CameraCharacteristics.Key<android.graphics.Rect> SENSOR_INFO_ACTIVE_ARRAY_SIZE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> SENSOR_INFO_COLOR_FILTER_ARRANGEMENT;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Range<java.lang.Long>> SENSOR_INFO_EXPOSURE_TIME_RANGE;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Boolean> SENSOR_INFO_LENS_SHADING_APPLIED;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Long> SENSOR_INFO_MAX_FRAME_DURATION;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.SizeF> SENSOR_INFO_PHYSICAL_SIZE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Size> SENSOR_INFO_PIXEL_ARRAY_SIZE;
@@ -12653,8 +12695,10 @@
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> SENSOR_ORIENTATION;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> SENSOR_REFERENCE_ILLUMINANT1;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Byte> SENSOR_REFERENCE_ILLUMINANT2;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> SHADING_AVAILABLE_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<boolean[]> STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key<byte[]> STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> STATISTICS_INFO_MAX_FACE_COUNT;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> SYNC_MAX_LATENCY;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> TONEMAP_AVAILABLE_TONE_MAP_MODES;
@@ -12698,7 +12742,10 @@
     method public java.lang.String[] getCameraIdList() throws android.hardware.camera2.CameraAccessException;
     method public void openCamera(java.lang.String, android.hardware.camera2.CameraDevice.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public void registerAvailabilityCallback(android.hardware.camera2.CameraManager.AvailabilityCallback, android.os.Handler);
+    method public void registerTorchCallback(android.hardware.camera2.CameraManager.TorchCallback, android.os.Handler);
+    method public void setTorchMode(java.lang.String, boolean) throws android.hardware.camera2.CameraAccessException;
     method public void unregisterAvailabilityCallback(android.hardware.camera2.CameraManager.AvailabilityCallback);
+    method public void unregisterTorchCallback(android.hardware.camera2.CameraManager.TorchCallback);
   }
 
   public static abstract class CameraManager.AvailabilityCallback {
@@ -12707,6 +12754,12 @@
     method public void onCameraUnavailable(java.lang.String);
   }
 
+  public static abstract class CameraManager.TorchCallback {
+    ctor public CameraManager.TorchCallback();
+    method public void onTorchModeChanged(java.lang.String, boolean);
+    method public void onTorchModeUnavailable(java.lang.String);
+  }
+
   public abstract class CameraMetadata {
     method public java.util.List<TKey> getKeys();
     field public static final int COLOR_CORRECTION_ABERRATION_MODE_FAST = 1; // 0x1
@@ -12724,6 +12777,7 @@
     field public static final int CONTROL_AE_MODE_ON_ALWAYS_FLASH = 3; // 0x3
     field public static final int CONTROL_AE_MODE_ON_AUTO_FLASH = 2; // 0x2
     field public static final int CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE = 4; // 0x4
+    field public static final int CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL = 2; // 0x2
     field public static final int CONTROL_AE_PRECAPTURE_TRIGGER_IDLE = 0; // 0x0
     field public static final int CONTROL_AE_PRECAPTURE_TRIGGER_START = 1; // 0x1
     field public static final int CONTROL_AE_STATE_CONVERGED = 2; // 0x2
@@ -12820,6 +12874,7 @@
     field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY = 2; // 0x2
     field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED = 0; // 0x0
     field public static final int LENS_FACING_BACK = 1; // 0x1
+    field public static final int LENS_FACING_EXTERNAL = 2; // 0x2
     field public static final int LENS_FACING_FRONT = 0; // 0x0
     field public static final int LENS_INFO_FOCUS_DISTANCE_CALIBRATION_APPROXIMATE = 1; // 0x1
     field public static final int LENS_INFO_FOCUS_DISTANCE_CALIBRATION_CALIBRATED = 2; // 0x2
@@ -12830,13 +12885,16 @@
     field public static final int LENS_STATE_STATIONARY = 0; // 0x0
     field public static final int NOISE_REDUCTION_MODE_FAST = 1; // 0x1
     field public static final int NOISE_REDUCTION_MODE_HIGH_QUALITY = 2; // 0x2
+    field public static final int NOISE_REDUCTION_MODE_MINIMAL = 3; // 0x3
     field public static final int NOISE_REDUCTION_MODE_OFF = 0; // 0x0
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE = 0; // 0x0
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE = 6; // 0x6
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING = 2; // 0x2
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR = 1; // 0x1
+    field public static final int REQUEST_AVAILABLE_CAPABILITIES_OPAQUE_REPROCESSING = 4; // 0x4
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_RAW = 3; // 0x3
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS = 5; // 0x5
+    field public static final int REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING = 7; // 0x7
     field public static final int SCALER_CROPPING_TYPE_CENTER_ONLY = 0; // 0x0
     field public static final int SCALER_CROPPING_TYPE_FREEFORM = 1; // 0x1
     field public static final int SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR = 3; // 0x3
@@ -12886,7 +12944,11 @@
     field public static final int SYNC_MAX_LATENCY_UNKNOWN = -1; // 0xffffffff
     field public static final int TONEMAP_MODE_CONTRAST_CURVE = 0; // 0x0
     field public static final int TONEMAP_MODE_FAST = 1; // 0x1
+    field public static final int TONEMAP_MODE_GAMMA_VALUE = 3; // 0x3
     field public static final int TONEMAP_MODE_HIGH_QUALITY = 2; // 0x2
+    field public static final int TONEMAP_MODE_PRESET_CURVE = 4; // 0x4
+    field public static final int TONEMAP_PRESET_CURVE_REC709 = 1; // 0x1
+    field public static final int TONEMAP_PRESET_CURVE_SRGB = 0; // 0x0
   }
 
   public class CaptureFailure {
@@ -12942,6 +13004,7 @@
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Float> LENS_FOCUS_DISTANCE;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> LENS_OPTICAL_STABILIZATION_MODE;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> NOISE_REDUCTION_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Float> REPROCESS_EFFECTIVE_EXPOSURE_FACTOR;
     field public static final android.hardware.camera2.CaptureRequest.Key<android.graphics.Rect> SCALER_CROP_REGION;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Long> SENSOR_EXPOSURE_TIME;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Long> SENSOR_FRAME_DURATION;
@@ -12953,7 +13016,9 @@
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Boolean> STATISTICS_HOT_PIXEL_MAP_MODE;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> STATISTICS_LENS_SHADING_MAP_MODE;
     field public static final android.hardware.camera2.CaptureRequest.Key<android.hardware.camera2.params.TonemapCurve> TONEMAP_CURVE;
+    field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Float> TONEMAP_GAMMA;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> TONEMAP_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> TONEMAP_PRESET_CURVE;
   }
 
   public static final class CaptureRequest.Builder {
@@ -13016,9 +13081,14 @@
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> LENS_FOCAL_LENGTH;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> LENS_FOCUS_DISTANCE;
     field public static final android.hardware.camera2.CaptureResult.Key<android.util.Pair<java.lang.Float, java.lang.Float>> LENS_FOCUS_RANGE;
+    field public static final android.hardware.camera2.CaptureResult.Key<float[]> LENS_INTRINSIC_CALIBRATION;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> LENS_OPTICAL_STABILIZATION_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key<float[]> LENS_POSE_ROTATION;
+    field public static final android.hardware.camera2.CaptureResult.Key<float[]> LENS_POSE_TRANSLATION;
+    field public static final android.hardware.camera2.CaptureResult.Key<float[]> LENS_RADIAL_DISTORTION;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> LENS_STATE;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> NOISE_REDUCTION_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> REPROCESS_EFFECTIVE_EXPOSURE_FACTOR;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Byte> REQUEST_PIPELINE_DEPTH;
     field public static final android.hardware.camera2.CaptureResult.Key<android.graphics.Rect> SCALER_CROP_REGION;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Long> SENSOR_EXPOSURE_TIME;
@@ -13040,7 +13110,9 @@
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> STATISTICS_LENS_SHADING_MAP_MODE;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> STATISTICS_SCENE_FLICKER;
     field public static final android.hardware.camera2.CaptureResult.Key<android.hardware.camera2.params.TonemapCurve> TONEMAP_CURVE;
+    field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> TONEMAP_GAMMA;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> TONEMAP_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> TONEMAP_PRESET_CURVE;
   }
 
   public static final class CaptureResult.Key {
@@ -16910,34 +16982,43 @@
 
   public class ConnectivityManager {
     method public void addDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener);
+    method public boolean bindProcessToNetwork(android.net.Network);
+    method public android.net.Network getActiveNetwork();
     method public android.net.NetworkInfo getActiveNetworkInfo();
-    method public android.net.NetworkInfo[] getAllNetworkInfo();
+    method public deprecated android.net.NetworkInfo[] getAllNetworkInfo();
     method public android.net.Network[] getAllNetworks();
     method public deprecated boolean getBackgroundDataSetting();
+    method public android.net.Network getBoundNetworkForProcess();
+    method public android.net.ProxyInfo getDefaultProxy();
     method public android.net.LinkProperties getLinkProperties(android.net.Network);
     method public android.net.NetworkCapabilities getNetworkCapabilities(android.net.Network);
-    method public android.net.NetworkInfo getNetworkInfo(int);
+    method public deprecated android.net.NetworkInfo getNetworkInfo(int);
     method public android.net.NetworkInfo getNetworkInfo(android.net.Network);
     method public deprecated int getNetworkPreference();
-    method public static android.net.Network getProcessDefaultNetwork();
+    method public static deprecated android.net.Network getProcessDefaultNetwork();
+    method public void ignoreNetworkWithCaptivePortal(android.net.Network, java.lang.String);
     method public boolean isActiveNetworkMetered();
     method public boolean isDefaultNetworkActive();
     method public static boolean isNetworkTypeValid(int);
     method public void registerNetworkCallback(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
     method public void releaseNetworkRequest(android.app.PendingIntent);
     method public void removeDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener);
-    method public void reportBadNetwork(android.net.Network);
+    method public deprecated void reportBadNetwork(android.net.Network);
+    method public void reportCaptivePortalDismissed(android.net.Network, java.lang.String);
+    method public void reportNetworkConnectivity(android.net.Network, boolean);
     method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
     method public void requestNetwork(android.net.NetworkRequest, android.app.PendingIntent);
     method public deprecated boolean requestRouteToHost(int, int);
     method public deprecated void setNetworkPreference(int);
-    method public static boolean setProcessDefaultNetwork(android.net.Network);
+    method public static deprecated boolean setProcessDefaultNetwork(android.net.Network);
     method public deprecated int startUsingNetworkFeature(int, java.lang.String);
     method public deprecated int stopUsingNetworkFeature(int, java.lang.String);
     method public void unregisterNetworkCallback(android.net.ConnectivityManager.NetworkCallback);
     field public static final deprecated java.lang.String ACTION_BACKGROUND_DATA_SETTING_CHANGED = "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED";
+    field public static final java.lang.String ACTION_CAPTIVE_PORTAL_SIGN_IN = "android.net.conn.CAPTIVE_PORTAL";
     field public static final java.lang.String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
     field public static final deprecated int DEFAULT_NETWORK_PREFERENCE = 1; // 0x1
+    field public static final java.lang.String EXTRA_CAPTIVE_PORTAL_TOKEN = "captivePortalToken";
     field public static final java.lang.String EXTRA_EXTRA_INFO = "extraInfo";
     field public static final java.lang.String EXTRA_IS_FAILOVER = "isFailover";
     field public static final java.lang.String EXTRA_NETWORK = "android.net.extra.NETWORK";
@@ -17098,8 +17179,10 @@
     method public int describeContents();
     method public java.net.InetAddress[] getAllByName(java.lang.String) throws java.net.UnknownHostException;
     method public java.net.InetAddress getByName(java.lang.String) throws java.net.UnknownHostException;
+    method public long getNetworkHandle();
     method public javax.net.SocketFactory getSocketFactory();
     method public java.net.URLConnection openConnection(java.net.URL) throws java.io.IOException;
+    method public java.net.URLConnection openConnection(java.net.URL, java.net.Proxy) throws java.io.IOException;
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.net.Network> CREATOR;
   }
@@ -17209,7 +17292,7 @@
     method public static final deprecated int getDefaultPort();
     method public static final deprecated java.lang.String getHost(android.content.Context);
     method public static final deprecated int getPort(android.content.Context);
-    field public static final java.lang.String EXTRA_PROXY_INFO = "android.intent.extra.PROXY_INFO";
+    field public static final deprecated java.lang.String EXTRA_PROXY_INFO = "android.intent.extra.PROXY_INFO";
     field public static final java.lang.String PROXY_CHANGE_ACTION = "android.intent.action.PROXY_CHANGE";
   }
 
@@ -17867,11 +17950,23 @@
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
     field public java.lang.String BSSID;
+    field public static final int CHANNEL_WIDTH_160MHZ = 3; // 0x3
+    field public static final int CHANNEL_WIDTH_20MHZ = 0; // 0x0
+    field public static final int CHANNEL_WIDTH_40MHZ = 1; // 0x1
+    field public static final int CHANNEL_WIDTH_80MHZ = 2; // 0x2
+    field public static final int CHANNEL_WIDTH_80MHZ_PLUS_MHZ = 4; // 0x4
     field public java.lang.String SSID;
     field public java.lang.String capabilities;
+    field public int centerFreq0;
+    field public int centerFreq1;
+    field public int channelWidth;
     field public int frequency;
+    field public boolean is80211McRTTResponder;
     field public int level;
+    field public java.lang.String operatorFriendlyName;
+    field public boolean passpointNetwork;
     field public long timestamp;
+    field public java.lang.String venueName;
   }
 
   public final class SupplicantState extends java.lang.Enum implements android.os.Parcelable {
@@ -17898,6 +17993,7 @@
   public class WifiConfiguration implements android.os.Parcelable {
     ctor public WifiConfiguration();
     method public int describeContents();
+    method public boolean isPasspoint();
     method public void writeToParcel(android.os.Parcel, int);
     field public java.lang.String BSSID;
     field public java.lang.String FQDN;
@@ -17907,11 +18003,15 @@
     field public java.util.BitSet allowedKeyManagement;
     field public java.util.BitSet allowedPairwiseCiphers;
     field public java.util.BitSet allowedProtocols;
+    field public int apBand;
+    field public int apChannel;
     field public android.net.wifi.WifiEnterpriseConfig enterpriseConfig;
     field public boolean hiddenSSID;
     field public int networkId;
     field public java.lang.String preSharedKey;
     field public int priority;
+    field public java.lang.String providerFriendlyName;
+    field public java.util.HashSet<java.lang.Long> roamingConsortiumIds;
     field public int status;
     field public java.lang.String[] wepKeys;
     field public int wepTxKeyIndex;
@@ -17969,6 +18069,7 @@
     ctor public WifiEnterpriseConfig();
     ctor public WifiEnterpriseConfig(android.net.wifi.WifiEnterpriseConfig);
     method public int describeContents();
+    method public java.lang.String getAltSubjectMatch();
     method public java.lang.String getAnonymousIdentity();
     method public java.security.cert.X509Certificate getCaCertificate();
     method public java.security.cert.X509Certificate getClientCertificate();
@@ -17976,7 +18077,10 @@
     method public java.lang.String getIdentity();
     method public java.lang.String getPassword();
     method public int getPhase2Method();
-    method public java.lang.String getSubjectMatch();
+    method public java.lang.String getPlmn();
+    method public java.lang.String getRealm();
+    method public deprecated java.lang.String getSubjectMatch();
+    method public void setAltSubjectMatch(java.lang.String);
     method public void setAnonymousIdentity(java.lang.String);
     method public void setCaCertificate(java.security.cert.X509Certificate);
     method public void setClientKeyEntry(java.security.PrivateKey, java.security.cert.X509Certificate);
@@ -17984,13 +18088,16 @@
     method public void setIdentity(java.lang.String);
     method public void setPassword(java.lang.String);
     method public void setPhase2Method(int);
-    method public void setSubjectMatch(java.lang.String);
+    method public void setPlmn(java.lang.String);
+    method public void setRealm(java.lang.String);
+    method public deprecated void setSubjectMatch(java.lang.String);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.net.wifi.WifiEnterpriseConfig> CREATOR;
   }
 
   public static final class WifiEnterpriseConfig.Eap {
     field public static final int AKA = 5; // 0x5
+    field public static final int AKA_PRIME = 6; // 0x6
     field public static final int NONE = -1; // 0xffffffff
     field public static final int PEAP = 0; // 0x0
     field public static final int PWD = 3; // 0x3
@@ -25269,6 +25376,7 @@
     field public static final java.lang.String DEBUG_APP = "debug_app";
     field public static final java.lang.String DEVELOPMENT_SETTINGS_ENABLED = "development_settings_enabled";
     field public static final java.lang.String DEVICE_PROVISIONED = "device_provisioned";
+    field public static final java.lang.String HIDE_CARRIER_NETWORK_SETTINGS = "hide_carrier_network_settings";
     field public static final java.lang.String HTTP_PROXY = "http_proxy";
     field public static final deprecated java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
     field public static final java.lang.String MODE_RINGER = "mode_ringer";
@@ -25435,6 +25543,7 @@
     field public static final android.net.Uri DEFAULT_RINGTONE_URI;
     field public static final deprecated java.lang.String DEVICE_PROVISIONED = "device_provisioned";
     field public static final deprecated java.lang.String DIM_SCREEN = "dim_screen";
+    field public static final java.lang.String DTMF_TONE_TYPE_WHEN_DIALING = "dtmf_tone_type";
     field public static final java.lang.String DTMF_TONE_WHEN_DIALING = "dtmf_tone";
     field public static final java.lang.String END_BUTTON_BEHAVIOR = "end_button_behavior";
     field public static final java.lang.String FONT_SCALE = "font_scale";
@@ -25483,6 +25592,7 @@
     field public static final java.lang.String USER_ROTATION = "user_rotation";
     field public static final deprecated java.lang.String USE_GOOGLE_MAIL = "use_google_mail";
     field public static final java.lang.String VIBRATE_ON = "vibrate_on";
+    field public static final java.lang.String VIBRATE_WHEN_RINGING = "vibrate_when_ringing";
     field public static final java.lang.String VOLUME_ALARM = "volume_alarm";
     field public static final java.lang.String VOLUME_BLUETOOTH_SCO = "volume_bluetooth_sco";
     field public static final java.lang.String VOLUME_MUSIC = "volume_music";
@@ -25876,6 +25986,8 @@
     field public static final int NOTIFICATION_CHANNEL_STATE_MESSAGE_WAITING = 2; // 0x2
     field public static final int NOTIFICATION_CHANNEL_STATE_NO_CONNECTION = 1; // 0x1
     field public static final int NOTIFICATION_CHANNEL_STATE_OK = 0; // 0x0
+    field public static final java.lang.String PHONE_ACCOUNT_COMPONENT_NAME = "phone_account_component_name";
+    field public static final java.lang.String PHONE_ACCOUNT_ID = "phone_account_id";
     field public static final java.lang.String SETTINGS_URI = "settings_uri";
     field public static final java.lang.String SOURCE_PACKAGE = "source_package";
     field public static final java.lang.String VOICEMAIL_ACCESS_URI = "voicemail_access_uri";
@@ -25885,6 +25997,8 @@
     method public static android.net.Uri buildSourceUri(java.lang.String);
     field public static final android.net.Uri CONTENT_URI;
     field public static final java.lang.String DATE = "date";
+    field public static final java.lang.String DELETED = "deleted";
+    field public static final java.lang.String DIRTY = "dirty";
     field public static final java.lang.String DIR_TYPE = "vnd.android.cursor.dir/voicemails";
     field public static final java.lang.String DURATION = "duration";
     field public static final java.lang.String HAS_CONTENT = "has_content";
@@ -25892,6 +26006,8 @@
     field public static final java.lang.String ITEM_TYPE = "vnd.android.cursor.item/voicemail";
     field public static final java.lang.String MIME_TYPE = "mime_type";
     field public static final java.lang.String NUMBER = "number";
+    field public static final java.lang.String PHONE_ACCOUNT_COMPONENT_NAME = "subscription_component_name";
+    field public static final java.lang.String PHONE_ACCOUNT_ID = "subscription_id";
     field public static final java.lang.String SOURCE_DATA = "source_data";
     field public static final java.lang.String SOURCE_PACKAGE = "source_package";
     field public static final java.lang.String TRANSCRIPTION = "transcription";
@@ -26909,6 +27025,26 @@
 
 package android.service.carrier {
 
+  public abstract class CarrierConfigService extends android.app.Service {
+    ctor public CarrierConfigService();
+    method public final android.os.IBinder onBind(android.content.Intent);
+    method public abstract android.os.Bundle onLoadConfig(android.service.carrier.CarrierIdentifier);
+    field public static final java.lang.String SERVICE_INTERFACE = "android.service.carrier.CarrierConfigService";
+  }
+
+  public class CarrierIdentifier implements android.os.Parcelable {
+    ctor public CarrierIdentifier(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
+    method public int describeContents();
+    method public java.lang.String getGid1();
+    method public java.lang.String getGid2();
+    method public java.lang.String getImsi();
+    method public java.lang.String getMcc();
+    method public java.lang.String getMnc();
+    method public java.lang.String getSpn();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.carrier.CarrierIdentifier> CREATOR;
+  }
+
   public abstract class CarrierMessagingService extends android.app.Service {
     ctor public CarrierMessagingService();
     method public android.os.IBinder onBind(android.content.Intent);
@@ -28166,17 +28302,573 @@
 
 package android.telecom {
 
+  public final class AudioState implements android.os.Parcelable {
+    ctor public AudioState(boolean, int, int);
+    ctor public AudioState(android.telecom.AudioState);
+    method public static java.lang.String audioRouteToString(int);
+    method public int describeContents();
+    method public int getRoute();
+    method public int getSupportedRouteMask();
+    method public boolean isMuted();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telecom.AudioState> CREATOR;
+    field public static final int ROUTE_BLUETOOTH = 2; // 0x2
+    field public static final int ROUTE_EARPIECE = 1; // 0x1
+    field public static final int ROUTE_SPEAKER = 8; // 0x8
+    field public static final int ROUTE_WIRED_HEADSET = 4; // 0x4
+    field public static final int ROUTE_WIRED_OR_EARPIECE = 5; // 0x5
+  }
+
+  public final class Call {
+    method public void addListener(android.telecom.Call.Listener);
+    method public void answer(int);
+    method public void conference(android.telecom.Call);
+    method public void disconnect();
+    method public java.util.List<java.lang.String> getCannedTextResponses();
+    method public java.util.List<android.telecom.Call> getChildren();
+    method public java.util.List<android.telecom.Call> getConferenceableCalls();
+    method public android.telecom.Call.Details getDetails();
+    method public android.telecom.Call getParent();
+    method public java.lang.String getRemainingPostDialSequence();
+    method public int getState();
+    method public android.telecom.InCallService.VideoCall getVideoCall();
+    method public void hold();
+    method public void mergeConference();
+    method public void phoneAccountSelected(android.telecom.PhoneAccountHandle, boolean);
+    method public void playDtmfTone(char);
+    method public void postDialContinue(boolean);
+    method public void reject(boolean, java.lang.String);
+    method public void removeListener(android.telecom.Call.Listener);
+    method public void splitFromConference();
+    method public void stopDtmfTone();
+    method public void swapConference();
+    method public void unhold();
+    field public static final java.lang.String AVAILABLE_PHONE_ACCOUNTS = "selectPhoneAccountAccounts";
+    field public static final int STATE_ACTIVE = 4; // 0x4
+    field public static final int STATE_CONNECTING = 9; // 0x9
+    field public static final int STATE_DIALING = 1; // 0x1
+    field public static final int STATE_DISCONNECTED = 7; // 0x7
+    field public static final int STATE_DISCONNECTING = 10; // 0xa
+    field public static final int STATE_HOLDING = 3; // 0x3
+    field public static final int STATE_NEW = 0; // 0x0
+    field public static final int STATE_PRE_DIAL_WAIT = 8; // 0x8
+    field public static final int STATE_RINGING = 2; // 0x2
+  }
+
+  public static class Call.Details {
+    method public static boolean can(int, int);
+    method public boolean can(int);
+    method public static java.lang.String capabilitiesToString(int);
+    method public android.telecom.PhoneAccountHandle getAccountHandle();
+    method public int getCallCapabilities();
+    method public int getCallProperties();
+    method public java.lang.String getCallerDisplayName();
+    method public int getCallerDisplayNamePresentation();
+    method public final long getConnectTimeMillis();
+    method public android.telecom.DisconnectCause getDisconnectCause();
+    method public android.os.Bundle getExtras();
+    method public android.telecom.GatewayInfo getGatewayInfo();
+    method public android.net.Uri getHandle();
+    method public int getHandlePresentation();
+    method public android.telecom.StatusHints getStatusHints();
+    method public int getVideoState();
+    field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000
+    field public static final int CAPABILITY_GENERIC_CONFERENCE = 16384; // 0x4000
+    field public static final int CAPABILITY_HIGH_DEF_AUDIO = 32768; // 0x8000
+    field public static final int CAPABILITY_HOLD = 1; // 0x1
+    field public static final int CAPABILITY_MANAGE_CONFERENCE = 128; // 0x80
+    field public static final int CAPABILITY_MERGE_CONFERENCE = 4; // 0x4
+    field public static final int CAPABILITY_MUTE = 64; // 0x40
+    field public static final int CAPABILITY_RESPOND_VIA_TEXT = 32; // 0x20
+    field public static final int CAPABILITY_SEPARATE_FROM_CONFERENCE = 4096; // 0x1000
+    field public static final int CAPABILITY_SHOW_CALLBACK_NUMBER = 131072; // 0x20000
+    field public static final int CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL = 768; // 0x300
+    field public static final int CAPABILITY_SUPPORTS_VT_LOCAL_RX = 256; // 0x100
+    field public static final int CAPABILITY_SUPPORTS_VT_LOCAL_TX = 512; // 0x200
+    field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL = 3072; // 0xc00
+    field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_RX = 1024; // 0x400
+    field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 2048; // 0x800
+    field public static final int CAPABILITY_SUPPORT_HOLD = 2; // 0x2
+    field public static final int CAPABILITY_SWAP_CONFERENCE = 8; // 0x8
+    field public static final int CAPABILITY_WIFI = 65536; // 0x10000
+  }
+
+  public static abstract class Call.Listener {
+    ctor public Call.Listener();
+    method public void onCallDestroyed(android.telecom.Call);
+    method public void onCannedTextResponsesLoaded(android.telecom.Call, java.util.List<java.lang.String>);
+    method public void onChildrenChanged(android.telecom.Call, java.util.List<android.telecom.Call>);
+    method public void onConferenceableCallsChanged(android.telecom.Call, java.util.List<android.telecom.Call>);
+    method public void onDetailsChanged(android.telecom.Call, android.telecom.Call.Details);
+    method public void onParentChanged(android.telecom.Call, android.telecom.Call);
+    method public void onPostDialWait(android.telecom.Call, java.lang.String);
+    method public void onStateChanged(android.telecom.Call, int);
+    method public void onVideoCallChanged(android.telecom.Call, android.telecom.InCallService.VideoCall);
+  }
+
+  public class CallProperties {
+    ctor public CallProperties();
+    field public static final int CONFERENCE = 1; // 0x1
+  }
+
+  public final class CallState {
+    method public static java.lang.String toString(int);
+    field public static final int ABORTED = 8; // 0x8
+    field public static final int ACTIVE = 5; // 0x5
+    field public static final int CONNECTING = 1; // 0x1
+    field public static final int DIALING = 3; // 0x3
+    field public static final int DISCONNECTED = 7; // 0x7
+    field public static final int DISCONNECTING = 9; // 0x9
+    field public static final int NEW = 0; // 0x0
+    field public static final int ON_HOLD = 6; // 0x6
+    field public static final int PRE_DIAL_WAIT = 2; // 0x2
+    field public static final int RINGING = 4; // 0x4
+  }
+
+  public final class CameraCapabilities implements android.os.Parcelable {
+    ctor public CameraCapabilities(int, int);
+    method public int describeContents();
+    method public int getHeight();
+    method public int getWidth();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telecom.CameraCapabilities> CREATOR;
+  }
+
+  public abstract class Conference implements android.telecom.IConferenceable {
+    ctor public Conference(android.telecom.PhoneAccountHandle);
+    method public final boolean addConnection(android.telecom.Connection);
+    method public final void destroy();
+    method public final android.telecom.AudioState getAudioState();
+    method public final java.util.List<android.telecom.Connection> getConferenceableConnections();
+    method public final long getConnectTimeMillis();
+    method public final int getConnectionCapabilities();
+    method public final java.util.List<android.telecom.Connection> getConnections();
+    method public final android.telecom.DisconnectCause getDisconnectCause();
+    method public final android.telecom.PhoneAccountHandle getPhoneAccountHandle();
+    method public android.telecom.Connection getPrimaryConnection();
+    method public final int getState();
+    method public void onAudioStateChanged(android.telecom.AudioState);
+    method public void onConnectionAdded(android.telecom.Connection);
+    method public void onDisconnect();
+    method public void onHold();
+    method public void onMerge(android.telecom.Connection);
+    method public void onMerge();
+    method public void onPlayDtmfTone(char);
+    method public void onSeparate(android.telecom.Connection);
+    method public void onStopDtmfTone();
+    method public void onSwap();
+    method public void onUnhold();
+    method public final void removeConnection(android.telecom.Connection);
+    method public final void setActive();
+    method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
+    method public void setConnectTimeMillis(long);
+    method public final void setConnectionCapabilities(int);
+    method public final void setDisconnected(android.telecom.DisconnectCause);
+    method public final void setOnHold();
+    field public static final long CONNECT_TIME_NOT_SPECIFIED = 0L; // 0x0L
+  }
+
+  public abstract class Connection implements android.telecom.IConferenceable {
+    ctor public Connection();
+    method public static java.lang.String capabilitiesToString(int);
+    method public static android.telecom.Connection createCanceledConnection();
+    method public static android.telecom.Connection createFailedConnection(android.telecom.DisconnectCause);
+    method public final void destroy();
+    method public final android.net.Uri getAddress();
+    method public final int getAddressPresentation();
+    method public final boolean getAudioModeIsVoip();
+    method public final android.telecom.AudioState getAudioState();
+    method public final java.lang.String getCallerDisplayName();
+    method public final int getCallerDisplayNamePresentation();
+    method public final android.telecom.Conference getConference();
+    method public final java.util.List<android.telecom.IConferenceable> getConferenceables();
+    method public final int getConnectionCapabilities();
+    method public final android.telecom.DisconnectCause getDisconnectCause();
+    method public final int getState();
+    method public final android.telecom.StatusHints getStatusHints();
+    method public final android.telecom.Connection.VideoProvider getVideoProvider();
+    method public final boolean isRingbackRequested();
+    method public void onAbort();
+    method public void onAnswer();
+    method public void onAudioStateChanged(android.telecom.AudioState);
+    method public void onDisconnect();
+    method public void onHold();
+    method public void onPlayDtmfTone(char);
+    method public void onPostDialContinue(boolean);
+    method public void onReject();
+    method public void onSeparate();
+    method public void onStateChanged(int);
+    method public void onStopDtmfTone();
+    method public void onUnhold();
+    method public final void setActive();
+    method public final void setAddress(android.net.Uri, int);
+    method public final void setAudioModeIsVoip(boolean);
+    method public final void setCallerDisplayName(java.lang.String, int);
+    method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
+    method public final void setConferenceables(java.util.List<android.telecom.IConferenceable>);
+    method public final void setConnectionCapabilities(int);
+    method public final void setConnectionService(android.telecom.ConnectionService);
+    method public final void setDialing();
+    method public final void setDisconnected(android.telecom.DisconnectCause);
+    method public final void setInitialized();
+    method public final void setInitializing();
+    method public final void setOnHold();
+    method public final void setPostDialWait(java.lang.String);
+    method public final void setRingbackRequested(boolean);
+    method public final void setRinging();
+    method public final void setStatusHints(android.telecom.StatusHints);
+    method public static java.lang.String stateToString(int);
+    field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000
+    field public static final int CAPABILITY_HOLD = 1; // 0x1
+    field public static final int CAPABILITY_MANAGE_CONFERENCE = 128; // 0x80
+    field public static final int CAPABILITY_MERGE_CONFERENCE = 4; // 0x4
+    field public static final int CAPABILITY_MUTE = 64; // 0x40
+    field public static final int CAPABILITY_RESPOND_VIA_TEXT = 32; // 0x20
+    field public static final int CAPABILITY_SEPARATE_FROM_CONFERENCE = 4096; // 0x1000
+    field public static final int CAPABILITY_SUPPORT_HOLD = 2; // 0x2
+    field public static final int CAPABILITY_SWAP_CONFERENCE = 8; // 0x8
+    field public static final int STATE_ACTIVE = 4; // 0x4
+    field public static final int STATE_DIALING = 3; // 0x3
+    field public static final int STATE_DISCONNECTED = 6; // 0x6
+    field public static final int STATE_HOLDING = 5; // 0x5
+    field public static final int STATE_INITIALIZING = 0; // 0x0
+    field public static final int STATE_NEW = 1; // 0x1
+    field public static final int STATE_RINGING = 2; // 0x2
+  }
+
+  public static abstract class Connection.VideoProvider {
+    ctor public Connection.VideoProvider();
+    method public void changeCallDataUsage(long);
+    method public void changeCameraCapabilities(android.telecom.CameraCapabilities);
+    method public void changePeerDimensions(int, int);
+    method public void changeVideoQuality(int);
+    method public void handleCallSessionEvent(int);
+    method public abstract void onRequestCameraCapabilities();
+    method public abstract void onRequestConnectionDataUsage();
+    method public abstract void onSendSessionModifyRequest(android.telecom.VideoProfile);
+    method public abstract void onSendSessionModifyResponse(android.telecom.VideoProfile);
+    method public abstract void onSetCamera(java.lang.String);
+    method public abstract void onSetDeviceOrientation(int);
+    method public abstract void onSetDisplaySurface(android.view.Surface);
+    method public abstract void onSetPauseImage(java.lang.String);
+    method public abstract void onSetPreviewSurface(android.view.Surface);
+    method public abstract void onSetZoom(float);
+    method public void receiveSessionModifyRequest(android.telecom.VideoProfile);
+    method public void receiveSessionModifyResponse(int, android.telecom.VideoProfile, android.telecom.VideoProfile);
+    field public static final int SESSION_EVENT_CAMERA_FAILURE = 5; // 0x5
+    field public static final int SESSION_EVENT_CAMERA_READY = 6; // 0x6
+    field public static final int SESSION_EVENT_RX_PAUSE = 1; // 0x1
+    field public static final int SESSION_EVENT_RX_RESUME = 2; // 0x2
+    field public static final int SESSION_EVENT_TX_START = 3; // 0x3
+    field public static final int SESSION_EVENT_TX_STOP = 4; // 0x4
+    field public static final int SESSION_MODIFY_REQUEST_FAIL = 2; // 0x2
+    field public static final int SESSION_MODIFY_REQUEST_INVALID = 3; // 0x3
+    field public static final int SESSION_MODIFY_REQUEST_REJECTED_BY_REMOTE = 5; // 0x5
+    field public static final int SESSION_MODIFY_REQUEST_SUCCESS = 1; // 0x1
+    field public static final int SESSION_MODIFY_REQUEST_TIMED_OUT = 4; // 0x4
+  }
+
+  public final class ConnectionRequest implements android.os.Parcelable {
+    ctor public ConnectionRequest(android.telecom.PhoneAccountHandle, android.net.Uri, android.os.Bundle);
+    method public int describeContents();
+    method public android.telecom.PhoneAccountHandle getAccountHandle();
+    method public android.net.Uri getAddress();
+    method public android.os.Bundle getExtras();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telecom.ConnectionRequest> CREATOR;
+  }
+
+  public abstract class ConnectionService extends android.app.Service {
+    ctor public ConnectionService();
+    method public final void addConference(android.telecom.Conference);
+    method public final void addExistingConnection(android.telecom.PhoneAccountHandle, android.telecom.Connection);
+    method public final void conferenceRemoteConnections(android.telecom.RemoteConnection, android.telecom.RemoteConnection);
+    method public final android.telecom.RemoteConnection createRemoteIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+    method public final android.telecom.RemoteConnection createRemoteOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+    method public final java.util.Collection<android.telecom.Connection> getAllConnections();
+    method public final android.os.IBinder onBind(android.content.Intent);
+    method public void onConference(android.telecom.Connection, android.telecom.Connection);
+    method public android.telecom.Connection onCreateIncomingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+    method public android.telecom.Connection onCreateOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
+    method public void onRemoteConferenceAdded(android.telecom.RemoteConference);
+    method public void onRemoteExistingConnectionAdded(android.telecom.RemoteConnection);
+    field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.ConnectionService";
+  }
+
+  public final class DisconnectCause implements android.os.Parcelable {
+    ctor public DisconnectCause(int);
+    ctor public DisconnectCause(int, java.lang.String);
+    ctor public DisconnectCause(int, java.lang.CharSequence, java.lang.CharSequence, java.lang.String);
+    ctor public DisconnectCause(int, java.lang.CharSequence, java.lang.CharSequence, java.lang.String, int);
+    method public int describeContents();
+    method public int getCode();
+    method public java.lang.CharSequence getDescription();
+    method public java.lang.CharSequence getLabel();
+    method public java.lang.String getReason();
+    method public int getTone();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int BUSY = 7; // 0x7
+    field public static final int CANCELED = 4; // 0x4
+    field public static final int CONNECTION_MANAGER_NOT_SUPPORTED = 10; // 0xa
+    field public static final android.os.Parcelable.Creator<android.telecom.DisconnectCause> CREATOR;
+    field public static final int ERROR = 1; // 0x1
+    field public static final int LOCAL = 2; // 0x2
+    field public static final int MISSED = 5; // 0x5
+    field public static final int OTHER = 9; // 0x9
+    field public static final int REJECTED = 6; // 0x6
+    field public static final int REMOTE = 3; // 0x3
+    field public static final int RESTRICTED = 8; // 0x8
+    field public static final int UNKNOWN = 0; // 0x0
+  }
+
+  public class GatewayInfo implements android.os.Parcelable {
+    ctor public GatewayInfo(java.lang.String, android.net.Uri, android.net.Uri);
+    method public int describeContents();
+    method public android.net.Uri getGatewayAddress();
+    method public java.lang.String getGatewayProviderPackageName();
+    method public android.net.Uri getOriginalAddress();
+    method public boolean isEmpty();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telecom.GatewayInfo> CREATOR;
+  }
+
+  public abstract interface IConferenceable {
+  }
+
+  public abstract class InCallService extends android.app.Service {
+    ctor public InCallService();
+    method public final android.telecom.Phone getPhone();
+    method public android.os.IBinder onBind(android.content.Intent);
+    method public void onPhoneCreated(android.telecom.Phone);
+    method public void onPhoneDestroyed(android.telecom.Phone);
+    field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.InCallService";
+  }
+
+  public static abstract class InCallService.VideoCall {
+    ctor public InCallService.VideoCall();
+    method public abstract void removeVideoCallListener();
+    method public abstract void requestCallDataUsage();
+    method public abstract void requestCameraCapabilities();
+    method public abstract void sendSessionModifyRequest(android.telecom.VideoProfile);
+    method public abstract void sendSessionModifyResponse(android.telecom.VideoProfile);
+    method public abstract void setCamera(java.lang.String);
+    method public abstract void setDeviceOrientation(int);
+    method public abstract void setDisplaySurface(android.view.Surface);
+    method public abstract void setPauseImage(java.lang.String);
+    method public abstract void setPreviewSurface(android.view.Surface);
+    method public abstract void setVideoCallListener(android.telecom.InCallService.VideoCall.Listener);
+    method public abstract void setZoom(float);
+  }
+
+  public static abstract class InCallService.VideoCall.Listener {
+    ctor public InCallService.VideoCall.Listener();
+    method public abstract void onCallDataUsageChanged(long);
+    method public abstract void onCallSessionEvent(int);
+    method public abstract void onCameraCapabilitiesChanged(android.telecom.CameraCapabilities);
+    method public abstract void onPeerDimensionsChanged(int, int);
+    method public abstract void onSessionModifyRequestReceived(android.telecom.VideoProfile);
+    method public abstract void onSessionModifyResponseReceived(int, android.telecom.VideoProfile, android.telecom.VideoProfile);
+    method public abstract void onVideoQualityChanged(int);
+  }
+
+  public final class Phone {
+    method public final void addListener(android.telecom.Phone.Listener);
+    method public final boolean canAddCall();
+    method public final android.telecom.AudioState getAudioState();
+    method public final java.util.List<android.telecom.Call> getCalls();
+    method public final void removeListener(android.telecom.Phone.Listener);
+    method public final void setAudioRoute(int);
+    method public final void setMuted(boolean);
+  }
+
+  public static abstract class Phone.Listener {
+    ctor public Phone.Listener();
+    method public void onAudioStateChanged(android.telecom.Phone, android.telecom.AudioState);
+    method public void onBringToForeground(android.telecom.Phone, boolean);
+    method public void onCallAdded(android.telecom.Phone, android.telecom.Call);
+    method public void onCallRemoved(android.telecom.Phone, android.telecom.Call);
+    method public void onCanAddCallChanged(android.telecom.Phone, boolean);
+  }
+
+  public class PhoneAccount implements android.os.Parcelable {
+    method public static android.telecom.PhoneAccount.Builder builder(android.telecom.PhoneAccountHandle, java.lang.CharSequence);
+    method public android.graphics.drawable.Drawable createIconDrawable(android.content.Context);
+    method public int describeContents();
+    method public android.telecom.PhoneAccountHandle getAccountHandle();
+    method public android.net.Uri getAddress();
+    method public int getCapabilities();
+    method public int getHighlightColor();
+    method public android.graphics.Bitmap getIconBitmap();
+    method public java.lang.String getIconPackageName();
+    method public int getIconResId();
+    method public int getIconTint();
+    method public java.lang.CharSequence getLabel();
+    method public java.lang.CharSequence getShortDescription();
+    method public android.net.Uri getSubscriptionAddress();
+    method public java.util.List<java.lang.String> getSupportedUriSchemes();
+    method public boolean hasCapabilities(int);
+    method public boolean supportsUriScheme(java.lang.String);
+    method public android.telecom.PhoneAccount.Builder toBuilder();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int CAPABILITY_CALL_PROVIDER = 2; // 0x2
+    field public static final int CAPABILITY_CONNECTION_MANAGER = 1; // 0x1
+    field public static final int CAPABILITY_PLACE_EMERGENCY_CALLS = 16; // 0x10
+    field public static final int CAPABILITY_SIM_SUBSCRIPTION = 4; // 0x4
+    field public static final android.os.Parcelable.Creator<android.telecom.PhoneAccount> CREATOR;
+    field public static final int NO_HIGHLIGHT_COLOR = 0; // 0x0
+    field public static final int NO_ICON_TINT = 0; // 0x0
+    field public static final int NO_RESOURCE_ID = -1; // 0xffffffff
+    field public static final java.lang.String SCHEME_SIP = "sip";
+    field public static final java.lang.String SCHEME_TEL = "tel";
+    field public static final java.lang.String SCHEME_VOICEMAIL = "voicemail";
+  }
+
+  public static class PhoneAccount.Builder {
+    ctor public PhoneAccount.Builder(android.telecom.PhoneAccountHandle, java.lang.CharSequence);
+    ctor public PhoneAccount.Builder(android.telecom.PhoneAccount);
+    method public android.telecom.PhoneAccount.Builder addSupportedUriScheme(java.lang.String);
+    method public android.telecom.PhoneAccount build();
+    method public android.telecom.PhoneAccount.Builder setAddress(android.net.Uri);
+    method public android.telecom.PhoneAccount.Builder setCapabilities(int);
+    method public android.telecom.PhoneAccount.Builder setHighlightColor(int);
+    method public android.telecom.PhoneAccount.Builder setIcon(android.content.Context, int);
+    method public android.telecom.PhoneAccount.Builder setIcon(java.lang.String, int);
+    method public android.telecom.PhoneAccount.Builder setIcon(android.content.Context, int, int);
+    method public android.telecom.PhoneAccount.Builder setIcon(java.lang.String, int, int);
+    method public android.telecom.PhoneAccount.Builder setIcon(android.graphics.Bitmap);
+    method public android.telecom.PhoneAccount.Builder setShortDescription(java.lang.CharSequence);
+    method public android.telecom.PhoneAccount.Builder setSubscriptionAddress(android.net.Uri);
+    method public android.telecom.PhoneAccount.Builder setSupportedUriSchemes(java.util.List<java.lang.String>);
+  }
+
+  public class PhoneAccountHandle implements android.os.Parcelable {
+    ctor public PhoneAccountHandle(android.content.ComponentName, java.lang.String);
+    ctor public PhoneAccountHandle(android.content.ComponentName, java.lang.String, android.os.UserHandle);
+    method public int describeContents();
+    method public android.content.ComponentName getComponentName();
+    method public java.lang.String getId();
+    method public android.os.UserHandle getUserHandle();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telecom.PhoneAccountHandle> CREATOR;
+  }
+
+  public final class RemoteConference {
+    method public void disconnect();
+    method public java.util.List<android.telecom.RemoteConnection> getConferenceableConnections();
+    method public final int getConnectionCapabilities();
+    method public final java.util.List<android.telecom.RemoteConnection> getConnections();
+    method public android.telecom.DisconnectCause getDisconnectCause();
+    method public final int getState();
+    method public void hold();
+    method public void merge();
+    method public void playDtmfTone(char);
+    method public final void registerCallback(android.telecom.RemoteConference.Callback);
+    method public void separate(android.telecom.RemoteConnection);
+    method public void setAudioState(android.telecom.AudioState);
+    method public void stopDtmfTone();
+    method public void swap();
+    method public void unhold();
+    method public final void unregisterCallback(android.telecom.RemoteConference.Callback);
+  }
+
+  public static abstract class RemoteConference.Callback {
+    ctor public RemoteConference.Callback();
+    method public void onConferenceableConnectionsChanged(android.telecom.RemoteConference, java.util.List<android.telecom.RemoteConnection>);
+    method public void onConnectionAdded(android.telecom.RemoteConference, android.telecom.RemoteConnection);
+    method public void onConnectionCapabilitiesChanged(android.telecom.RemoteConference, int);
+    method public void onConnectionRemoved(android.telecom.RemoteConference, android.telecom.RemoteConnection);
+    method public void onDestroyed(android.telecom.RemoteConference);
+    method public void onDisconnected(android.telecom.RemoteConference, android.telecom.DisconnectCause);
+    method public void onStateChanged(android.telecom.RemoteConference, int, int);
+  }
+
+  public final class RemoteConnection {
+    method public void abort();
+    method public void answer();
+    method public void disconnect();
+    method public android.net.Uri getAddress();
+    method public int getAddressPresentation();
+    method public java.lang.CharSequence getCallerDisplayName();
+    method public int getCallerDisplayNamePresentation();
+    method public android.telecom.RemoteConference getConference();
+    method public java.util.List<android.telecom.RemoteConnection> getConferenceableConnections();
+    method public int getConnectionCapabilities();
+    method public android.telecom.DisconnectCause getDisconnectCause();
+    method public int getState();
+    method public android.telecom.StatusHints getStatusHints();
+    method public void hold();
+    method public boolean isRingbackRequested();
+    method public boolean isVoipAudioMode();
+    method public void playDtmfTone(char);
+    method public void postDialContinue(boolean);
+    method public void registerCallback(android.telecom.RemoteConnection.Callback);
+    method public void reject();
+    method public void setAudioState(android.telecom.AudioState);
+    method public void stopDtmfTone();
+    method public void unhold();
+    method public void unregisterCallback(android.telecom.RemoteConnection.Callback);
+  }
+
+  public static abstract class RemoteConnection.Callback {
+    ctor public RemoteConnection.Callback();
+    method public void onAddressChanged(android.telecom.RemoteConnection, android.net.Uri, int);
+    method public void onCallerDisplayNameChanged(android.telecom.RemoteConnection, java.lang.String, int);
+    method public void onConferenceChanged(android.telecom.RemoteConnection, android.telecom.RemoteConference);
+    method public void onConferenceableConnectionsChanged(android.telecom.RemoteConnection, java.util.List<android.telecom.RemoteConnection>);
+    method public void onConnectionCapabilitiesChanged(android.telecom.RemoteConnection, int);
+    method public void onDestroyed(android.telecom.RemoteConnection);
+    method public void onDisconnected(android.telecom.RemoteConnection, android.telecom.DisconnectCause);
+    method public void onPostDialChar(android.telecom.RemoteConnection, char);
+    method public void onPostDialWait(android.telecom.RemoteConnection, java.lang.String);
+    method public void onRingbackRequested(android.telecom.RemoteConnection, boolean);
+    method public void onStateChanged(android.telecom.RemoteConnection, int);
+    method public void onStatusHintsChanged(android.telecom.RemoteConnection, android.telecom.StatusHints);
+    method public void onVoipAudioChanged(android.telecom.RemoteConnection, boolean);
+  }
+
+  public final class StatusHints implements android.os.Parcelable {
+    ctor public StatusHints(android.content.ComponentName, java.lang.CharSequence, int, android.os.Bundle);
+    method public int describeContents();
+    method public android.os.Bundle getExtras();
+    method public android.graphics.drawable.Drawable getIcon(android.content.Context);
+    method public int getIconResId();
+    method public java.lang.CharSequence getLabel();
+    method public android.content.ComponentName getPackageName();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telecom.StatusHints> CREATOR;
+  }
+
   public class TelecomManager {
+    method public void addNewIncomingCall(android.telecom.PhoneAccountHandle, android.os.Bundle);
     method public void cancelMissedCallsNotification();
+    method public android.net.Uri getAdnUriForPhoneAccount(android.telecom.PhoneAccountHandle);
+    method public java.util.List<android.telecom.PhoneAccountHandle> getCallCapablePhoneAccounts();
+    method public android.telecom.PhoneAccountHandle getDefaultOutgoingPhoneAccount(java.lang.String);
+    method public java.lang.String getLine1Number(android.telecom.PhoneAccountHandle);
+    method public android.telecom.PhoneAccount getPhoneAccount(android.telecom.PhoneAccountHandle);
+    method public android.telecom.PhoneAccountHandle getSimCallManager();
+    method public java.lang.String getVoiceMailNumber(android.telecom.PhoneAccountHandle);
     method public boolean handleMmi(java.lang.String);
+    method public boolean handleMmi(java.lang.String, android.telecom.PhoneAccountHandle);
     method public boolean isInCall();
+    method public boolean isVoiceMailNumber(android.telecom.PhoneAccountHandle, java.lang.String);
+    method public void registerPhoneAccount(android.telecom.PhoneAccount);
     method public void showInCallScreen(boolean);
+    method public void silenceRinger();
+    method public void unregisterPhoneAccount(android.telecom.PhoneAccountHandle);
+    field public static final java.lang.String ACTION_CHANGE_PHONE_ACCOUNTS = "android.telecom.action.CHANGE_PHONE_ACCOUNTS";
+    field public static final java.lang.String ACTION_INCOMING_CALL = "android.telecom.action.INCOMING_CALL";
     field public static final java.lang.String ACTION_SHOW_CALL_SETTINGS = "android.telecom.action.SHOW_CALL_SETTINGS";
     field public static final char DTMF_CHARACTER_PAUSE = 44; // 0x002c ','
     field public static final char DTMF_CHARACTER_WAIT = 59; // 0x003b ';'
+    field public static final java.lang.String EXTRA_CALL_BACK_NUMBER = "android.telecom.extra.CALL_BACK_NUMBER";
     field public static final java.lang.String EXTRA_CALL_DISCONNECT_CAUSE = "android.telecom.extra.CALL_DISCONNECT_CAUSE";
     field public static final java.lang.String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecom.extra.CALL_DISCONNECT_MESSAGE";
+    field public static final java.lang.String EXTRA_INCOMING_CALL_EXTRAS = "android.telecom.extra.INCOMING_CALL_EXTRAS";
+    field public static final java.lang.String EXTRA_OUTGOING_CALL_EXTRAS = "android.telecom.extra.OUTGOING_CALL_EXTRAS";
+    field public static final java.lang.String EXTRA_PHONE_ACCOUNT_HANDLE = "android.telecom.extra.PHONE_ACCOUNT_HANDLE";
     field public static final java.lang.String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.telecom.extra.START_CALL_WITH_SPEAKERPHONE";
+    field public static final java.lang.String EXTRA_START_CALL_WITH_VIDEO_STATE = "android.telecom.extra.START_CALL_WITH_VIDEO_STATE";
     field public static final java.lang.String GATEWAY_ORIGINAL_ADDRESS = "android.telecom.extra.GATEWAY_ORIGINAL_ADDRESS";
     field public static final java.lang.String GATEWAY_PROVIDER_PACKAGE = "android.telecom.extra.GATEWAY_PROVIDER_PACKAGE";
     field public static final int PRESENTATION_ALLOWED = 1; // 0x1
@@ -28185,10 +28877,51 @@
     field public static final int PRESENTATION_UNKNOWN = 3; // 0x3
   }
 
+  public class VideoProfile implements android.os.Parcelable {
+    ctor public VideoProfile(int);
+    ctor public VideoProfile(int, int);
+    method public int describeContents();
+    method public int getQuality();
+    method public int getVideoState();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telecom.VideoProfile> CREATOR;
+    field public static final int QUALITY_DEFAULT = 4; // 0x4
+    field public static final int QUALITY_HIGH = 1; // 0x1
+    field public static final int QUALITY_LOW = 3; // 0x3
+    field public static final int QUALITY_MEDIUM = 2; // 0x2
+  }
+
+  public static class VideoProfile.VideoState {
+    ctor public VideoProfile.VideoState();
+    method public static boolean isAudioOnly(int);
+    method public static boolean isBidirectional(int);
+    method public static boolean isPaused(int);
+    method public static boolean isReceptionEnabled(int);
+    method public static boolean isTransmissionEnabled(int);
+    field public static final int AUDIO_ONLY = 0; // 0x0
+    field public static final int BIDIRECTIONAL = 3; // 0x3
+    field public static final int PAUSED = 4; // 0x4
+    field public static final int RX_ENABLED = 2; // 0x2
+    field public static final int TX_ENABLED = 1; // 0x1
+  }
+
 }
 
 package android.telephony {
 
+  public class CarrierConfigManager {
+    ctor public CarrierConfigManager();
+    method public android.os.Bundle getConfig();
+    method public android.os.Bundle getConfigForSubId(int);
+    method public void reloadCarrierConfigForSubId(int);
+    field public static final java.lang.String ACTION_CARRIER_CONFIG_CHANGED = "android.intent.action.carrier_config_changed";
+    field public static final java.lang.String BOOL_CARRIER_VOLTE_AVAILABLE = "bool_carrier_volte_available";
+    field public static final java.lang.String BOOL_CARRIER_VOLTE_PROVISIONED = "bool_carrier_volte_provisioned";
+    field public static final java.lang.String BOOL_CARRIER_VOLTE_TTY_SUPPORTED = "bool_carrier_volte_tty_supported";
+    field public static final java.lang.String BOOL_SHOW_APN_SETTING_CDMA = "bool_show_apn_setting_cdma";
+    field public static final java.lang.String INT_VOLTE_REPLACEMENT_RAT = "int_volte_replacement_rat";
+  }
+
   public final class CellIdentityCdma implements android.os.Parcelable {
     method public int describeContents();
     method public int getBasestationId();
@@ -28378,6 +29111,7 @@
 
   public class PhoneNumberUtils {
     ctor public PhoneNumberUtils();
+    method public static void addPhoneTtsSpan(android.text.Spannable, int, int);
     method public static java.lang.String calledPartyBCDFragmentToString(byte[], int, int);
     method public static java.lang.String calledPartyBCDToString(byte[], int, int);
     method public static boolean compare(java.lang.String, java.lang.String);
@@ -28394,6 +29128,8 @@
     method public static java.lang.String formatNumberToE164(java.lang.String, java.lang.String);
     method public static deprecated int getFormatTypeForLocale(java.util.Locale);
     method public static java.lang.String getNumberFromIntent(android.content.Intent, android.content.Context);
+    method public static android.text.style.TtsSpan getPhoneTtsSpan(java.lang.String);
+    method public static java.lang.CharSequence getPhoneTtsSpannable(java.lang.CharSequence);
     method public static java.lang.String getStrippedReversed(java.lang.String);
     method public static final boolean is12Key(char);
     method public static final boolean isDialable(char);
@@ -28656,6 +29392,7 @@
     method public int getDataActivity();
     method public int getDataState();
     method public java.lang.String getDeviceId();
+    method public java.lang.String getDeviceId(int);
     method public java.lang.String getDeviceSoftwareVersion();
     method public java.lang.String getGroupIdLevel1();
     method public java.lang.String getLine1Number();
@@ -28666,6 +29403,7 @@
     method public java.lang.String getNetworkOperator();
     method public java.lang.String getNetworkOperatorName();
     method public int getNetworkType();
+    method public int getPhoneCount();
     method public int getPhoneType();
     method public java.lang.String getSimCountryIso();
     method public java.lang.String getSimOperator();
diff --git a/api/system-current.txt b/api/system-current.txt
index 9920ca7..ead5bdf 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -28,6 +28,7 @@
     field public static final java.lang.String BATTERY_STATS = "android.permission.BATTERY_STATS";
     field public static final java.lang.String BIND_ACCESSIBILITY_SERVICE = "android.permission.BIND_ACCESSIBILITY_SERVICE";
     field public static final java.lang.String BIND_APPWIDGET = "android.permission.BIND_APPWIDGET";
+    field public static final java.lang.String BIND_CARRIER_CONFIG_SERVICE = "android.permission.BIND_CARRIER_CONFIG_SERVICE";
     field public static final java.lang.String BIND_CARRIER_MESSAGING_SERVICE = "android.permission.BIND_CARRIER_MESSAGING_SERVICE";
     field public static final java.lang.String BIND_CONDITION_PROVIDER_SERVICE = "android.permission.BIND_CONDITION_PROVIDER_SERVICE";
     field public static final java.lang.String BIND_CONNECTION_SERVICE = "android.permission.BIND_CONNECTION_SERVICE";
@@ -6033,6 +6034,7 @@
     method public android.bluetooth.BluetoothDevice getRemoteDevice(byte[]);
     method public int getScanMode();
     method public int getState();
+    method public boolean isBleScanAlwaysAvailable();
     method public boolean isDiscovering();
     method public boolean isEnabled();
     method public boolean isMultipleAdvertisementSupported();
@@ -6049,6 +6051,7 @@
     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";
     field public static final java.lang.String ACTION_LOCAL_NAME_CHANGED = "android.bluetooth.adapter.action.LOCAL_NAME_CHANGED";
+    field public static final java.lang.String ACTION_REQUEST_BLE_SCAN_ALWAYS_AVAILABLE = "android.bluetooth.adapter.action.REQUEST_BLE_SCAN_ALWAYS_AVAILABLE";
     field public static final java.lang.String ACTION_REQUEST_DISCOVERABLE = "android.bluetooth.adapter.action.REQUEST_DISCOVERABLE";
     field public static final java.lang.String ACTION_REQUEST_ENABLE = "android.bluetooth.adapter.action.REQUEST_ENABLE";
     field public static final java.lang.String ACTION_SCAN_MODE_CHANGED = "android.bluetooth.adapter.action.SCAN_MODE_CHANGED";
@@ -6699,6 +6702,7 @@
     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 SAP = 10; // 0xa
     field public static final int STATE_CONNECTED = 2; // 0x2
     field public static final int STATE_CONNECTING = 1; // 0x1
     field public static final int STATE_DISCONNECTED = 0; // 0x0
@@ -6710,6 +6714,25 @@
     method public abstract void onServiceDisconnected(int);
   }
 
+  public final class BluetoothSap implements android.bluetooth.BluetoothProfile {
+    method public synchronized void close();
+    method public boolean connect(android.bluetooth.BluetoothDevice);
+    method public boolean disconnect(android.bluetooth.BluetoothDevice);
+    method public android.bluetooth.BluetoothDevice getClient();
+    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 int getPriority(android.bluetooth.BluetoothDevice);
+    method public int getState();
+    method public boolean isConnected(android.bluetooth.BluetoothDevice);
+    method public boolean setPriority(android.bluetooth.BluetoothDevice, int);
+    field public static final java.lang.String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED";
+    field public static final int RESULT_CANCELED = 2; // 0x2
+    field public static final int RESULT_FAILURE = 0; // 0x0
+    field public static final int RESULT_SUCCESS = 1; // 0x1
+    field public static final int STATE_ERROR = -1; // 0xffffffff
+  }
+
   public final class BluetoothServerSocket implements java.io.Closeable {
     method public android.bluetooth.BluetoothSocket accept() throws java.io.IOException;
     method public android.bluetooth.BluetoothSocket accept(int) throws java.io.IOException;
@@ -6719,10 +6742,16 @@
   public final class BluetoothSocket implements java.io.Closeable {
     method public void close() throws java.io.IOException;
     method public void connect() throws java.io.IOException;
+    method public int getConnectionType();
     method public java.io.InputStream getInputStream() throws java.io.IOException;
+    method public int getMaxReceivePacketSize();
+    method public int getMaxTransmitPacketSize();
     method public java.io.OutputStream getOutputStream() throws java.io.IOException;
     method public android.bluetooth.BluetoothDevice getRemoteDevice();
     method public boolean isConnected();
+    field public static final int TYPE_L2CAP = 3; // 0x3
+    field public static final int TYPE_RFCOMM = 1; // 0x1
+    field public static final int TYPE_SCO = 2; // 0x2
   }
 
 }
@@ -6889,6 +6918,7 @@
     field public static final int SCAN_MODE_BALANCED = 1; // 0x1
     field public static final int SCAN_MODE_LOW_LATENCY = 2; // 0x2
     field public static final int SCAN_MODE_LOW_POWER = 0; // 0x0
+    field public static final int SCAN_MODE_OPPORTUNISTIC = -1; // 0xffffffff
     field public static final int SCAN_RESULT_TYPE_ABBREVIATED = 1; // 0x1
     field public static final int SCAN_RESULT_TYPE_FULL = 0; // 0x0
   }
@@ -7475,6 +7505,7 @@
     field public static final java.lang.String BLUETOOTH_SERVICE = "bluetooth";
     field public static final java.lang.String CAMERA_SERVICE = "camera";
     field public static final java.lang.String CAPTIONING_SERVICE = "captioning";
+    field public static final java.lang.String CARRIER_CONFIG_SERVICE = "carrier_config";
     field public static final java.lang.String CLIPBOARD_SERVICE = "clipboard";
     field public static final java.lang.String CONNECTIVITY_SERVICE = "connectivity";
     field public static final java.lang.String CONSUMER_IR_SERVICE = "consumer_ir";
@@ -11091,6 +11122,8 @@
   public class ImageFormat {
     ctor public ImageFormat();
     method public static int getBitsPerPixel(int);
+    field public static final int DEPTH16 = 1144402265; // 0x44363159
+    field public static final int DEPTH_POINT_CLOUD = 257; // 0x101
     field public static final int JPEG = 256; // 0x100
     field public static final int NV16 = 16; // 0x10
     field public static final int NV21 = 17; // 0x11
@@ -12864,6 +12897,8 @@
     field public static final int CAMERA_DISABLED = 1; // 0x1
     field public static final int CAMERA_DISCONNECTED = 2; // 0x2
     field public static final int CAMERA_ERROR = 3; // 0x3
+    field public static final int CAMERA_IN_USE = 4; // 0x4
+    field public static final int MAX_CAMERAS_IN_USE = 5; // 0x5
   }
 
   public abstract class CameraCaptureSession implements java.lang.AutoCloseable {
@@ -12907,11 +12942,14 @@
     field public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Range<java.lang.Integer>[]> CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Range<java.lang.Integer>> CONTROL_AE_COMPENSATION_RANGE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Rational> CONTROL_AE_COMPENSATION_STEP;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Boolean> CONTROL_AE_LOCK_AVAILABLE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AF_AVAILABLE_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AVAILABLE_EFFECTS;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AVAILABLE_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AVAILABLE_SCENE_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AWB_AVAILABLE_MODES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Boolean> CONTROL_AWB_LOCK_AVAILABLE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> CONTROL_MAX_REGIONS_AE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> CONTROL_MAX_REGIONS_AF;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> CONTROL_MAX_REGIONS_AWB;
@@ -12928,8 +12966,13 @@
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> LENS_INFO_FOCUS_DISTANCE_CALIBRATION;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Float> LENS_INFO_HYPERFOCAL_DISTANCE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Float> LENS_INFO_MINIMUM_FOCUS_DISTANCE;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_INTRINSIC_CALIBRATION;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_POSE_ROTATION;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_POSE_TRANSLATION;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key<float[]> LENS_RADIAL_DISTORTION;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> REQUEST_AVAILABLE_CAPABILITIES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_MAX_NUM_INPUT_STREAMS;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_MAX_NUM_OUTPUT_PROC;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_MAX_NUM_OUTPUT_PROC_STALLING;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_MAX_NUM_OUTPUT_RAW;
@@ -12949,6 +12992,7 @@
     field public static final android.hardware.camera2.CameraCharacteristics.Key<android.graphics.Rect> SENSOR_INFO_ACTIVE_ARRAY_SIZE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> SENSOR_INFO_COLOR_FILTER_ARRANGEMENT;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Range<java.lang.Long>> SENSOR_INFO_EXPOSURE_TIME_RANGE;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Boolean> SENSOR_INFO_LENS_SHADING_APPLIED;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Long> SENSOR_INFO_MAX_FRAME_DURATION;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.SizeF> SENSOR_INFO_PHYSICAL_SIZE;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Size> SENSOR_INFO_PIXEL_ARRAY_SIZE;
@@ -12959,8 +13003,10 @@
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> SENSOR_ORIENTATION;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> SENSOR_REFERENCE_ILLUMINANT1;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Byte> SENSOR_REFERENCE_ILLUMINANT2;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> SHADING_AVAILABLE_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<boolean[]> STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES;
+    field public static final android.hardware.camera2.CameraCharacteristics.Key<byte[]> STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> STATISTICS_INFO_MAX_FACE_COUNT;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> SYNC_MAX_LATENCY;
     field public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> TONEMAP_AVAILABLE_TONE_MAP_MODES;
@@ -13004,7 +13050,10 @@
     method public java.lang.String[] getCameraIdList() throws android.hardware.camera2.CameraAccessException;
     method public void openCamera(java.lang.String, android.hardware.camera2.CameraDevice.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
     method public void registerAvailabilityCallback(android.hardware.camera2.CameraManager.AvailabilityCallback, android.os.Handler);
+    method public void registerTorchCallback(android.hardware.camera2.CameraManager.TorchCallback, android.os.Handler);
+    method public void setTorchMode(java.lang.String, boolean) throws android.hardware.camera2.CameraAccessException;
     method public void unregisterAvailabilityCallback(android.hardware.camera2.CameraManager.AvailabilityCallback);
+    method public void unregisterTorchCallback(android.hardware.camera2.CameraManager.TorchCallback);
   }
 
   public static abstract class CameraManager.AvailabilityCallback {
@@ -13013,6 +13062,12 @@
     method public void onCameraUnavailable(java.lang.String);
   }
 
+  public static abstract class CameraManager.TorchCallback {
+    ctor public CameraManager.TorchCallback();
+    method public void onTorchModeChanged(java.lang.String, boolean);
+    method public void onTorchModeUnavailable(java.lang.String);
+  }
+
   public abstract class CameraMetadata {
     method public java.util.List<TKey> getKeys();
     field public static final int COLOR_CORRECTION_ABERRATION_MODE_FAST = 1; // 0x1
@@ -13030,6 +13085,7 @@
     field public static final int CONTROL_AE_MODE_ON_ALWAYS_FLASH = 3; // 0x3
     field public static final int CONTROL_AE_MODE_ON_AUTO_FLASH = 2; // 0x2
     field public static final int CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE = 4; // 0x4
+    field public static final int CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL = 2; // 0x2
     field public static final int CONTROL_AE_PRECAPTURE_TRIGGER_IDLE = 0; // 0x0
     field public static final int CONTROL_AE_PRECAPTURE_TRIGGER_START = 1; // 0x1
     field public static final int CONTROL_AE_STATE_CONVERGED = 2; // 0x2
@@ -13126,6 +13182,7 @@
     field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY = 2; // 0x2
     field public static final int INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED = 0; // 0x0
     field public static final int LENS_FACING_BACK = 1; // 0x1
+    field public static final int LENS_FACING_EXTERNAL = 2; // 0x2
     field public static final int LENS_FACING_FRONT = 0; // 0x0
     field public static final int LENS_INFO_FOCUS_DISTANCE_CALIBRATION_APPROXIMATE = 1; // 0x1
     field public static final int LENS_INFO_FOCUS_DISTANCE_CALIBRATION_CALIBRATED = 2; // 0x2
@@ -13136,13 +13193,16 @@
     field public static final int LENS_STATE_STATIONARY = 0; // 0x0
     field public static final int NOISE_REDUCTION_MODE_FAST = 1; // 0x1
     field public static final int NOISE_REDUCTION_MODE_HIGH_QUALITY = 2; // 0x2
+    field public static final int NOISE_REDUCTION_MODE_MINIMAL = 3; // 0x3
     field public static final int NOISE_REDUCTION_MODE_OFF = 0; // 0x0
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE = 0; // 0x0
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE = 6; // 0x6
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING = 2; // 0x2
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR = 1; // 0x1
+    field public static final int REQUEST_AVAILABLE_CAPABILITIES_OPAQUE_REPROCESSING = 4; // 0x4
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_RAW = 3; // 0x3
     field public static final int REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS = 5; // 0x5
+    field public static final int REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING = 7; // 0x7
     field public static final int SCALER_CROPPING_TYPE_CENTER_ONLY = 0; // 0x0
     field public static final int SCALER_CROPPING_TYPE_FREEFORM = 1; // 0x1
     field public static final int SENSOR_INFO_COLOR_FILTER_ARRANGEMENT_BGGR = 3; // 0x3
@@ -13192,7 +13252,11 @@
     field public static final int SYNC_MAX_LATENCY_UNKNOWN = -1; // 0xffffffff
     field public static final int TONEMAP_MODE_CONTRAST_CURVE = 0; // 0x0
     field public static final int TONEMAP_MODE_FAST = 1; // 0x1
+    field public static final int TONEMAP_MODE_GAMMA_VALUE = 3; // 0x3
     field public static final int TONEMAP_MODE_HIGH_QUALITY = 2; // 0x2
+    field public static final int TONEMAP_MODE_PRESET_CURVE = 4; // 0x4
+    field public static final int TONEMAP_PRESET_CURVE_REC709 = 1; // 0x1
+    field public static final int TONEMAP_PRESET_CURVE_SRGB = 0; // 0x0
   }
 
   public class CaptureFailure {
@@ -13248,6 +13312,7 @@
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Float> LENS_FOCUS_DISTANCE;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> LENS_OPTICAL_STABILIZATION_MODE;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> NOISE_REDUCTION_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Float> REPROCESS_EFFECTIVE_EXPOSURE_FACTOR;
     field public static final android.hardware.camera2.CaptureRequest.Key<android.graphics.Rect> SCALER_CROP_REGION;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Long> SENSOR_EXPOSURE_TIME;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Long> SENSOR_FRAME_DURATION;
@@ -13259,7 +13324,9 @@
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Boolean> STATISTICS_HOT_PIXEL_MAP_MODE;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> STATISTICS_LENS_SHADING_MAP_MODE;
     field public static final android.hardware.camera2.CaptureRequest.Key<android.hardware.camera2.params.TonemapCurve> TONEMAP_CURVE;
+    field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Float> TONEMAP_GAMMA;
     field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> TONEMAP_MODE;
+    field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> TONEMAP_PRESET_CURVE;
   }
 
   public static final class CaptureRequest.Builder {
@@ -13322,9 +13389,14 @@
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> LENS_FOCAL_LENGTH;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> LENS_FOCUS_DISTANCE;
     field public static final android.hardware.camera2.CaptureResult.Key<android.util.Pair<java.lang.Float, java.lang.Float>> LENS_FOCUS_RANGE;
+    field public static final android.hardware.camera2.CaptureResult.Key<float[]> LENS_INTRINSIC_CALIBRATION;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> LENS_OPTICAL_STABILIZATION_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key<float[]> LENS_POSE_ROTATION;
+    field public static final android.hardware.camera2.CaptureResult.Key<float[]> LENS_POSE_TRANSLATION;
+    field public static final android.hardware.camera2.CaptureResult.Key<float[]> LENS_RADIAL_DISTORTION;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> LENS_STATE;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> NOISE_REDUCTION_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> REPROCESS_EFFECTIVE_EXPOSURE_FACTOR;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Byte> REQUEST_PIPELINE_DEPTH;
     field public static final android.hardware.camera2.CaptureResult.Key<android.graphics.Rect> SCALER_CROP_REGION;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Long> SENSOR_EXPOSURE_TIME;
@@ -13346,7 +13418,9 @@
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> STATISTICS_LENS_SHADING_MAP_MODE;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> STATISTICS_SCENE_FLICKER;
     field public static final android.hardware.camera2.CaptureResult.Key<android.hardware.camera2.params.TonemapCurve> TONEMAP_CURVE;
+    field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> TONEMAP_GAMMA;
     field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> TONEMAP_MODE;
+    field public static final android.hardware.camera2.CaptureResult.Key<java.lang.Integer> TONEMAP_PRESET_CURVE;
   }
 
   public static final class CaptureResult.Key {
@@ -18169,34 +18243,43 @@
 
   public class ConnectivityManager {
     method public void addDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener);
+    method public boolean bindProcessToNetwork(android.net.Network);
+    method public android.net.Network getActiveNetwork();
     method public android.net.NetworkInfo getActiveNetworkInfo();
-    method public android.net.NetworkInfo[] getAllNetworkInfo();
+    method public deprecated android.net.NetworkInfo[] getAllNetworkInfo();
     method public android.net.Network[] getAllNetworks();
     method public deprecated boolean getBackgroundDataSetting();
+    method public android.net.Network getBoundNetworkForProcess();
+    method public android.net.ProxyInfo getDefaultProxy();
     method public android.net.LinkProperties getLinkProperties(android.net.Network);
     method public android.net.NetworkCapabilities getNetworkCapabilities(android.net.Network);
-    method public android.net.NetworkInfo getNetworkInfo(int);
+    method public deprecated android.net.NetworkInfo getNetworkInfo(int);
     method public android.net.NetworkInfo getNetworkInfo(android.net.Network);
     method public deprecated int getNetworkPreference();
-    method public static android.net.Network getProcessDefaultNetwork();
+    method public static deprecated android.net.Network getProcessDefaultNetwork();
+    method public void ignoreNetworkWithCaptivePortal(android.net.Network, java.lang.String);
     method public boolean isActiveNetworkMetered();
     method public boolean isDefaultNetworkActive();
     method public static boolean isNetworkTypeValid(int);
     method public void registerNetworkCallback(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
     method public void releaseNetworkRequest(android.app.PendingIntent);
     method public void removeDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener);
-    method public void reportBadNetwork(android.net.Network);
+    method public deprecated void reportBadNetwork(android.net.Network);
+    method public void reportCaptivePortalDismissed(android.net.Network, java.lang.String);
+    method public void reportNetworkConnectivity(android.net.Network, boolean);
     method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
     method public void requestNetwork(android.net.NetworkRequest, android.app.PendingIntent);
     method public deprecated boolean requestRouteToHost(int, int);
     method public deprecated void setNetworkPreference(int);
-    method public static boolean setProcessDefaultNetwork(android.net.Network);
+    method public static deprecated boolean setProcessDefaultNetwork(android.net.Network);
     method public deprecated int startUsingNetworkFeature(int, java.lang.String);
     method public deprecated int stopUsingNetworkFeature(int, java.lang.String);
     method public void unregisterNetworkCallback(android.net.ConnectivityManager.NetworkCallback);
     field public static final deprecated java.lang.String ACTION_BACKGROUND_DATA_SETTING_CHANGED = "android.net.conn.BACKGROUND_DATA_SETTING_CHANGED";
+    field public static final java.lang.String ACTION_CAPTIVE_PORTAL_SIGN_IN = "android.net.conn.CAPTIVE_PORTAL";
     field public static final java.lang.String CONNECTIVITY_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
     field public static final deprecated int DEFAULT_NETWORK_PREFERENCE = 1; // 0x1
+    field public static final java.lang.String EXTRA_CAPTIVE_PORTAL_TOKEN = "captivePortalToken";
     field public static final java.lang.String EXTRA_EXTRA_INFO = "extraInfo";
     field public static final java.lang.String EXTRA_IS_FAILOVER = "isFailover";
     field public static final java.lang.String EXTRA_NETWORK = "android.net.extra.NETWORK";
@@ -18357,8 +18440,10 @@
     method public int describeContents();
     method public java.net.InetAddress[] getAllByName(java.lang.String) throws java.net.UnknownHostException;
     method public java.net.InetAddress getByName(java.lang.String) throws java.net.UnknownHostException;
+    method public long getNetworkHandle();
     method public javax.net.SocketFactory getSocketFactory();
     method public java.net.URLConnection openConnection(java.net.URL) throws java.io.IOException;
+    method public java.net.URLConnection openConnection(java.net.URL, java.net.Proxy) throws java.io.IOException;
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.net.Network> CREATOR;
   }
@@ -18493,7 +18578,7 @@
     method public static final deprecated int getDefaultPort();
     method public static final deprecated java.lang.String getHost(android.content.Context);
     method public static final deprecated int getPort(android.content.Context);
-    field public static final java.lang.String EXTRA_PROXY_INFO = "android.intent.extra.PROXY_INFO";
+    field public static final deprecated java.lang.String EXTRA_PROXY_INFO = "android.intent.extra.PROXY_INFO";
     field public static final java.lang.String PROXY_CHANGE_ACTION = "android.intent.action.PROXY_CHANGE";
   }
 
@@ -19197,7 +19282,8 @@
   }
 
   public class RttManager {
-    method public android.net.wifi.RttManager.Capabilities getCapabilities();
+    method public deprecated android.net.wifi.RttManager.Capabilities getCapabilities();
+    method public android.net.wifi.RttManager.RttCapabilities getRttCapabilities();
     method public void startRanging(android.net.wifi.RttManager.RttParams[], android.net.wifi.RttManager.RttListener);
     method public void stopRanging(android.net.wifi.RttManager.RttListener);
     field public static final int BASE = 160256; // 0x27200
@@ -19207,37 +19293,57 @@
     field public static final int CMD_OP_STOP_RANGING = 160257; // 0x27201
     field public static final int CMD_OP_SUCCEEDED = 160259; // 0x27203
     field public static final java.lang.String DESCRIPTION_KEY = "android.net.wifi.RttManager.Description";
+    field public static final int PREAMBLE_HT = 2; // 0x2
+    field public static final int PREAMBLE_LEGACY = 1; // 0x1
+    field public static final int PREAMBLE_VHT = 4; // 0x4
     field public static final int REASON_INVALID_LISTENER = -3; // 0xfffffffd
     field public static final int REASON_INVALID_REQUEST = -4; // 0xfffffffc
     field public static final int REASON_NOT_AVAILABLE = -2; // 0xfffffffe
     field public static final int REASON_UNSPECIFIED = -1; // 0xffffffff
-    field public static final int RTT_CHANNEL_WIDTH_10 = 6; // 0x6
-    field public static final int RTT_CHANNEL_WIDTH_160 = 3; // 0x3
-    field public static final int RTT_CHANNEL_WIDTH_20 = 0; // 0x0
-    field public static final int RTT_CHANNEL_WIDTH_40 = 1; // 0x1
-    field public static final int RTT_CHANNEL_WIDTH_5 = 5; // 0x5
-    field public static final int RTT_CHANNEL_WIDTH_80 = 2; // 0x2
-    field public static final int RTT_CHANNEL_WIDTH_80P80 = 4; // 0x4
-    field public static final int RTT_CHANNEL_WIDTH_UNSPECIFIED = -1; // 0xffffffff
+    field public static final int RTT_BW_10_SUPPORT = 2; // 0x2
+    field public static final int RTT_BW_160_SUPPORT = 32; // 0x20
+    field public static final int RTT_BW_20_SUPPORT = 4; // 0x4
+    field public static final int RTT_BW_40_SUPPORT = 8; // 0x8
+    field public static final int RTT_BW_5_SUPPORT = 1; // 0x1
+    field public static final int RTT_BW_80_SUPPORT = 16; // 0x10
+    field public static final deprecated int RTT_CHANNEL_WIDTH_10 = 6; // 0x6
+    field public static final deprecated int RTT_CHANNEL_WIDTH_160 = 3; // 0x3
+    field public static final deprecated int RTT_CHANNEL_WIDTH_20 = 0; // 0x0
+    field public static final deprecated int RTT_CHANNEL_WIDTH_40 = 1; // 0x1
+    field public static final deprecated int RTT_CHANNEL_WIDTH_5 = 5; // 0x5
+    field public static final deprecated int RTT_CHANNEL_WIDTH_80 = 2; // 0x2
+    field public static final deprecated int RTT_CHANNEL_WIDTH_80P80 = 4; // 0x4
+    field public static final deprecated int RTT_CHANNEL_WIDTH_UNSPECIFIED = -1; // 0xffffffff
+    field public static final int RTT_PEER_NAN = 5; // 0x5
+    field public static final int RTT_PEER_P2P_CLIENT = 4; // 0x4
+    field public static final int RTT_PEER_P2P_GO = 3; // 0x3
     field public static final int RTT_PEER_TYPE_AP = 1; // 0x1
     field public static final int RTT_PEER_TYPE_STA = 2; // 0x2
-    field public static final int RTT_PEER_TYPE_UNSPECIFIED = 0; // 0x0
+    field public static final deprecated int RTT_PEER_TYPE_UNSPECIFIED = 0; // 0x0
     field public static final int RTT_STATUS_ABORTED = 8; // 0x8
     field public static final int RTT_STATUS_FAILURE = 1; // 0x1
     field public static final int RTT_STATUS_FAIL_AP_ON_DIFF_CHANNEL = 6; // 0x6
+    field public static final int RTT_STATUS_FAIL_BUSY_TRY_LATER = 12; // 0xc
+    field public static final int RTT_STATUS_FAIL_FTM_PARAM_OVERRIDE = 15; // 0xf
+    field public static final int RTT_STATUS_FAIL_INVALID_TS = 9; // 0x9
     field public static final int RTT_STATUS_FAIL_NOT_SCHEDULED_YET = 4; // 0x4
     field public static final int RTT_STATUS_FAIL_NO_CAPABILITY = 7; // 0x7
     field public static final int RTT_STATUS_FAIL_NO_RSP = 2; // 0x2
+    field public static final int RTT_STATUS_FAIL_PROTOCOL = 10; // 0xa
     field public static final int RTT_STATUS_FAIL_REJECTED = 3; // 0x3
+    field public static final int RTT_STATUS_FAIL_SCHEDULE = 11; // 0xb
     field public static final int RTT_STATUS_FAIL_TM_TIMEOUT = 5; // 0x5
+    field public static final int RTT_STATUS_INVALID_REQ = 13; // 0xd
+    field public static final int RTT_STATUS_NO_WIFI = 14; // 0xe
     field public static final int RTT_STATUS_SUCCESS = 0; // 0x0
-    field public static final int RTT_TYPE_11_MC = 4; // 0x4
-    field public static final int RTT_TYPE_11_V = 2; // 0x2
+    field public static final deprecated int RTT_TYPE_11_MC = 4; // 0x4
+    field public static final deprecated int RTT_TYPE_11_V = 2; // 0x2
     field public static final int RTT_TYPE_ONE_SIDED = 1; // 0x1
-    field public static final int RTT_TYPE_UNSPECIFIED = 0; // 0x0
+    field public static final int RTT_TYPE_TWO_SIDED = 2; // 0x2
+    field public static final deprecated int RTT_TYPE_UNSPECIFIED = 0; // 0x0
   }
 
-  public class RttManager.Capabilities {
+  public deprecated class RttManager.Capabilities {
     ctor public RttManager.Capabilities();
     field public int supportedPeerType;
     field public int supportedType;
@@ -19256,6 +19362,20 @@
     field public android.net.wifi.RttManager.RttResult[] mResults;
   }
 
+  public static class RttManager.RttCapabilities implements android.os.Parcelable {
+    ctor public RttManager.RttCapabilities();
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public int bwSupported;
+    field public boolean lciSupported;
+    field public boolean lcrSupported;
+    field public boolean oneSidedRttSupported;
+    field public int preambleSupported;
+    field public deprecated boolean supportedPeerType;
+    field public deprecated boolean supportedType;
+    field public boolean twoSided11McRttSupported;
+  }
+
   public static abstract interface RttManager.RttListener {
     method public abstract void onAborted();
     method public abstract void onFailure(int, java.lang.String);
@@ -19264,41 +19384,90 @@
 
   public static class RttManager.RttParams {
     ctor public RttManager.RttParams();
+    field public boolean LCIRequest;
+    field public boolean LCRRequest;
+    field public int bandwidth;
     field public java.lang.String bssid;
+    field public int burstTimeout;
+    field public int centerFreq0;
+    field public int centerFreq1;
     field public int channelWidth;
     field public int deviceType;
     field public int frequency;
-    field public int num_retries;
-    field public int num_samples;
+    field public int interval;
+    field public int numRetriesPerFTMR;
+    field public int numRetriesPerMeasurementFrame;
+    field public int numSamplesPerBurst;
+    field public deprecated int num_retries;
+    field public deprecated int num_samples;
+    field public int numberBurst;
+    field public int preamble;
     field public int requestType;
   }
 
   public static class RttManager.RttResult {
     ctor public RttManager.RttResult();
+    field public android.net.wifi.RttManager.WifiInformationElement LCI;
+    field public android.net.wifi.RttManager.WifiInformationElement LCR;
     field public java.lang.String bssid;
-    field public int distance_cm;
-    field public int distance_sd_cm;
-    field public int distance_spread_cm;
-    field public int requestType;
+    field public int burstDuration;
+    field public int burstNumber;
+    field public int distance;
+    field public int distanceSpread;
+    field public int distanceStandardDeviation;
+    field public deprecated int distance_cm;
+    field public deprecated int distance_sd_cm;
+    field public deprecated int distance_spread_cm;
+    field public int frameNumberPerBurstPeer;
+    field public int measurementFrameNumber;
+    field public int measurementType;
+    field public int negotiatedBurstNum;
+    field public deprecated int requestType;
+    field public int retryAfterDuration;
     field public int rssi;
-    field public int rssi_spread;
-    field public long rtt_ns;
-    field public long rtt_sd_ns;
-    field public long rtt_spread_ns;
+    field public int rssiSpread;
+    field public deprecated int rssi_spread;
+    field public long rtt;
+    field public long rttSpread;
+    field public long rttStandardDeviation;
+    field public deprecated long rtt_ns;
+    field public deprecated long rtt_sd_ns;
+    field public deprecated long rtt_spread_ns;
+    field public int rxRate;
     field public int status;
+    field public int successMeasurementFrameNumber;
     field public long ts;
-    field public int tx_rate;
+    field public int txRate;
+    field public deprecated int tx_rate;
+  }
+
+  public static class RttManager.WifiInformationElement {
+    ctor public RttManager.WifiInformationElement();
+    field public byte[] data;
+    field public byte id;
   }
 
   public class ScanResult implements android.os.Parcelable {
     method public int describeContents();
     method public void writeToParcel(android.os.Parcel, int);
     field public java.lang.String BSSID;
+    field public static final int CHANNEL_WIDTH_160MHZ = 3; // 0x3
+    field public static final int CHANNEL_WIDTH_20MHZ = 0; // 0x0
+    field public static final int CHANNEL_WIDTH_40MHZ = 1; // 0x1
+    field public static final int CHANNEL_WIDTH_80MHZ = 2; // 0x2
+    field public static final int CHANNEL_WIDTH_80MHZ_PLUS_MHZ = 4; // 0x4
     field public java.lang.String SSID;
     field public java.lang.String capabilities;
+    field public int centerFreq0;
+    field public int centerFreq1;
+    field public int channelWidth;
     field public int frequency;
+    field public boolean is80211McRTTResponder;
     field public int level;
+    field public java.lang.String operatorFriendlyName;
+    field public boolean passpointNetwork;
     field public long timestamp;
+    field public java.lang.String venueName;
   }
 
   public final class SupplicantState extends java.lang.Enum implements android.os.Parcelable {
@@ -19325,6 +19494,7 @@
   public class WifiConfiguration implements android.os.Parcelable {
     ctor public WifiConfiguration();
     method public int describeContents();
+    method public boolean isPasspoint();
     method public void writeToParcel(android.os.Parcel, int);
     field public java.lang.String BSSID;
     field public java.lang.String FQDN;
@@ -19334,6 +19504,8 @@
     field public java.util.BitSet allowedKeyManagement;
     field public java.util.BitSet allowedPairwiseCiphers;
     field public java.util.BitSet allowedProtocols;
+    field public int apBand;
+    field public int apChannel;
     field public int creatorUid;
     field public android.net.wifi.WifiEnterpriseConfig enterpriseConfig;
     field public boolean hiddenSSID;
@@ -19344,6 +19516,8 @@
     field public int numScorerOverrideAndSwitchedNetwork;
     field public java.lang.String preSharedKey;
     field public int priority;
+    field public java.lang.String providerFriendlyName;
+    field public java.util.HashSet<java.lang.Long> roamingConsortiumIds;
     field public int status;
     field public java.lang.String[] wepKeys;
     field public int wepTxKeyIndex;
@@ -19416,6 +19590,7 @@
     ctor public WifiEnterpriseConfig();
     ctor public WifiEnterpriseConfig(android.net.wifi.WifiEnterpriseConfig);
     method public int describeContents();
+    method public java.lang.String getAltSubjectMatch();
     method public java.lang.String getAnonymousIdentity();
     method public java.security.cert.X509Certificate getCaCertificate();
     method public java.security.cert.X509Certificate getClientCertificate();
@@ -19423,7 +19598,10 @@
     method public java.lang.String getIdentity();
     method public java.lang.String getPassword();
     method public int getPhase2Method();
-    method public java.lang.String getSubjectMatch();
+    method public java.lang.String getPlmn();
+    method public java.lang.String getRealm();
+    method public deprecated java.lang.String getSubjectMatch();
+    method public void setAltSubjectMatch(java.lang.String);
     method public void setAnonymousIdentity(java.lang.String);
     method public void setCaCertificate(java.security.cert.X509Certificate);
     method public void setClientKeyEntry(java.security.PrivateKey, java.security.cert.X509Certificate);
@@ -19431,13 +19609,16 @@
     method public void setIdentity(java.lang.String);
     method public void setPassword(java.lang.String);
     method public void setPhase2Method(int);
-    method public void setSubjectMatch(java.lang.String);
+    method public void setPlmn(java.lang.String);
+    method public void setRealm(java.lang.String);
+    method public deprecated void setSubjectMatch(java.lang.String);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.net.wifi.WifiEnterpriseConfig> CREATOR;
   }
 
   public static final class WifiEnterpriseConfig.Eap {
     field public static final int AKA = 5; // 0x5
+    field public static final int AKA_PRIME = 6; // 0x6
     field public static final int NONE = -1; // 0xffffffff
     field public static final int PEAP = 0; // 0x0
     field public static final int PWD = 3; // 0x3
@@ -19595,9 +19776,11 @@
     method public void configureWifiChange(android.net.wifi.WifiScanner.WifiChangeSettings);
     method public android.net.wifi.ScanResult[] getScanResults();
     method public void startBackgroundScan(android.net.wifi.WifiScanner.ScanSettings, android.net.wifi.WifiScanner.ScanListener);
+    method public void startScan(android.net.wifi.WifiScanner.ScanSettings, android.net.wifi.WifiScanner.ScanListener);
     method public void startTrackingBssids(android.net.wifi.WifiScanner.BssidInfo[], int, android.net.wifi.WifiScanner.BssidListener);
     method public void startTrackingWifiChange(android.net.wifi.WifiScanner.WifiChangeListener);
     method public void stopBackgroundScan(android.net.wifi.WifiScanner.ScanListener);
+    method public void stopScan(android.net.wifi.WifiScanner.ScanListener);
     method public void stopTrackingBssids(android.net.wifi.WifiScanner.BssidListener);
     method public void stopTrackingWifiChange(android.net.wifi.WifiScanner.WifiChangeListener);
     field public static final int MAX_SCAN_PERIOD_MS = 1024000; // 0xfa000
@@ -19634,6 +19817,7 @@
 
   public static abstract interface WifiScanner.BssidListener implements android.net.wifi.WifiScanner.ActionListener {
     method public abstract void onFound(android.net.wifi.ScanResult[]);
+    method public abstract void onLost(android.net.wifi.ScanResult[]);
   }
 
   public static class WifiScanner.ChannelSpec {
@@ -19649,10 +19833,37 @@
     field public android.net.wifi.WifiScanner.BssidInfo[] bssidInfos;
   }
 
+  public static class WifiScanner.ParcelableScanData implements android.os.Parcelable {
+    ctor public WifiScanner.ParcelableScanData(android.net.wifi.WifiScanner.ScanData[]);
+    method public int describeContents();
+    method public android.net.wifi.WifiScanner.ScanData[] getResults();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public android.net.wifi.WifiScanner.ScanData[] mResults;
+  }
+
+  public static class WifiScanner.ParcelableScanResults implements android.os.Parcelable {
+    ctor public WifiScanner.ParcelableScanResults(android.net.wifi.ScanResult[]);
+    method public int describeContents();
+    method public android.net.wifi.ScanResult[] getResults();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public android.net.wifi.ScanResult[] mResults;
+  }
+
+  public static class WifiScanner.ScanData implements android.os.Parcelable {
+    ctor public WifiScanner.ScanData(int, int, android.net.wifi.ScanResult[]);
+    ctor public WifiScanner.ScanData(android.net.wifi.WifiScanner.ScanData);
+    method public int describeContents();
+    method public int getFlags();
+    method public int getId();
+    method public android.net.wifi.ScanResult[] getResults();
+    method public void writeToParcel(android.os.Parcel, int);
+  }
+
   public static abstract interface WifiScanner.ScanListener implements android.net.wifi.WifiScanner.ActionListener {
     method public abstract void onFullResult(android.net.wifi.ScanResult);
     method public abstract void onPeriodChanged(int);
-    method public abstract void onResults(android.net.wifi.ScanResult[]);
+    method public abstract deprecated void onResults(android.net.wifi.ScanResult[]);
+    method public abstract void onResults(android.net.wifi.WifiScanner.ScanData[]);
   }
 
   public static class WifiScanner.ScanSettings implements android.os.Parcelable {
@@ -19661,6 +19872,7 @@
     method public void writeToParcel(android.os.Parcel, int);
     field public int band;
     field public android.net.wifi.WifiScanner.ChannelSpec[] channels;
+    field public int maxScansToCache;
     field public int numBssidsPerScan;
     field public int periodInMs;
     field public int reportEvents;
@@ -26861,6 +27073,7 @@
     field public static final java.lang.String DEBUG_APP = "debug_app";
     field public static final java.lang.String DEVELOPMENT_SETTINGS_ENABLED = "development_settings_enabled";
     field public static final java.lang.String DEVICE_PROVISIONED = "device_provisioned";
+    field public static final java.lang.String HIDE_CARRIER_NETWORK_SETTINGS = "hide_carrier_network_settings";
     field public static final java.lang.String HTTP_PROXY = "http_proxy";
     field public static final deprecated java.lang.String INSTALL_NON_MARKET_APPS = "install_non_market_apps";
     field public static final java.lang.String MODE_RINGER = "mode_ringer";
@@ -27027,6 +27240,7 @@
     field public static final android.net.Uri DEFAULT_RINGTONE_URI;
     field public static final deprecated java.lang.String DEVICE_PROVISIONED = "device_provisioned";
     field public static final deprecated java.lang.String DIM_SCREEN = "dim_screen";
+    field public static final java.lang.String DTMF_TONE_TYPE_WHEN_DIALING = "dtmf_tone_type";
     field public static final java.lang.String DTMF_TONE_WHEN_DIALING = "dtmf_tone";
     field public static final java.lang.String END_BUTTON_BEHAVIOR = "end_button_behavior";
     field public static final java.lang.String FONT_SCALE = "font_scale";
@@ -27075,6 +27289,7 @@
     field public static final java.lang.String USER_ROTATION = "user_rotation";
     field public static final deprecated java.lang.String USE_GOOGLE_MAIL = "use_google_mail";
     field public static final java.lang.String VIBRATE_ON = "vibrate_on";
+    field public static final java.lang.String VIBRATE_WHEN_RINGING = "vibrate_when_ringing";
     field public static final java.lang.String VOLUME_ALARM = "volume_alarm";
     field public static final java.lang.String VOLUME_BLUETOOTH_SCO = "volume_bluetooth_sco";
     field public static final java.lang.String VOLUME_MUSIC = "volume_music";
@@ -27468,6 +27683,8 @@
     field public static final int NOTIFICATION_CHANNEL_STATE_MESSAGE_WAITING = 2; // 0x2
     field public static final int NOTIFICATION_CHANNEL_STATE_NO_CONNECTION = 1; // 0x1
     field public static final int NOTIFICATION_CHANNEL_STATE_OK = 0; // 0x0
+    field public static final java.lang.String PHONE_ACCOUNT_COMPONENT_NAME = "phone_account_component_name";
+    field public static final java.lang.String PHONE_ACCOUNT_ID = "phone_account_id";
     field public static final java.lang.String SETTINGS_URI = "settings_uri";
     field public static final java.lang.String SOURCE_PACKAGE = "source_package";
     field public static final java.lang.String VOICEMAIL_ACCESS_URI = "voicemail_access_uri";
@@ -27477,6 +27694,8 @@
     method public static android.net.Uri buildSourceUri(java.lang.String);
     field public static final android.net.Uri CONTENT_URI;
     field public static final java.lang.String DATE = "date";
+    field public static final java.lang.String DELETED = "deleted";
+    field public static final java.lang.String DIRTY = "dirty";
     field public static final java.lang.String DIR_TYPE = "vnd.android.cursor.dir/voicemails";
     field public static final java.lang.String DURATION = "duration";
     field public static final java.lang.String HAS_CONTENT = "has_content";
@@ -27484,6 +27703,8 @@
     field public static final java.lang.String ITEM_TYPE = "vnd.android.cursor.item/voicemail";
     field public static final java.lang.String MIME_TYPE = "mime_type";
     field public static final java.lang.String NUMBER = "number";
+    field public static final java.lang.String PHONE_ACCOUNT_COMPONENT_NAME = "subscription_component_name";
+    field public static final java.lang.String PHONE_ACCOUNT_ID = "subscription_id";
     field public static final java.lang.String SOURCE_DATA = "source_data";
     field public static final java.lang.String SOURCE_PACKAGE = "source_package";
     field public static final java.lang.String TRANSCRIPTION = "transcription";
@@ -28501,6 +28722,26 @@
 
 package android.service.carrier {
 
+  public abstract class CarrierConfigService extends android.app.Service {
+    ctor public CarrierConfigService();
+    method public final android.os.IBinder onBind(android.content.Intent);
+    method public abstract android.os.Bundle onLoadConfig(android.service.carrier.CarrierIdentifier);
+    field public static final java.lang.String SERVICE_INTERFACE = "android.service.carrier.CarrierConfigService";
+  }
+
+  public class CarrierIdentifier implements android.os.Parcelable {
+    ctor public CarrierIdentifier(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
+    method public int describeContents();
+    method public java.lang.String getGid1();
+    method public java.lang.String getGid2();
+    method public java.lang.String getImsi();
+    method public java.lang.String getMcc();
+    method public java.lang.String getMnc();
+    method public java.lang.String getSpn();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.service.carrier.CarrierIdentifier> CREATOR;
+  }
+
   public abstract class CarrierMessagingService extends android.app.Service {
     ctor public CarrierMessagingService();
     method public android.os.IBinder onBind(android.content.Intent);
@@ -29880,6 +30121,7 @@
   public final class AudioState implements android.os.Parcelable {
     ctor public AudioState(boolean, int, int);
     ctor public AudioState(android.telecom.AudioState);
+    method public static java.lang.String audioRouteToString(int);
     method public int describeContents();
     method public int getRoute();
     method public int getSupportedRouteMask();
@@ -29891,9 +30133,6 @@
     field public static final int ROUTE_SPEAKER = 8; // 0x8
     field public static final int ROUTE_WIRED_HEADSET = 4; // 0x4
     field public static final int ROUTE_WIRED_OR_EARPIECE = 5; // 0x5
-    field public final boolean isMuted;
-    field public final int route;
-    field public final int supportedRouteMask;
   }
 
   public final class Call {
@@ -29908,6 +30147,7 @@
     method public android.telecom.Call getParent();
     method public java.lang.String getRemainingPostDialSequence();
     method public int getState();
+    method public android.telecom.InCallService.VideoCall getVideoCall();
     method public void hold();
     method public void mergeConference();
     method public void phoneAccountSelected(android.telecom.PhoneAccountHandle, boolean);
@@ -29919,6 +30159,7 @@
     method public void stopDtmfTone();
     method public void swapConference();
     method public void unhold();
+    field public static final java.lang.String AVAILABLE_PHONE_ACCOUNTS = "selectPhoneAccountAccounts";
     field public static final int STATE_ACTIVE = 4; // 0x4
     field public static final int STATE_CONNECTING = 9; // 0x9
     field public static final int STATE_DIALING = 1; // 0x1
@@ -29931,13 +30172,15 @@
   }
 
   public static class Call.Details {
+    method public static boolean can(int, int);
+    method public boolean can(int);
     method public static java.lang.String capabilitiesToString(int);
     method public android.telecom.PhoneAccountHandle getAccountHandle();
     method public int getCallCapabilities();
     method public int getCallProperties();
     method public java.lang.String getCallerDisplayName();
     method public int getCallerDisplayNamePresentation();
-    method public long getConnectTimeMillis();
+    method public final long getConnectTimeMillis();
     method public android.telecom.DisconnectCause getDisconnectCause();
     method public android.os.Bundle getExtras();
     method public android.telecom.GatewayInfo getGatewayInfo();
@@ -29946,14 +30189,24 @@
     method public android.telecom.StatusHints getStatusHints();
     method public int getVideoState();
     field public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 8192; // 0x2000
+    field public static final int CAPABILITY_GENERIC_CONFERENCE = 16384; // 0x4000
+    field public static final int CAPABILITY_HIGH_DEF_AUDIO = 32768; // 0x8000
     field public static final int CAPABILITY_HOLD = 1; // 0x1
     field public static final int CAPABILITY_MANAGE_CONFERENCE = 128; // 0x80
     field public static final int CAPABILITY_MERGE_CONFERENCE = 4; // 0x4
     field public static final int CAPABILITY_MUTE = 64; // 0x40
     field public static final int CAPABILITY_RESPOND_VIA_TEXT = 32; // 0x20
     field public static final int CAPABILITY_SEPARATE_FROM_CONFERENCE = 4096; // 0x1000
+    field public static final int CAPABILITY_SHOW_CALLBACK_NUMBER = 131072; // 0x20000
+    field public static final int CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL = 768; // 0x300
+    field public static final int CAPABILITY_SUPPORTS_VT_LOCAL_RX = 256; // 0x100
+    field public static final int CAPABILITY_SUPPORTS_VT_LOCAL_TX = 512; // 0x200
+    field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL = 3072; // 0xc00
+    field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_RX = 1024; // 0x400
+    field public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 2048; // 0x800
     field public static final int CAPABILITY_SUPPORT_HOLD = 2; // 0x2
     field public static final int CAPABILITY_SWAP_CONFERENCE = 8; // 0x8
+    field public static final int CAPABILITY_WIFI = 65536; // 0x10000
   }
 
   public static abstract class Call.Listener {
@@ -29966,6 +30219,12 @@
     method public void onParentChanged(android.telecom.Call, android.telecom.Call);
     method public void onPostDialWait(android.telecom.Call, java.lang.String);
     method public void onStateChanged(android.telecom.Call, int);
+    method public void onVideoCallChanged(android.telecom.Call, android.telecom.InCallService.VideoCall);
+  }
+
+  public class CallProperties {
+    ctor public CallProperties();
+    field public static final int CONFERENCE = 1; // 0x1
   }
 
   public final class CallState {
@@ -29982,13 +30241,22 @@
     field public static final int RINGING = 4; // 0x4
   }
 
+  public final class CameraCapabilities implements android.os.Parcelable {
+    ctor public CameraCapabilities(int, int);
+    method public int describeContents();
+    method public int getHeight();
+    method public int getWidth();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telecom.CameraCapabilities> CREATOR;
+  }
+
   public abstract class Conference implements android.telecom.IConferenceable {
     ctor public Conference(android.telecom.PhoneAccountHandle);
     method public final boolean addConnection(android.telecom.Connection);
     method public final void destroy();
     method public final android.telecom.AudioState getAudioState();
     method public final java.util.List<android.telecom.Connection> getConferenceableConnections();
-    method public long getConnectTimeMillis();
+    method public final long getConnectTimeMillis();
     method public final int getConnectionCapabilities();
     method public final java.util.List<android.telecom.Connection> getConnections();
     method public final android.telecom.DisconnectCause getDisconnectCause();
@@ -30015,8 +30283,7 @@
     method public final void setDisconnected(android.telecom.DisconnectCause);
     method public final void setOnHold();
     method public final void setStatusHints(android.telecom.StatusHints);
-    field public static long CONNECT_TIME_NOT_SPECIFIED;
-    field protected android.telecom.PhoneAccountHandle mPhoneAccount;
+    field public static final long CONNECT_TIME_NOT_SPECIFIED = 0L; // 0x0L
   }
 
   public abstract class Connection implements android.telecom.IConferenceable {
@@ -30029,7 +30296,6 @@
     method public final int getAddressPresentation();
     method public final boolean getAudioModeIsVoip();
     method public final android.telecom.AudioState getAudioState();
-    method public final deprecated int getCallCapabilities();
     method public final java.lang.String getCallerDisplayName();
     method public final int getCallerDisplayNamePresentation();
     method public final android.telecom.Conference getConference();
@@ -30038,8 +30304,8 @@
     method public final android.telecom.DisconnectCause getDisconnectCause();
     method public final int getState();
     method public final android.telecom.StatusHints getStatusHints();
+    method public final android.telecom.Connection.VideoProvider getVideoProvider();
     method public final boolean isRingbackRequested();
-    method protected void notifyConferenceStarted();
     method public void onAbort();
     method public void onAnswer();
     method public void onAudioStateChanged(android.telecom.AudioState);
@@ -30055,7 +30321,6 @@
     method public final void setActive();
     method public final void setAddress(android.net.Uri, int);
     method public final void setAudioModeIsVoip(boolean);
-    method public final deprecated void setCallCapabilities(int);
     method public final void setCallerDisplayName(java.lang.String, int);
     method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
     method public final void setConferenceables(java.util.List<android.telecom.IConferenceable>);
@@ -30089,6 +30354,38 @@
     field public static final int STATE_RINGING = 2; // 0x2
   }
 
+  public static abstract class Connection.VideoProvider {
+    ctor public Connection.VideoProvider();
+    method public void changeCallDataUsage(long);
+    method public void changeCameraCapabilities(android.telecom.CameraCapabilities);
+    method public void changePeerDimensions(int, int);
+    method public void changeVideoQuality(int);
+    method public void handleCallSessionEvent(int);
+    method public abstract void onRequestCameraCapabilities();
+    method public abstract void onRequestConnectionDataUsage();
+    method public abstract void onSendSessionModifyRequest(android.telecom.VideoProfile);
+    method public abstract void onSendSessionModifyResponse(android.telecom.VideoProfile);
+    method public abstract void onSetCamera(java.lang.String);
+    method public abstract void onSetDeviceOrientation(int);
+    method public abstract void onSetDisplaySurface(android.view.Surface);
+    method public abstract void onSetPauseImage(java.lang.String);
+    method public abstract void onSetPreviewSurface(android.view.Surface);
+    method public abstract void onSetZoom(float);
+    method public void receiveSessionModifyRequest(android.telecom.VideoProfile);
+    method public void receiveSessionModifyResponse(int, android.telecom.VideoProfile, android.telecom.VideoProfile);
+    field public static final int SESSION_EVENT_CAMERA_FAILURE = 5; // 0x5
+    field public static final int SESSION_EVENT_CAMERA_READY = 6; // 0x6
+    field public static final int SESSION_EVENT_RX_PAUSE = 1; // 0x1
+    field public static final int SESSION_EVENT_RX_RESUME = 2; // 0x2
+    field public static final int SESSION_EVENT_TX_START = 3; // 0x3
+    field public static final int SESSION_EVENT_TX_STOP = 4; // 0x4
+    field public static final int SESSION_MODIFY_REQUEST_FAIL = 2; // 0x2
+    field public static final int SESSION_MODIFY_REQUEST_INVALID = 3; // 0x3
+    field public static final int SESSION_MODIFY_REQUEST_REJECTED_BY_REMOTE = 5; // 0x5
+    field public static final int SESSION_MODIFY_REQUEST_SUCCESS = 1; // 0x1
+    field public static final int SESSION_MODIFY_REQUEST_TIMED_OUT = 4; // 0x4
+  }
+
   public final class ConnectionRequest implements android.os.Parcelable {
     ctor public ConnectionRequest(android.telecom.PhoneAccountHandle, android.net.Uri, android.os.Bundle);
     method public int describeContents();
@@ -30158,13 +30455,40 @@
 
   public abstract class InCallService extends android.app.Service {
     ctor public InCallService();
-    method public android.telecom.Phone getPhone();
+    method public final android.telecom.Phone getPhone();
     method public android.os.IBinder onBind(android.content.Intent);
     method public void onPhoneCreated(android.telecom.Phone);
     method public void onPhoneDestroyed(android.telecom.Phone);
     field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.InCallService";
   }
 
+  public static abstract class InCallService.VideoCall {
+    ctor public InCallService.VideoCall();
+    method public abstract void removeVideoCallListener();
+    method public abstract void requestCallDataUsage();
+    method public abstract void requestCameraCapabilities();
+    method public abstract void sendSessionModifyRequest(android.telecom.VideoProfile);
+    method public abstract void sendSessionModifyResponse(android.telecom.VideoProfile);
+    method public abstract void setCamera(java.lang.String);
+    method public abstract void setDeviceOrientation(int);
+    method public abstract void setDisplaySurface(android.view.Surface);
+    method public abstract void setPauseImage(java.lang.String);
+    method public abstract void setPreviewSurface(android.view.Surface);
+    method public abstract void setVideoCallListener(android.telecom.InCallService.VideoCall.Listener);
+    method public abstract void setZoom(float);
+  }
+
+  public static abstract class InCallService.VideoCall.Listener {
+    ctor public InCallService.VideoCall.Listener();
+    method public abstract void onCallDataUsageChanged(long);
+    method public abstract void onCallSessionEvent(int);
+    method public abstract void onCameraCapabilitiesChanged(android.telecom.CameraCapabilities);
+    method public abstract void onPeerDimensionsChanged(int, int);
+    method public abstract void onSessionModifyRequestReceived(android.telecom.VideoProfile);
+    method public abstract void onSessionModifyResponseReceived(int, android.telecom.VideoProfile, android.telecom.VideoProfile);
+    method public abstract void onVideoQualityChanged(int);
+  }
+
   public final class Phone {
     method public final void addListener(android.telecom.Phone.Listener);
     method public final boolean canAddCall();
@@ -30173,8 +30497,6 @@
     method public final void removeListener(android.telecom.Phone.Listener);
     method public final void setAudioRoute(int);
     method public final void setMuted(boolean);
-    method public final void setProximitySensorOff(boolean);
-    method public final void setProximitySensorOn();
   }
 
   public static abstract class Phone.Listener {
@@ -30204,10 +30526,14 @@
     method public java.util.List<java.lang.String> getSupportedUriSchemes();
     method public boolean hasCapabilities(int);
     method public boolean supportsUriScheme(java.lang.String);
+    method public android.telecom.PhoneAccount.Builder toBuilder();
     method public void writeToParcel(android.os.Parcel, int);
+    field public static final int CAPABILITY_CALL_PROVIDER = 2; // 0x2
     field public static final int CAPABILITY_CONNECTION_MANAGER = 1; // 0x1
+    field public static final int CAPABILITY_MULTI_USER = 32; // 0x20
     field public static final int CAPABILITY_PLACE_EMERGENCY_CALLS = 16; // 0x10
     field public static final int CAPABILITY_SIM_SUBSCRIPTION = 4; // 0x4
+    field public static final int CAPABILITY_VIDEO_CALLING = 8; // 0x8
     field public static final android.os.Parcelable.Creator<android.telecom.PhoneAccount> CREATOR;
     field public static final int NO_HIGHLIGHT_COLOR = 0; // 0x0
     field public static final int NO_ICON_TINT = 0; // 0x0
@@ -30220,6 +30546,7 @@
   public static class PhoneAccount.Builder {
     ctor public PhoneAccount.Builder(android.telecom.PhoneAccountHandle, java.lang.CharSequence);
     ctor public PhoneAccount.Builder(android.telecom.PhoneAccount);
+    method public android.telecom.PhoneAccount.Builder addSupportedUriScheme(java.lang.String);
     method public android.telecom.PhoneAccount build();
     method public android.telecom.PhoneAccount.Builder setAddress(android.net.Uri);
     method public android.telecom.PhoneAccount.Builder setCapabilities(int);
@@ -30236,9 +30563,11 @@
 
   public class PhoneAccountHandle implements android.os.Parcelable {
     ctor public PhoneAccountHandle(android.content.ComponentName, java.lang.String);
+    ctor public PhoneAccountHandle(android.content.ComponentName, java.lang.String, android.os.UserHandle);
     method public int describeContents();
     method public android.content.ComponentName getComponentName();
     method public java.lang.String getId();
+    method public android.os.UserHandle getUserHandle();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.telecom.PhoneAccountHandle> CREATOR;
   }
@@ -30334,12 +30663,14 @@
     method public void addNewIncomingCall(android.telecom.PhoneAccountHandle, android.os.Bundle);
     method public void addNewUnknownCall(android.telecom.PhoneAccountHandle, android.os.Bundle);
     method public void cancelMissedCallsNotification();
-    method public void clearAccounts();
+    method public deprecated void clearAccounts();
+    method public void clearPhoneAccounts();
     method public boolean endCall();
     method public android.net.Uri getAdnUriForPhoneAccount(android.telecom.PhoneAccountHandle);
     method public java.util.List<android.telecom.PhoneAccountHandle> getAllPhoneAccountHandles();
     method public java.util.List<android.telecom.PhoneAccount> getAllPhoneAccounts();
     method public int getAllPhoneAccountsCount();
+    method public java.util.List<android.telecom.PhoneAccountHandle> getCallCapablePhoneAccounts();
     method public int getCallState();
     method public android.telecom.PhoneAccountHandle getConnectionManager();
     method public android.telecom.PhoneAccountHandle getDefaultOutgoingPhoneAccount(java.lang.String);
@@ -30349,10 +30680,10 @@
     method public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsForPackage();
     method public java.util.List<android.telecom.PhoneAccountHandle> getPhoneAccountsSupportingScheme(java.lang.String);
     method public java.util.List<android.telecom.PhoneAccountHandle> getRegisteredConnectionManagers();
+    method public android.telecom.PhoneAccountHandle getSimCallManager();
+    method public java.lang.String getVoiceMailNumber(android.telecom.PhoneAccountHandle);
     method public boolean handleMmi(java.lang.String);
-    method public boolean handleMmi(android.telecom.PhoneAccountHandle, java.lang.String);
-    method public boolean hasMultipleCallCapableAccounts();
-    method public boolean hasVoiceMailNumber(android.telecom.PhoneAccountHandle);
+    method public boolean handleMmi(java.lang.String, android.telecom.PhoneAccountHandle);
     method public boolean isInCall();
     method public boolean isRinging();
     method public boolean isTtySupported();
@@ -30363,6 +30694,8 @@
     method public void unregisterPhoneAccount(android.telecom.PhoneAccountHandle);
     field public static final java.lang.String ACTION_CHANGE_PHONE_ACCOUNTS = "android.telecom.action.CHANGE_PHONE_ACCOUNTS";
     field public static final java.lang.String ACTION_CONNECTION_SERVICE_CONFIGURE = "android.telecom.action.CONNECTION_SERVICE_CONFIGURE";
+    field public static final java.lang.String ACTION_INCOMING_CALL = "android.telecom.action.INCOMING_CALL";
+    field public static final java.lang.String ACTION_PHONE_ACCOUNT_REGISTERED = "android.telecom.action.PHONE_ACCOUNT_REGISTERED";
     field public static final java.lang.String ACTION_SHOW_CALL_SETTINGS = "android.telecom.action.SHOW_CALL_SETTINGS";
     field public static final char DTMF_CHARACTER_PAUSE = 44; // 0x002c ','
     field public static final char DTMF_CHARACTER_WAIT = 59; // 0x003b ';'
@@ -30374,6 +30707,7 @@
     field public static final java.lang.String EXTRA_OUTGOING_CALL_EXTRAS = "android.telecom.extra.OUTGOING_CALL_EXTRAS";
     field public static final java.lang.String EXTRA_PHONE_ACCOUNT_HANDLE = "android.telecom.extra.PHONE_ACCOUNT_HANDLE";
     field public static final java.lang.String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.telecom.extra.START_CALL_WITH_SPEAKERPHONE";
+    field public static final java.lang.String EXTRA_START_CALL_WITH_VIDEO_STATE = "android.telecom.extra.START_CALL_WITH_VIDEO_STATE";
     field public static final java.lang.String GATEWAY_ORIGINAL_ADDRESS = "android.telecom.extra.GATEWAY_ORIGINAL_ADDRESS";
     field public static final java.lang.String GATEWAY_PROVIDER_PACKAGE = "android.telecom.extra.GATEWAY_PROVIDER_PACKAGE";
     field public static final int PRESENTATION_ALLOWED = 1; // 0x1
@@ -30382,10 +30716,57 @@
     field public static final int PRESENTATION_UNKNOWN = 3; // 0x3
   }
 
+  public class VideoProfile implements android.os.Parcelable {
+    ctor public VideoProfile(int);
+    ctor public VideoProfile(int, int);
+    method public int describeContents();
+    method public int getQuality();
+    method public int getVideoState();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final android.os.Parcelable.Creator<android.telecom.VideoProfile> CREATOR;
+    field public static final int QUALITY_DEFAULT = 4; // 0x4
+    field public static final int QUALITY_HIGH = 1; // 0x1
+    field public static final int QUALITY_LOW = 3; // 0x3
+    field public static final int QUALITY_MEDIUM = 2; // 0x2
+  }
+
+  public static class VideoProfile.VideoState {
+    ctor public VideoProfile.VideoState();
+    method public static boolean isAudioOnly(int);
+    method public static boolean isBidirectional(int);
+    method public static boolean isPaused(int);
+    method public static boolean isReceptionEnabled(int);
+    method public static boolean isTransmissionEnabled(int);
+    field public static final int AUDIO_ONLY = 0; // 0x0
+    field public static final int BIDIRECTIONAL = 3; // 0x3
+    field public static final int PAUSED = 4; // 0x4
+    field public static final int RX_ENABLED = 2; // 0x2
+    field public static final int TX_ENABLED = 1; // 0x1
+  }
+
 }
 
 package android.telephony {
 
+  public class CarrierConfigManager {
+    ctor public CarrierConfigManager();
+    method public android.os.Bundle getConfig();
+    method public android.os.Bundle getConfigForSubId(int);
+    method public static android.os.Bundle getDefaultConfig();
+    method public void reloadCarrierConfigForSubId(int);
+    method public void updateConfigForPhoneId(int, java.lang.String);
+    field public static final java.lang.String ACTION_CARRIER_CONFIG_CHANGED = "android.intent.action.carrier_config_changed";
+    field public static final java.lang.String BOOL_CARRIER_VOLTE_AVAILABLE = "bool_carrier_volte_available";
+    field public static final java.lang.String BOOL_CARRIER_VOLTE_PROVISIONED = "bool_carrier_volte_provisioned";
+    field public static final java.lang.String BOOL_CARRIER_VOLTE_TTY_SUPPORTED = "bool_carrier_volte_tty_supported";
+    field public static final java.lang.String BOOL_SHOW_APN_SETTING_CDMA = "bool_show_apn_setting_cdma";
+    field public static final java.lang.String INT_VOLTE_REPLACEMENT_RAT = "int_volte_replacement_rat";
+    field public static final java.lang.String SHORT_VVM_PORT_NUMBER = "string_vvm_port_number";
+    field public static final java.lang.String STRING_VVM_DESTINATION_NUMBER = "string_vvm_destination_number";
+    field public static final java.lang.String STRING_VVM_TYPE = "string_vvm_type";
+    field public static final java.lang.String VVM_TYPE_OMTP = "vvm_type_omtp";
+  }
+
   public final class CellIdentityCdma implements android.os.Parcelable {
     method public int describeContents();
     method public int getBasestationId();
@@ -30575,6 +30956,7 @@
 
   public class PhoneNumberUtils {
     ctor public PhoneNumberUtils();
+    method public static void addPhoneTtsSpan(android.text.Spannable, int, int);
     method public static java.lang.String calledPartyBCDFragmentToString(byte[], int, int);
     method public static java.lang.String calledPartyBCDToString(byte[], int, int);
     method public static boolean compare(java.lang.String, java.lang.String);
@@ -30591,6 +30973,8 @@
     method public static java.lang.String formatNumberToE164(java.lang.String, java.lang.String);
     method public static deprecated int getFormatTypeForLocale(java.util.Locale);
     method public static java.lang.String getNumberFromIntent(android.content.Intent, android.content.Context);
+    method public static android.text.style.TtsSpan getPhoneTtsSpan(java.lang.String);
+    method public static java.lang.CharSequence getPhoneTtsSpannable(java.lang.CharSequence);
     method public static java.lang.String getStrippedReversed(java.lang.String);
     method public static final boolean is12Key(char);
     method public static final boolean isDialable(char);
@@ -30870,6 +31254,7 @@
     method public boolean getDataEnabled(int);
     method public int getDataState();
     method public java.lang.String getDeviceId();
+    method public java.lang.String getDeviceId(int);
     method public java.lang.String getDeviceSoftwareVersion();
     method public java.lang.String getGroupIdLevel1();
     method public java.lang.String getLine1Number();
@@ -30880,6 +31265,7 @@
     method public java.lang.String getNetworkOperator();
     method public java.lang.String getNetworkOperatorName();
     method public int getNetworkType();
+    method public int getPhoneCount();
     method public int getPhoneType();
     method public java.lang.String getSimCountryIso();
     method public java.lang.String getSimOperator();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index f2be45c..c3a1555 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -849,10 +849,10 @@
         }
 
         public void setHttpProxy(String host, String port, String exclList, Uri pacFileUrl) {
-            final Network network = ConnectivityManager.getProcessDefaultNetwork();
+            final ConnectivityManager cm = ConnectivityManager.from(getSystemContext());
+            final Network network = cm.getBoundNetworkForProcess();
             if (network != null) {
-                Proxy.setHttpProxySystemProperty(
-                        ConnectivityManager.from(getSystemContext()).getDefaultProxy());
+                Proxy.setHttpProxySystemProperty(cm.getDefaultProxy());
             } else {
                 Proxy.setHttpProxySystemProperty(host, port, exclList, pacFileUrl);
             }
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 2ef046d..641f5d2 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -125,6 +125,7 @@
 import android.service.fingerprint.IFingerprintService;
 import android.service.fingerprint.FingerprintManager;
 import android.telecom.TelecomManager;
+import android.telephony.CarrierConfigManager;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.content.ClipboardManager;
@@ -768,6 +769,12 @@
                 IBinder b = ServiceManager.getService(APPWIDGET_SERVICE);
                 return new AppWidgetManager(ctx, IAppWidgetService.Stub.asInterface(b));
             }});
+
+        registerService(CARRIER_CONFIG_SERVICE, new ServiceFetcher() {
+            public Object createService(ContextImpl ctx) {
+                return new CarrierConfigManager();
+            }
+        });
     }
 
     static ContextImpl getImpl(Context context) {
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index b8f4bf8..875aef6 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2009-2014 The Android Open Source Project
+ * Copyright (C) 2009-2015 The Android Open Source Project
+ * Copyright (C) 2015 Samsung LSI
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -18,6 +19,7 @@
 
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemApi;
 import android.bluetooth.le.BluetoothLeAdvertiser;
 import android.bluetooth.le.BluetoothLeScanner;
 import android.bluetooth.le.ScanCallback;
@@ -32,6 +34,9 @@
 import android.os.ParcelUuid;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.app.ActivityThread;
+import android.os.SystemProperties;
+import android.os.Binder;
 import android.util.Log;
 import android.util.Pair;
 
@@ -154,6 +159,24 @@
     public static final int STATE_TURNING_OFF = 13;
 
     /**
+     * Indicates the local Bluetooth adapter is turning Bluetooth LE mode on.
+     * @hide
+     */
+    public static final int STATE_BLE_TURNING_ON = 14;
+
+    /**
+     * Indicates the local Bluetooth adapter is in LE only mode.
+     * @hide
+     */
+    public static final int STATE_BLE_ON = 15;
+
+    /**
+     * Indicates the local Bluetooth adapter is turning off LE only mode.
+     * @hide
+     */
+    public static final int STATE_BLE_TURNING_OFF = 16;
+
+    /**
      * Activity Action: Show a system activity that requests discoverable mode.
      * This activity will also request the user to turn on Bluetooth if it
      * is not currently enabled.
@@ -209,6 +232,23 @@
             "android.bluetooth.adapter.action.REQUEST_ENABLE";
 
     /**
+     * Activity Action: Show a system activity that allows user to enable BLE scans even when
+     * Bluetooth is turned off.<p>
+     *
+     * Notification of result of this activity is posted using
+     * {@link android.app.Activity#onActivityResult}. The <code>resultCode</code> will be
+     * {@link android.app.Activity#RESULT_OK} if BLE scan always available setting is turned on or
+     * {@link android.app.Activity#RESULT_CANCELED} if the user has rejected the request or an
+     * error occurred.
+     *
+     * @hide
+     */
+    @SystemApi
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_REQUEST_BLE_SCAN_ALWAYS_AVAILABLE =
+            "android.bluetooth.adapter.action.REQUEST_BLE_SCAN_ALWAYS_AVAILABLE";
+
+    /**
      * Broadcast Action: Indicates the Bluetooth scan mode of the local Adapter
      * has changed.
      * <p>Always contains the extra fields {@link #EXTRA_SCAN_MODE} and {@link
@@ -347,6 +387,39 @@
     public static final String EXTRA_PREVIOUS_CONNECTION_STATE =
           "android.bluetooth.adapter.extra.PREVIOUS_CONNECTION_STATE";
 
+    /**
+     * Broadcast Action: The Bluetooth adapter state has changed in LE only mode.
+     * @hide
+     */
+    public static final String ACTION_BLE_STATE_CHANGED =
+        "anrdoid.bluetooth.adapter.action.BLE_STATE_CHANGED";
+
+    /**
+     * Broadcast Action: The notifys Bluetooth ACL connected event. This will be
+     * by BLE Always on enabled application to know the ACL_CONNECTED event
+     * when Bluetooth state in STATE_BLE_ON. This denotes GATT connection
+     * as Bluetooth LE is the only feature available in STATE_BLE_ON
+     *
+     * This is counterpart of {@link BluetoothDevice#ACTION_ACL_CONNECTED} which
+     * works in Bluetooth state STATE_ON
+     * @hide
+     */
+    public static final String ACTION_BLE_ACL_CONNECTED =
+        "android.bluetooth.adapter.action.BLE_ACL_CONNECTED";
+
+    /**
+     * Broadcast Action: The notifys Bluetooth ACL connected event. This will be
+     * by BLE Always on enabled application to know the ACL_DISCONNECTED event
+     * when Bluetooth state in STATE_BLE_ON. This denotes GATT disconnection as Bluetooth
+     * LE is the only feature available in STATE_BLE_ON
+     *
+     * This is counterpart of {@link BluetoothDevice#ACTION_ACL_DISCONNECTED} which
+     * works in Bluetooth state STATE_ON
+     * @hide
+     */
+    public static final String ACTION_BLE_ACL_DISCONNECTED =
+        "android.bluetooth.adapter.action.BLE_ACL_DISCONNECTED";
+
     /** The profile is in disconnected state */
     public static final int STATE_DISCONNECTED  = 0;
     /** The profile is in connecting state */
@@ -358,6 +431,19 @@
 
     /** @hide */
     public static final String BLUETOOTH_MANAGER_SERVICE = "bluetooth_manager";
+    private final IBinder mToken;
+
+
+    /** When creating a ServerSocket using listenUsingRfcommOn() or
+     *  listenUsingL2capOn() use SOCKET_CHANNEL_AUTO_STATIC to create
+     *  a ServerSocket that auto assigns a channel number to the first
+     *  bluetooth socket.
+     *  The channel number assigned to this first Bluetooth Socket will
+     *  be stored in the ServerSocket, and reused for subsequent Bluetooth
+     *  sockets.
+     * @hide */
+    public static final int SOCKET_CHANNEL_AUTO_STATIC_NO_SDP = -2;
+
 
     private static final int ADDRESS_LENGTH = 17;
 
@@ -416,6 +502,7 @@
         } catch (RemoteException e) {Log.e(TAG, "", e);}
         mManagerService = managerService;
         mLeScanClients = new HashMap<LeScanCallback, ScanCallback>();
+        mToken = new Binder();
     }
 
     /**
@@ -462,11 +549,9 @@
      * on this device before calling this method.
      */
     public BluetoothLeAdvertiser getBluetoothLeAdvertiser() {
-        if (getState() != STATE_ON) {
-            return null;
-        }
+        if (!getLeAccess()) return null;
         if (!isMultipleAdvertisementSupported() && !isPeripheralModeSupported()) {
-            Log.e(TAG, "bluetooth le advertising not supported");
+            Log.e(TAG, "Bluetooth LE advertising not supported");
             return null;
         }
         synchronized(mLock) {
@@ -481,9 +566,7 @@
      * Returns a {@link BluetoothLeScanner} object for Bluetooth LE scan operations.
      */
     public BluetoothLeScanner getBluetoothLeScanner() {
-        if (getState() != STATE_ON) {
-            return null;
-        }
+        if (!getLeAccess()) return null;
         synchronized(mLock) {
             if (sBluetoothLeScanner == null) {
                 sBluetoothLeScanner = new BluetoothLeScanner(mManagerService);
@@ -501,7 +584,6 @@
      * @return true if the local adapter is turned on
      */
     public boolean isEnabled() {
-
         try {
             synchronized(mManagerCallback) {
                 if (mService != null) return mService.isEnabled();
@@ -511,6 +593,178 @@
     }
 
     /**
+     * Return true if Bluetooth LE(Always BLE On feature) is currently
+     * enabled and ready for use
+     * <p>This returns true if current state is either STATE_ON or STATE_BLE_ON
+     *
+     * @return true if the local Bluetooth LE adapter is turned on
+     * @hide
+     */
+     public boolean isLeEnabled() {
+        final int state = getLeState();
+        if (state == BluetoothAdapter.STATE_ON) {
+            if (DBG) Log.d (TAG, "STATE_ON");
+        } else if (state == BluetoothAdapter.STATE_BLE_ON) {
+            if (DBG) Log.d (TAG, "STATE_BLE_ON");
+        } else {
+            if (DBG) Log.d (TAG, "STATE_OFF");
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Performs action based on user action to turn BT ON
+     * or OFF if BT is in BLE_ON state
+     */
+    private void notifyUserAction(boolean enable) {
+        if (mService == null) {
+            Log.e(TAG, "mService is null");
+            return;
+        }
+
+        try {
+            if (enable) {
+                mService.onLeServiceUp(); //NA:TODO implementation pending
+            } else {
+                mService.onBrEdrDown(); //NA:TODO implementation pending
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+        }
+    }
+
+    /**
+     * Returns true if LE only mode is enabled, that is apps
+     * have authorization to turn only BT ON and the calling
+     * app has privilage to do so
+     */
+    private boolean isLEAlwaysOnEnabled() {
+        boolean ret = false;
+        if (SystemProperties.getBoolean("ro.bluetooth.blealwayson", true) == true) {
+            Log.v(TAG, "LE always on mode is enabled");
+            // TODO: System API authorization check
+            ret = true;
+        } else {
+            Log.v(TAG, "LE always on mode is disabled");
+            ret = false;
+        }
+        return ret;
+    }
+
+    /**
+     * Turns off Bluetooth LE which was earlier turned on by calling EnableBLE().
+     *
+     * <p> If the internal Adapter state is STATE_BLE_ON, this would trigger the transition
+     * to STATE_OFF and completely shut-down Bluetooth
+     *
+     * <p> If the Adapter state is STATE_ON, This would unregister the existance of
+     * special Bluetooth LE application and hence the further turning off of Bluetooth
+     * from UI would ensure the complete turn-off of Bluetooth rather than staying back
+     * BLE only state
+     *
+     * <p>This is an asynchronous call: it will return immediately, and
+     * clients should listen for {@link #ACTION_BLE_STATE_CHANGED}
+     * to be notified of subsequent adapter state changes If this call returns
+     * true, then the adapter state will immediately transition from {@link
+     * #STATE_ON} to {@link #STATE_TURNING_OFF}, and some time
+     * later transition to either {@link #STATE_BLE_ON} or {@link
+     * #STATE_OFF} based on the existance of the further Always BLE ON enabled applications
+     * If this call returns false then there was an
+     * immediate problem that will prevent the QAdapter from being turned off -
+     * such as the QAadapter already being turned off.
+     *
+     * @return true to indicate success, or false on
+     *         immediate error
+     * @hide
+     */
+    public boolean disableBLE() {
+        if (isLEAlwaysOnEnabled() != true) return false;
+
+        int state = getLeState();
+        if (state == BluetoothAdapter.STATE_ON) {
+            if (DBG) Log.d (TAG, "STATE_ON: shouldn't disable");
+            try {
+                mManagerService.updateBleAppCount(mToken, false);
+            } catch (RemoteException e) {
+                Log.e(TAG, "", e);
+            }
+            return true;
+
+        } else if (state == BluetoothAdapter.STATE_BLE_ON) {
+            if (DBG) Log.d (TAG, "STATE_BLE_ON");
+            int bleAppCnt = 0;
+            try {
+                bleAppCnt = mManagerService.updateBleAppCount(mToken, false);
+            } catch (RemoteException e) {
+                Log.e(TAG, "", e);
+            }
+            if (bleAppCnt == 0) {
+                // Disable only if there are no other clients
+                notifyUserAction(false);
+            }
+            return true;
+        }
+
+        if (DBG) Log.d (TAG, "STATE_OFF: Already disabled");
+        return false;
+    }
+
+    /**
+     * Special Applications who want to only turn on Bluetooth Low Energy (BLE) would
+     * EnableBLE, EnableBLE brings-up Bluetooth so that application can access
+     * only LE related feature (Bluetooth GATT layers interfaces using the respective class)
+     * EnableBLE in turn registers the existance of a special App which wants to
+     * turn on Bluetooth Low enrgy part without making it visible at the settings UI
+     * as Bluetooth ON.
+     * <p>Invoking EnableBLE when Bluetooth is already in ON state, would just registers
+     * the existance of special Application and doesn't do anything to current BT state.
+     * when user turn OFF Bluetooth from UI, if there is an existance of special app, Bluetooth
+     * would stay in BLE_ON state so that LE features are still acessible to the special
+     * Applications.
+     *
+     * <p>This is an asynchronous call: it will return immediately, and
+     * clients should listen for {@link #ACTION_BLE_STATE_CHANGED}
+     * to be notified of subsequent adapter state changes. If this call returns
+     * true, then the adapter state will immediately transition from {@link
+     * #STATE_OFF} to {@link #STATE_BLE_TURNING_ON}, and some time
+     * later transition to either {@link #STATE_OFF} or {@link
+     * #STATE_BLE_ON}. If this call returns false then there was an
+     * immediate problem that will prevent the adapter from being turned on -
+     * such as Airplane mode, or the adapter is already turned on.
+     * (@link #ACTION_BLE_STATE_CHANGED) returns the Bluetooth Adapter's various
+     * states, It includes all the classic Bluetooth Adapter states along with
+     * internal BLE only states
+     *
+     * @return true to indicate Bluetooth LE start-up has begun, or false on
+     *         immediate error
+     * @hide
+     */
+    public boolean enableBLE() {
+        if (isLEAlwaysOnEnabled() != true) return false;
+
+        if (isLeEnabled() == true) {
+            if (DBG) Log.d(TAG, "enableBLE(): BT is already enabled..!");
+            try {
+                mManagerService.updateBleAppCount(mToken, true);
+            } catch (RemoteException e) {
+                Log.e(TAG, "", e);
+            }
+            return true;
+        }
+
+        try {
+            if (DBG) Log.d(TAG, "Calling enableBLE");
+            mManagerService.updateBleAppCount(mToken, true);
+            return mManagerService.enable();
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+        }
+
+        return false;
+    }
+
+    /**
      * Get the current state of the local Bluetooth adapter.
      * <p>Possible return values are
      * {@link #STATE_OFF},
@@ -528,6 +782,13 @@
                 {
                     int state=  mService.getState();
                     if (VDBG) Log.d(TAG, "" + hashCode() + ": getState(). Returning " + state);
+                    //consider all internal states as OFF
+                    if (state == BluetoothAdapter.STATE_BLE_ON
+                        || state == BluetoothAdapter.STATE_BLE_TURNING_ON
+                        || state == BluetoothAdapter.STATE_BLE_TURNING_OFF) {
+                        if (VDBG) Log.d(TAG, "Consider internal state as OFF");
+                        state = BluetoothAdapter.STATE_OFF;
+                    }
                     return state;
                 }
                 // TODO(BT) there might be a small gap during STATE_TURNING_ON that
@@ -539,6 +800,49 @@
     }
 
     /**
+     * Get the current state of the local Bluetooth adapter
+     * <p>This returns current internal state of Adapter including LE ON/OFF
+     *
+     * <p>Possible return values are
+     * {@link #STATE_OFF},
+     * {@link #STATE_BLE_TURNING_ON},
+     * {@link #STATE_BLE_ON},
+     * {@link #STATE_TURNING_ON},
+     * {@link #STATE_ON},
+     * {@link #STATE_TURNING_OFF},
+     * {@link #STATE_BLE_TURNING_OFF}.
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+     *
+     * @return current state of Bluetooth adapter
+     * @hide
+     */
+    public int getLeState() {
+        try {
+            synchronized(mManagerCallback) {
+                if (mService != null)
+                {
+                    int state=  mService.getState();
+                    if (VDBG) Log.d(TAG,"getLeState() returning " + state);
+                    return state;
+                }
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+        }
+        return BluetoothAdapter.STATE_OFF;
+    }
+
+    boolean getLeAccess() {
+        if(getLeState() == STATE_ON)
+            return true;
+
+        else if (getLeState() == STATE_BLE_ON)
+            return true; // TODO: FILTER SYSTEM APPS HERE <--
+
+        return false;
+    }
+
+    /**
      * Turn on the local Bluetooth adapter&mdash;do not use without explicit
      * user action to turn on Bluetooth.
      * <p>This powers on the underlying Bluetooth hardware, and starts all
@@ -566,10 +870,23 @@
      *         immediate error
      */
     public boolean enable() {
+        int state = STATE_OFF;
         if (isEnabled() == true){
             if (DBG) Log.d(TAG, "enable(): BT is already enabled..!");
             return true;
         }
+        //Use service interface to get the exact state
+        if (mService != null) {
+            try {
+               state = mService.getState();
+            } catch (RemoteException e) {Log.e(TAG, "", e);}
+        }
+
+        if (state == BluetoothAdapter.STATE_BLE_ON) {
+                Log.e(TAG, "BT is in BLE_ON State");
+                notifyUserAction(true);
+                return true;
+        }
         try {
             return mManagerService.enable();
         } catch (RemoteException e) {Log.e(TAG, "", e);}
@@ -919,6 +1236,22 @@
     }
 
     /**
+     * Returns {@code true} if BLE scan is always available, {@code false} otherwise. <p>
+     *
+     * If this returns {@code true}, application can issue {@link BluetoothLeScanner#startScan} and
+     * fetch scan results even when Bluetooth is turned off.<p>
+     *
+     * To change this setting, use {@link #ACTION_REQUEST_BLE_SCAN_ALWAYS_AVAILABLE}.
+     *
+     * @hide
+     */
+    @SystemApi
+    public boolean isBleScanAlwaysAvailable() {
+        // TODO: implement after Settings UI change.
+        return false;
+    }
+
+    /**
      * Returns whether peripheral mode is supported.
      *
      * @hide
@@ -964,6 +1297,24 @@
     }
 
     /**
+     * Return true if hardware has entries available for matching beacons
+     *
+     * @return true if there are hw entries available for matching beacons
+     * @hide
+     */
+    public boolean isHardwareTrackingFiltersAvailable() {
+        if (getState() != STATE_ON) return false;
+        try {
+            synchronized(mManagerCallback) {
+                if(mService != null) return (mService.numOfHwTrackFiltersAvailable() != 0);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+        }
+        return false;
+    }
+
+    /**
      * Return the record of {@link BluetoothActivityEnergyInfo} object that
      * has the activity and energy info. This can be used to ascertain what
      * the controller has been up to, since the last sample.
@@ -1092,6 +1443,9 @@
         BluetoothServerSocket socket = new BluetoothServerSocket(
                 BluetoothSocket.TYPE_RFCOMM, true, true, channel);
         int errno = socket.mSocket.bindListen();
+        if(channel == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
+            socket.setChannel(socket.mSocket.getPort());
+        }
         if (errno != 0) {
             //TODO(BT): Throw the same exception error code
             // that the previous code was using.
@@ -1226,6 +1580,9 @@
         BluetoothServerSocket socket = new BluetoothServerSocket(
                 BluetoothSocket.TYPE_RFCOMM, false, false, port);
         int errno = socket.mSocket.bindListen();
+        if(port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
+            socket.setChannel(socket.mSocket.getPort());
+        }
         if (errno != 0) {
             //TODO(BT): Throw the same exception error code
             // that the previous code was using.
@@ -1248,6 +1605,9 @@
         BluetoothServerSocket socket = new BluetoothServerSocket(
                 BluetoothSocket.TYPE_RFCOMM, false, true, port);
         int errno = socket.mSocket.bindListen();
+        if(port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
+            socket.setChannel(socket.mSocket.getPort());
+        }
         if (errno < 0) {
             //TODO(BT): Throw the same exception error code
             // that the previous code was using.
@@ -1278,6 +1638,30 @@
     }
 
     /**
+     * Construct an encrypted, authenticated, L2CAP server socket.
+     * Call #accept to retrieve connections to this socket.
+     * @return An L2CAP BluetoothServerSocket
+     * @throws IOException On error, for example Bluetooth not available, or
+     *                     insufficient permissions.
+     * @hide
+     */
+    public BluetoothServerSocket listenUsingL2capOn(int port) throws IOException {
+        BluetoothServerSocket socket = new BluetoothServerSocket(
+                BluetoothSocket.TYPE_L2CAP, true, true, port);
+        int errno = socket.mSocket.bindListen();
+        if(port == SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
+            socket.setChannel(socket.mSocket.getPort());
+        }
+        if (errno != 0) {
+            //TODO(BT): Throw the same exception error code
+            // that the previous code was using.
+            //socket.mSocket.throwErrnoNative(errno);
+            throw new IOException("Error: " + errno);
+        }
+        return socket;
+    }
+
+    /**
      * Read the local Out of Band Pairing Data
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
      *
@@ -1356,6 +1740,9 @@
         } else if (profile == BluetoothProfile.HEADSET_CLIENT) {
             BluetoothHeadsetClient headsetClient = new BluetoothHeadsetClient(context, listener);
             return true;
+        } else if (profile == BluetoothProfile.SAP) {
+            BluetoothSap sap = new BluetoothSap(context, listener);
+            return true;
         } else {
             return false;
         }
@@ -1420,6 +1807,10 @@
                 BluetoothHeadsetClient headsetClient = (BluetoothHeadsetClient)proxy;
                 headsetClient.close();
                 break;
+            case BluetoothProfile.SAP:
+                BluetoothSap sap = (BluetoothSap)proxy;
+                sap.close();
+                break;
         }
     }
 
@@ -1463,6 +1854,10 @@
                     }
                 }
             }
+
+            public void onBrEdrDown() {
+                if (VDBG) Log.i(TAG, "on QBrEdrDown: ");
+            }
     };
 
     /**
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index bb0d0a3..bfc374fb 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -302,6 +302,12 @@
      */
     public static final int DEVICE_TYPE_DUAL = 3;
 
+
+    /** @hide */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_SDP_RECORD =
+            "android.bluetooth.device.action.SDP_RECORD";
+
     /**
      * Broadcast Action: This intent is used to broadcast the {@link UUID}
      * wrapped as a {@link android.os.ParcelUuid} of the remote device after it
@@ -376,6 +382,9 @@
     /**@hide*/
     public static final int REQUEST_TYPE_MESSAGE_ACCESS = 3;
 
+    /**@hide*/
+    public static final int REQUEST_TYPE_SIM_ACCESS = 4;
+
     /**
      * Used as an extra field in {@link #ACTION_CONNECTION_ACCESS_REQUEST} intents,
      * Contains package name to return reply intent to.
@@ -526,6 +535,13 @@
      */
     public static final String EXTRA_UUID = "android.bluetooth.device.extra.UUID";
 
+    /** @hide */
+    public static final String EXTRA_SDP_RECORD =
+        "android.bluetooth.device.extra.SDP_RECORD";
+
+    /** @hide */
+    public static final String EXTRA_SDP_SEARCH_STATUS =
+            "android.bluetooth.device.extra.SDP_SEARCH_STATUS";
     /**
      * For {@link #getPhonebookAccessPermission}, {@link #setPhonebookAccessPermission},
      * {@link #getMessageAccessPermission} and {@link #setMessageAccessPermission}.
@@ -593,7 +609,9 @@
         public void onBluetoothServiceUp(IBluetooth bluetoothService)
                 throws RemoteException {
             synchronized (BluetoothDevice.class) {
-                sService = bluetoothService;
+                if (sService == null) {
+                    sService = bluetoothService;
+                }
             }
         }
 
@@ -603,6 +621,11 @@
                 sService = null;
             }
         }
+
+        public void onBrEdrDown()
+        {
+            if (DBG) Log.d(TAG, "onBrEdrDown: reached BLE ON state");
+        }
     };
     /**
      * Create a new BluetoothDevice
@@ -1017,7 +1040,7 @@
      *         or null on error
      */
      public ParcelUuid[] getUuids() {
-         if (sService == null) {
+         if (sService == null || isBluetoothEnabled() == false) {
             Log.e(TAG, "BT not enabled. Cannot get remote device Uuids");
              return null;
          }
@@ -1044,7 +1067,7 @@
       */
      public boolean fetchUuidsWithSdp() {
         IBluetooth service = sService;
-        if (service == null) {
+        if (service == null || isBluetoothEnabled() == false) {
             Log.e(TAG, "BT not enabled. Cannot fetchUuidsWithSdp");
             return false;
         }
@@ -1054,28 +1077,38 @@
             return false;
     }
 
+     /**
+      * Perform a service discovery on the remote device to get the SDP records associated
+      * with the specified UUID.
+      *
+      * <p>This API is asynchronous and {@link #ACTION_SDP_RECORD} intent is sent,
+      * with the SDP records found on the remote end. If there is an error
+      * in getting the SDP records or if the process takes a long time,
+      * {@link #ACTION_SDP_RECORD} intent is sent with an status value in
+      * {@link #EXTRA_SDP_SEARCH_STATUS} different from 0.
+      * Detailed status error codes can be found by members of the Bluetooth package in
+      * the AbstractionLayer class.
+      * <p>Requires {@link android.Manifest.permission#BLUETOOTH}.
+      * The SDP record data will be stored in the intent as {@link #EXTRA_SDP_RECORD}.
+      * The object type will match one of the SdpXxxRecord types, depending on the UUID searched
+      * for.
+      *
+      * @return False if the sanity check fails, True if the process
+      *               of initiating an ACL connection to the remote device
+      *               was started.
+      */
      /** @hide */
-     public boolean fetchMasInstances() {
+     public boolean sdpSearch(ParcelUuid uuid) {
          if (sService == null) {
-             Log.e(TAG, "BT not enabled. Cannot query remote device for MAS instances");
+             Log.e(TAG, "BT not enabled. Cannot query remote device sdp records");
              return false;
          }
          try {
-             return sService.fetchRemoteMasInstances(this);
+             return sService.sdpSearch(this,uuid);
          } catch (RemoteException e) {Log.e(TAG, "", e);}
          return false;
      }
 
-    /** @hide */
-    public int getServiceChannel(ParcelUuid uuid) {
-        //TODO(BT)
-        /*
-         try {
-             return sService.getRemoteServiceChannel(this, uuid);
-         } catch (RemoteException e) {Log.e(TAG, "", e);}*/
-         return BluetoothDevice.ERROR;
-    }
-
     /**
      * Set the pin during pairing when the pairing method is {@link #PAIRING_VARIANT_PIN}
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
@@ -1154,6 +1187,15 @@
         return false;
     }
 
+     boolean isBluetoothEnabled() {
+         boolean ret = false;
+         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+         if (adapter != null && adapter.isEnabled() == true) {
+             ret = true;
+         }
+         return ret;
+     }
+
     /**
      * Requires {@link android.Manifest.permission#BLUETOOTH}.
      * @return Whether the phonebook access is allowed to this device. Can be
@@ -1231,6 +1273,44 @@
     }
 
     /**
+     * Requires {@link android.Manifest.permission#BLUETOOTH}.
+     * @return Whether the Sim access is allowed to this device. Can be
+     *         {@link #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or {@link #ACCESS_REJECTED}.
+     * @hide
+     */
+    public int getSimAccessPermission() {
+        if (sService == null) {
+            return ACCESS_UNKNOWN;
+        }
+        try {
+            return sService.getSimAccessPermission(this);
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+        }
+        return ACCESS_UNKNOWN;
+    }
+
+    /**
+     * Sets whether the Sim access is allowed to this device.
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED}.
+     * @param value Can be {@link #ACCESS_UNKNOWN}, {@link #ACCESS_ALLOWED} or
+     *              {@link #ACCESS_REJECTED}.
+     * @return Whether the value has been successfully set.
+     * @hide
+     */
+    public boolean setSimAccessPermission(int value) {
+        if (sService == null) {
+            return false;
+        }
+        try {
+            return sService.setSimAccessPermission(this, value);
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+        }
+        return false;
+    }    
+    
+    /**
      * Create an RFCOMM {@link BluetoothSocket} ready to start a secure
      * outgoing connection to this remote device on given channel.
      * <p>The remote device will be authenticated and communication on this
@@ -1256,11 +1336,45 @@
      * @hide
      */
     public BluetoothSocket createRfcommSocket(int channel) throws IOException {
+        if (isBluetoothEnabled() == false) {
+            Log.e(TAG, "Bluetooth is not enabled");
+            throw new IOException();
+        }
         return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, channel,
                 null);
     }
 
     /**
+     * Create an L2cap {@link BluetoothSocket} ready to start a secure
+     * outgoing connection to this remote device on given channel.
+     * <p>The remote device will be authenticated and communication on this
+     * socket will be encrypted.
+     * <p> Use this socket only if an authenticated socket link is possible.
+     * Authentication refers to the authentication of the link key to
+     * prevent man-in-the-middle type of attacks.
+     * For example, for Bluetooth 2.1 devices, if any of the devices does not
+     * have an input and output capability or just has the ability to
+     * display a numeric key, a secure socket connection is not possible.
+     * In such a case, use {#link createInsecureRfcommSocket}.
+     * For more details, refer to the Security Model section 5.2 (vol 3) of
+     * Bluetooth Core Specification version 2.1 + EDR.
+     * <p>Use {@link BluetoothSocket#connect} to initiate the outgoing
+     * connection.
+     * <p>Valid L2CAP PSM channels are in range 1 to 2^16.
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+     *
+     * @param channel L2cap PSM/channel to connect to
+     * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection
+     * @throws IOException on error, for example Bluetooth not available, or
+     *                     insufficient permissions
+     * @hide
+     */
+    public BluetoothSocket createL2capSocket(int channel) throws IOException {
+        return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP, -1, true, true, this, channel,
+                null);
+    }
+
+    /**
      * Create an RFCOMM {@link BluetoothSocket} ready to start a secure
      * outgoing connection to this remote device using SDP lookup of uuid.
      * <p>This is designed to be used with {@link
@@ -1292,6 +1406,11 @@
      *                     insufficient permissions
      */
     public BluetoothSocket createRfcommSocketToServiceRecord(UUID uuid) throws IOException {
+        if (isBluetoothEnabled() == false) {
+            Log.e(TAG, "Bluetooth is not enabled");
+            throw new IOException();
+        }
+
         return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, -1,
                 new ParcelUuid(uuid));
     }
@@ -1325,6 +1444,10 @@
      *                     insufficient permissions
      */
     public BluetoothSocket createInsecureRfcommSocketToServiceRecord(UUID uuid) throws IOException {
+        if (isBluetoothEnabled() == false) {
+            Log.e(TAG, "Bluetooth is not enabled");
+            throw new IOException();
+        }
         return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, false, false, this, -1,
                 new ParcelUuid(uuid));
     }
@@ -1344,6 +1467,11 @@
      * @hide
      */
     public BluetoothSocket createInsecureRfcommSocket(int port) throws IOException {
+
+        if (isBluetoothEnabled() == false) {
+            Log.e(TAG, "Bluetooth is not enabled");
+            throw new IOException();
+        }
         return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, false, false, this, port,
                 null);
     }
@@ -1359,6 +1487,11 @@
      * @hide
      */
     public BluetoothSocket createScoSocket() throws IOException {
+
+        if (isBluetoothEnabled() == false) {
+            Log.e(TAG, "Bluetooth is not enabled");
+            throw new IOException();
+        }
         return new BluetoothSocket(BluetoothSocket.TYPE_SCO, -1, true, true, this, -1, null);
     }
 
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index 1367405..eecb073 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -103,17 +103,23 @@
      */
     public static final int MAP = 9;
 
+    /*
+     * SAP Profile
+     * @hide
+     */
+    public static final int SAP = 10;
+
     /**
      * A2DP Sink Profile
      * @hide
      */
-    public static final int A2DP_SINK = 10;
+    public static final int A2DP_SINK = 11;
 
     /**
      * AVRCP Controller Profile
      * @hide
      */
-    public static final int AVRCP_CONTROLLER = 11;
+    public static final int AVRCP_CONTROLLER = 12;
 
     /**
      * Headset Client - HFP HF Role
diff --git a/core/java/android/bluetooth/BluetoothSap.java b/core/java/android/bluetooth/BluetoothSap.java
new file mode 100644
index 0000000..7b4c6f9
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothSap.java
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 2008 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.List;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.RemoteException;
+import android.os.IBinder;
+import android.os.ServiceManager;
+import android.util.Log;
+
+
+public final class BluetoothSap implements BluetoothProfile {
+
+    private static final String TAG = "BluetoothSap";
+    private static final boolean DBG = true;
+    private static final boolean VDBG = false;
+
+    public static final String ACTION_CONNECTION_STATE_CHANGED =
+        "android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED";
+
+    private IBluetoothSap mService;
+    private final Context mContext;
+    private ServiceListener mServiceListener;
+    private BluetoothAdapter mAdapter;
+
+    /** There was an error trying to obtain the state */
+    public static final int STATE_ERROR        = -1;
+
+    public static final int RESULT_FAILURE = 0;
+    public static final int RESULT_SUCCESS = 1;
+    /** Connection canceled before completion. */
+    public static final int RESULT_CANCELED = 2;
+
+    final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
+            new IBluetoothStateChangeCallback.Stub() {
+                public void onBluetoothStateChange(boolean up) {
+                    if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
+                    if (!up) {
+                        if (VDBG) 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 (VDBG) Log.d(TAG,"Binding service...");
+                                    doBind();
+                                }
+                            } catch (Exception re) {
+                                Log.e(TAG,"",re);
+                            }
+                        }
+                    }
+                }
+        };
+
+    /**
+     * Create a BluetoothSap proxy object.
+     */
+    /*package*/ BluetoothSap(Context context, ServiceListener l) {
+        if (DBG) Log.d(TAG, "Create BluetoothSap proxy object");
+        mContext = context;
+        mServiceListener = l;
+        mAdapter = BluetoothAdapter.getDefaultAdapter();
+        IBluetoothManager mgr = mAdapter.getBluetoothManager();
+        if (mgr != null) {
+            try {
+                mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
+            } catch (RemoteException e) {
+                Log.e(TAG,"",e);
+            }
+        }
+        doBind();
+    }
+
+    boolean doBind() {
+        Intent intent = new Intent(IBluetoothMap.class.getName());
+        ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
+        intent.setComponent(comp);
+        if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
+                android.os.Process.myUserHandle())) {
+            Log.e(TAG, "Could not bind to Bluetooth SAP Service with " + intent);
+            return false;
+        }
+        return true;
+    }
+
+    protected void finalize() throws Throwable {
+        try {
+            close();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    /**
+     * Close the connection to the backing service.
+     * Other public functions of BluetoothSap will return default error
+     * results once close() has been called. Multiple invocations of close()
+     * are ok.
+     */
+    public synchronized void close() {
+        IBluetoothManager mgr = mAdapter.getBluetoothManager();
+        if (mgr != null) {
+            try {
+                mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
+            } catch (Exception e) {
+                Log.e(TAG,"",e);
+            }
+        }
+
+        synchronized (mConnection) {
+            if (mService != null) {
+                try {
+                    mService = null;
+                    mContext.unbindService(mConnection);
+                } catch (Exception re) {
+                    Log.e(TAG,"",re);
+                }
+            }
+        }
+        mServiceListener = null;
+    }
+
+    /**
+     * Get the current state of the BluetoothSap service.
+     * @return One of the STATE_ return codes, or STATE_ERROR if this proxy
+     *         object is currently not connected to the Sap service.
+     */
+    public int getState() {
+        if (VDBG) log("getState()");
+        if (mService != null) {
+            try {
+                return mService.getState();
+            } catch (RemoteException e) {Log.e(TAG, e.toString());}
+        } else {
+            Log.w(TAG, "Proxy not attached to service");
+            if (DBG) log(Log.getStackTraceString(new Throwable()));
+        }
+        return BluetoothSap.STATE_ERROR;
+    }
+
+    /**
+     * Get the currently connected remote Bluetooth device (PCE).
+     * @return The remote Bluetooth device, or null if not in connected or
+     *         connecting state, or if this proxy object is not connected to
+     *         the Sap service.
+     */
+    public BluetoothDevice getClient() {
+        if (VDBG) log("getClient()");
+        if (mService != null) {
+            try {
+                return mService.getClient();
+            } catch (RemoteException e) {Log.e(TAG, e.toString());}
+        } else {
+            Log.w(TAG, "Proxy not attached to service");
+            if (DBG) log(Log.getStackTraceString(new Throwable()));
+        }
+        return null;
+    }
+
+    /**
+     * Returns true if the specified Bluetooth device is connected.
+     * Returns false if not connected, or if this proxy object is not
+     * currently connected to the Sap service.
+     */
+    public boolean isConnected(BluetoothDevice device) {
+        if (VDBG) log("isConnected(" + device + ")");
+        if (mService != null) {
+            try {
+                return mService.isConnected(device);
+            } catch (RemoteException e) {Log.e(TAG, e.toString());}
+        } else {
+            Log.w(TAG, "Proxy not attached to service");
+            if (DBG) log(Log.getStackTraceString(new Throwable()));
+        }
+        return false;
+    }
+
+    /**
+     * Initiate connection. Initiation of outgoing connections is not
+     * supported for SAP server.
+     */
+    public boolean connect(BluetoothDevice device) {
+        if (DBG) log("connect(" + device + ")" + "not supported for SAPS");
+        return false;
+    }
+
+    /**
+     * Initiate disconnect.
+     *
+     * @param device Remote Bluetooth Device
+     * @return false on error,
+     *               true otherwise
+     */
+    public boolean disconnect(BluetoothDevice device) {
+        if (DBG) log("disconnect(" + device + ")");
+        if (mService != null && isEnabled() &&
+            isValidDevice(device)) {
+            try {
+                return mService.disconnect(device);
+            } catch (RemoteException e) {
+              Log.e(TAG, Log.getStackTraceString(new Throwable()));
+              return false;
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
+    }
+
+    /**
+     * Get the list of connected devices. Currently at most one.
+     *
+     * @return list of connected devices
+     */
+    public List<BluetoothDevice> getConnectedDevices() {
+        if (DBG) log("getConnectedDevices()");
+        if (mService != null && isEnabled()) {
+            try {
+                return mService.getConnectedDevices();
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return new ArrayList<BluetoothDevice>();
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return new ArrayList<BluetoothDevice>();
+    }
+
+    /**
+     * Get the list of devices matching specified states. Currently at most one.
+     *
+     * @return list of matching devices
+     */
+    public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
+        if (DBG) log("getDevicesMatchingStates()");
+        if (mService != null && isEnabled()) {
+            try {
+                return mService.getDevicesMatchingConnectionStates(states);
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return new ArrayList<BluetoothDevice>();
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return new ArrayList<BluetoothDevice>();
+    }
+
+    /**
+     * Get connection state of device
+     *
+     * @return device connection state
+     */
+    public int getConnectionState(BluetoothDevice device) {
+        if (DBG) log("getConnectionState(" + device + ")");
+        if (mService != null && isEnabled() &&
+            isValidDevice(device)) {
+            try {
+                return mService.getConnectionState(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return BluetoothProfile.STATE_DISCONNECTED;
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return BluetoothProfile.STATE_DISCONNECTED;
+    }
+
+    /**
+     * Set priority of the profile
+     *
+     * <p> The device should already be paired.
+     *
+     * @param device Paired bluetooth device
+     * @param priority
+     * @return true if priority is set, false on error
+     */
+    public boolean setPriority(BluetoothDevice device, int priority) {
+        if (DBG) log("setPriority(" + device + ", " + priority + ")");
+        if (mService != null && isEnabled() &&
+            isValidDevice(device)) {
+            if (priority != BluetoothProfile.PRIORITY_OFF &&
+                priority != BluetoothProfile.PRIORITY_ON) {
+              return false;
+            }
+            try {
+                return mService.setPriority(device, priority);
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return false;
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
+    }
+
+    /**
+     * Get the priority of the profile.
+     *
+     * @param device Bluetooth device
+     * @return priority of the device
+     */
+    public int getPriority(BluetoothDevice device) {
+        if (VDBG) log("getPriority(" + device + ")");
+        if (mService != null && isEnabled() &&
+            isValidDevice(device)) {
+            try {
+                return mService.getPriority(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+                return PRIORITY_OFF;
+            }
+        }
+        if (mService == null) Log.w(TAG, "Proxy not attached to service");
+        return PRIORITY_OFF;
+    }
+
+    private ServiceConnection mConnection = new ServiceConnection() {
+        public void onServiceConnected(ComponentName className, IBinder service) {
+            if (DBG) log("Proxy object connected");
+            mService = IBluetoothSap.Stub.asInterface(service);
+            if (mServiceListener != null) {
+                mServiceListener.onServiceConnected(BluetoothProfile.SAP, BluetoothSap.this);
+            }
+        }
+        public void onServiceDisconnected(ComponentName className) {
+            if (DBG) log("Proxy object disconnected");
+            mService = null;
+            if (mServiceListener != null) {
+                mServiceListener.onServiceDisconnected(BluetoothProfile.SAP);
+            }
+        }
+    };
+
+    private static void log(String msg) {
+        Log.d(TAG, msg);
+    }
+
+    private boolean isEnabled() {
+        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+
+        if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON)
+            return true;
+        log("Bluetooth is Not enabled");
+        return false;
+    }
+
+    private boolean isValidDevice(BluetoothDevice device) {
+       if (device == null)
+           return false;
+
+       if (BluetoothAdapter.checkBluetoothAddress(device.getAddress()))
+           return true;
+       return false;
+    }
+
+}
diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java
index bc56e55..21024a6 100644
--- a/core/java/android/bluetooth/BluetoothServerSocket.java
+++ b/core/java/android/bluetooth/BluetoothServerSocket.java
@@ -18,6 +18,7 @@
 
 import android.os.Handler;
 import android.os.ParcelUuid;
+import android.util.Log;
 
 import java.io.Closeable;
 import java.io.IOException;
@@ -66,10 +67,11 @@
  */
 public final class BluetoothServerSocket implements Closeable {
 
+    private static final String TAG = "BluetoothServerSocket";
     /*package*/ final BluetoothSocket mSocket;
     private Handler mHandler;
     private int mMessage;
-    private final int mChannel;
+    private int mChannel;
 
     /**
      * Construct a socket for incoming connections.
@@ -84,6 +86,9 @@
             throws IOException {
         mChannel = port;
         mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, port, null);
+        if(port == BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
+            mSocket.setExcludeSdp(true);
+        }
     }
 
     /**
@@ -98,6 +103,7 @@
     /*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, ParcelUuid uuid)
             throws IOException {
         mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, -1, uuid);
+        // TODO: This is the same as mChannel = -1 - is this intentional?
         mChannel = mSocket.getPort();
     }
 
@@ -153,6 +159,7 @@
     /*package*/ void setServiceName(String ServiceName) {
         mSocket.setServiceName(ServiceName);
     }
+
     /**
      * Returns the channel on which this socket is bound.
      * @hide
@@ -160,4 +167,47 @@
     public int getChannel() {
         return mChannel;
     }
+
+    /**
+     * Sets the channel on which future sockets are bound.
+     * Currently used only when a channel is auto generated.
+     */
+    /*package*/ void setChannel(int newChannel) {
+        /* TODO: From a design/architecture perspective this is wrong.
+         *       The bind operation should be conducted through this class
+         *       and the resulting port should be kept in mChannel, and
+         *       not set from BluetoothAdapter. */
+        if(mSocket != null) {
+            if(mSocket.getPort() != newChannel) {
+                Log.w(TAG,"The port set is different that the underlying port. mSocket.getPort(): "
+                            + mSocket.getPort() + " requested newChannel: " + newChannel);
+            }
+        }
+        mChannel = newChannel;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        sb.append("ServerSocket: Type: ");
+        switch(mSocket.getConnectionType()) {
+            case BluetoothSocket.TYPE_RFCOMM:
+            {
+                sb.append("TYPE_RFCOMM");
+                break;
+            }
+            case BluetoothSocket.TYPE_L2CAP:
+            {
+                sb.append("TYPE_L2CAP");
+                break;
+            }
+            case BluetoothSocket.TYPE_SCO:
+            {
+                sb.append("TYPE_SCO");
+                break;
+            }
+        }
+        sb.append(" Channel: ").append(mChannel);
+        return sb.toString();
+    }
 }
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index 36997e5..5702d11 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -21,6 +21,7 @@
 import android.os.RemoteException;
 import android.util.Log;
 
+import java.io.BufferedInputStream;
 import java.io.Closeable;
 import java.io.FileDescriptor;
 import java.io.IOException;
@@ -29,6 +30,8 @@
 import java.util.Locale;
 import java.util.UUID;
 import android.net.LocalSocket;
+
+import java.nio.Buffer;
 import java.nio.ByteOrder;
 import java.nio.ByteBuffer;
 /**
@@ -86,17 +89,19 @@
 
     /** @hide */
     public static final int MAX_RFCOMM_CHANNEL = 30;
+    /*package*/ static final int MAX_L2CAP_PACKAGE_SIZE = 0xFFFF;
 
     /** Keep TYPE_ fields in sync with BluetoothSocket.cpp */
-    /*package*/ static final int TYPE_RFCOMM = 1;
-    /*package*/ static final int TYPE_SCO = 2;
-    /*package*/ static final int TYPE_L2CAP = 3;
+    public static final int TYPE_RFCOMM = 1;
+    public static final int TYPE_SCO = 2;
+    public static final int TYPE_L2CAP = 3;
 
     /*package*/ static final int EBADFD = 77;
     /*package*/ static final int EADDRINUSE = 98;
 
     /*package*/ static final int SEC_FLAG_ENCRYPT = 1;
     /*package*/ static final int SEC_FLAG_AUTH = 1 << 1;
+    /*package*/ static final int BTSOCK_FLAG_NO_SDP  = 1 << 2;
 
     private final int mType;  /* one of TYPE_RFCOMM etc */
     private BluetoothDevice mDevice;    /* remote device */
@@ -106,6 +111,7 @@
     private final BluetoothInputStream mInputStream;
     private final BluetoothOutputStream mOutputStream;
     private final ParcelUuid mUuid;
+    private boolean mExcludeSdp = false;
     private ParcelFileDescriptor mPfd;
     private LocalSocket mSocket;
     private InputStream mSocketIS;
@@ -115,7 +121,11 @@
     private String mServiceName;
     private static int PROXY_CONNECTION_TIMEOUT = 5000;
 
-    private static int SOCK_SIGNAL_SIZE = 16;
+    private static int SOCK_SIGNAL_SIZE = 20;
+
+    private ByteBuffer mL2capBuffer = null;
+    private int mMaxTxPacketSize = 0; // The l2cap maximum packet size supported by the peer.
+    private int mMaxRxPacketSize = 0; // The l2cap maximum packet size that can be received.
 
     private enum SocketState {
         INIT,
@@ -144,12 +154,14 @@
      */
     /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt,
             BluetoothDevice device, int port, ParcelUuid uuid) throws IOException {
-        if (type == BluetoothSocket.TYPE_RFCOMM && uuid == null && fd == -1) {
+        if (VDBG) Log.d(TAG, "Creating new BluetoothSocket of type: " + type);
+        if (type == BluetoothSocket.TYPE_RFCOMM && uuid == null && fd == -1
+                && port != BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP) {
             if (port < 1 || port > MAX_RFCOMM_CHANNEL) {
                 throw new IOException("Invalid RFCOMM channel: " + port);
             }
         }
-        if(uuid != null)
+        if (uuid != null)
             mUuid = uuid;
         else mUuid = new ParcelUuid(new UUID(0, 0));
         mType = type;
@@ -172,6 +184,7 @@
         mOutputStream = new BluetoothOutputStream(this);
     }
     private BluetoothSocket(BluetoothSocket s) {
+        if (VDBG) Log.d(TAG, "Creating new Private BluetoothSocket of type: " + s.mType);
         mUuid = s.mUuid;
         mType = s.mType;
         mAuth = s.mAuth;
@@ -179,7 +192,11 @@
         mPort = s.mPort;
         mInputStream = new BluetoothInputStream(this);
         mOutputStream = new BluetoothOutputStream(this);
+        mMaxRxPacketSize = s.mMaxRxPacketSize;
+        mMaxTxPacketSize = s.mMaxTxPacketSize;
+
         mServiceName = s.mServiceName;
+        mExcludeSdp = s.mExcludeSdp;
     }
     private BluetoothSocket acceptSocket(String RemoteAddr) throws IOException {
         BluetoothSocket as = new BluetoothSocket(this);
@@ -229,6 +246,8 @@
             flags |= SEC_FLAG_AUTH;
         if(mEncrypt)
             flags |= SEC_FLAG_ENCRYPT;
+        if(mExcludeSdp)
+            flags |= BTSOCK_FLAG_NO_SDP;
         return flags;
     }
 
@@ -298,7 +317,8 @@
 
         try {
             if (mSocketState == SocketState.CLOSED) throw new IOException("socket closed");
-            IBluetooth bluetoothProxy = BluetoothAdapter.getDefaultAdapter().getBluetoothService(null);
+            IBluetooth bluetoothProxy =
+                    BluetoothAdapter.getDefaultAdapter().getBluetoothService(null);
             if (bluetoothProxy == null) throw new IOException("Bluetooth is off");
             mPfd = bluetoothProxy.connectSocket(mDevice, mType,
                     mUuid, mPort, getSecurityFlags());
@@ -370,7 +390,7 @@
                     mSocketState = SocketState.LISTENING;
             }
             if (DBG) Log.d(TAG, "channel: " + channel);
-            if (mPort == -1) {
+            if (mPort <= -1) {
                 mPort = channel;
             } // else ASSERT(mPort == channel)
             ret = 0;
@@ -391,7 +411,8 @@
 
     /*package*/ BluetoothSocket accept(int timeout) throws IOException {
         BluetoothSocket acceptedSocket;
-        if (mSocketState != SocketState.LISTENING) throw new IOException("bt socket is not in listen state");
+        if (mSocketState != SocketState.LISTENING)
+            throw new IOException("bt socket is not in listen state");
         if(timeout > 0) {
             Log.d(TAG, "accept() set timeout (ms):" + timeout);
            mSocket.setSoTimeout(timeout);
@@ -427,27 +448,80 @@
     }
 
     /*package*/ int read(byte[] b, int offset, int length) throws IOException {
-        if (mSocketIS == null) throw new IOException("read is called on null InputStream");
+        int ret = 0;
         if (VDBG) Log.d(TAG, "read in:  " + mSocketIS + " len: " + length);
-        int ret = mSocketIS.read(b, offset, length);
-        if(ret < 0)
+        if(mType == TYPE_L2CAP)
+        {
+            int bytesToRead = length;
+            if (VDBG) Log.v(TAG, "l2cap: read(): offset: " + offset + " length:" + length
+                    + "mL2capBuffer= " + mL2capBuffer);
+            if (mL2capBuffer == null) {
+                createL2capRxBuffer();
+            }
+            if (mL2capBuffer.remaining() == 0) {
+                if (VDBG) Log.v(TAG, "l2cap buffer empty, refilling...");
+                if (fillL2capRxBuffer() == -1) {
+                    return -1;
+                }
+            }
+            if (bytesToRead > mL2capBuffer.remaining()) {
+                bytesToRead = mL2capBuffer.remaining();
+            }
+            if(VDBG) Log.v(TAG, "get(): offset: " + offset
+                    + " bytesToRead: " + bytesToRead);
+            mL2capBuffer.get(b, offset, bytesToRead);
+            ret = bytesToRead;
+        }else {
+            if (VDBG) Log.v(TAG, "default: read(): offset: " + offset + " length:" + length);
+            ret = mSocketIS.read(b, offset, length);
+        }
+        if (ret < 0)
             throw new IOException("bt socket closed, read return: " + ret);
         if (VDBG) Log.d(TAG, "read out:  " + mSocketIS + " ret: " + ret);
         return ret;
     }
 
     /*package*/ int write(byte[] b, int offset, int length) throws IOException {
-        if (mSocketOS == null) throw new IOException("write is called on null OutputStream");
-        if (VDBG) Log.d(TAG, "write: " + mSocketOS + " length: " + length);
-        mSocketOS.write(b, offset, length);
-        // There is no good way to confirm since the entire process is asynchronous anyway
-        if (VDBG) Log.d(TAG, "write out: " + mSocketOS + " length: " + length);
-        return length;
+
+        //TODO: Since bindings can exist between the SDU size and the
+        //      protocol, we might need to throw an exception instead of just
+        //      splitting the write into multiple smaller writes.
+        //      Rfcomm uses dynamic allocation, and should not have any bindings
+        //      to the actual message length.
+            if (VDBG) Log.d(TAG, "write: " + mSocketOS + " length: " + length);
+            if (mType == TYPE_L2CAP) {
+                if(length <= mMaxTxPacketSize) {
+                    mSocketOS.write(b, offset, length);
+                } else {
+                    int tmpOffset = offset;
+                    int tmpLength = mMaxTxPacketSize;
+                    int endIndex = offset + length;
+                    boolean done = false;
+                    if(DBG) Log.w(TAG, "WARNING: Write buffer larger than L2CAP packet size!\n"
+                            + "Packet will be divided into SDU packets of size "
+                            + mMaxTxPacketSize);
+                    do{
+                        mSocketOS.write(b, tmpOffset, tmpLength);
+                        tmpOffset += mMaxTxPacketSize;
+                        if((tmpOffset + mMaxTxPacketSize) > endIndex) {
+                            tmpLength = endIndex - tmpOffset;
+                            done = true;
+                        }
+                    } while(!done);
+
+                }
+            } else {
+                mSocketOS.write(b, offset, length);
+            }
+            // There is no good way to confirm since the entire process is asynchronous anyway
+            if (VDBG) Log.d(TAG, "write out: " + mSocketOS + " length: " + length);
+            return length;
     }
 
     @Override
     public void close() throws IOException {
-        if (DBG) Log.d(TAG, "close() in, this: " + this + ", channel: " + mPort + ", state: " + mSocketState);
+        if (DBG) Log.d(TAG, "close() in, this: " + this + ", channel: " + mPort + ", state: "
+                + mSocketState);
         if(mSocketState == SocketState.CLOSED)
             return;
         else
@@ -457,8 +531,9 @@
                  if(mSocketState == SocketState.CLOSED)
                     return;
                  mSocketState = SocketState.CLOSED;
-                 if (DBG) Log.d(TAG, "close() this: " + this + ", channel: " + mPort + ", mSocketIS: " + mSocketIS +
-                        ", mSocketOS: " + mSocketOS + "mSocket: " + mSocket);
+                 if (DBG) Log.d(TAG, "close() this: " + this + ", channel: " + mPort +
+                         ", mSocketIS: " + mSocketIS + ", mSocketOS: " + mSocketOS +
+                         "mSocket: " + mSocket);
                  if(mSocket != null) {
                     if (DBG) Log.d(TAG, "Closing mSocket: " + mSocket);
                     mSocket.shutdownInput();
@@ -480,6 +555,47 @@
     /*package */ int getPort() {
         return mPort;
     }
+
+    /**
+     * Get the maximum supported Transmit packet size for the underlying transport.
+     * Use this to optimize the writes done to the output socket, to avoid sending
+     * half full packets.
+     * @return the maximum supported Transmit packet size for the underlying transport.
+     */
+    public int getMaxTransmitPacketSize(){
+        return mMaxTxPacketSize;
+    }
+
+    /**
+     * Get the maximum supported Receive packet size for the underlying transport.
+     * Use this to optimize the reads done on the input stream, as any call to read
+     * will return a maximum of this amount of bytes - or for some transports a
+     * multiple of this value.
+     * @return the maximum supported Receive packet size for the underlying transport.
+     */
+    public int getMaxReceivePacketSize(){
+        return mMaxRxPacketSize;
+    }
+
+    /**
+     * Get the type of the underlying connection
+     * @return one of TYPE_
+     */
+    public int getConnectionType() {
+        return mType;
+    }
+
+    /**
+     * Change if a SDP entry should be automatically created.
+     * Must be called before calling .bind, for the call to have any effect.
+     * @param mExcludeSdp <li>TRUE  - do not auto generate SDP record.
+     *                    <li>FALSE - default - auto generate SPP SDP record.
+     * @hide
+     */
+    public void setExcludeSdp(boolean excludeSdp) {
+        this.mExcludeSdp = excludeSdp;
+    }
+
     private String convertAddr(final byte[] addr)  {
         return String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X",
                 addr[0] , addr[1], addr[2], addr[3] , addr[4], addr[5]);
@@ -487,8 +603,10 @@
     private String waitSocketSignal(InputStream is) throws IOException {
         byte [] sig = new byte[SOCK_SIGNAL_SIZE];
         int ret = readAll(is, sig);
-        if (VDBG) Log.d(TAG, "waitSocketSignal read 16 bytes signal ret: " + ret);
+        if (VDBG) Log.d(TAG, "waitSocketSignal read " + SOCK_SIGNAL_SIZE +
+                " bytes signal ret: " + ret);
         ByteBuffer bb = ByteBuffer.wrap(sig);
+        /* the struct in native is decorated with __attribute__((packed)), hence this is possible */
         bb.order(ByteOrder.nativeOrder());
         int size = bb.getShort();
         if(size != SOCK_SIGNAL_SIZE)
@@ -497,19 +615,36 @@
         bb.get(addr);
         int channel = bb.getInt();
         int status = bb.getInt();
+        mMaxTxPacketSize = (bb.getShort() & 0xffff); // Convert to unsigned value
+        mMaxRxPacketSize = (bb.getShort() & 0xffff); // Convert to unsigned value
         String RemoteAddr = convertAddr(addr);
         if (VDBG) Log.d(TAG, "waitSocketSignal: sig size: " + size + ", remote addr: "
-                + RemoteAddr + ", channel: " + channel + ", status: " + status);
+                + RemoteAddr + ", channel: " + channel + ", status: " + status
+                + " MaxRxPktSize: " + mMaxRxPacketSize + " MaxTxPktSize: " + mMaxTxPacketSize);
         if(status != 0)
             throw new IOException("Connection failure, status: " + status);
         return RemoteAddr;
     }
+
+    private void createL2capRxBuffer(){
+        if(mType == TYPE_L2CAP) {
+            // Allocate the buffer to use for reads.
+            if(VDBG) Log.v(TAG, "  Creating mL2capBuffer: mMaxPacketSize: " + mMaxRxPacketSize);
+            mL2capBuffer = ByteBuffer.wrap(new byte[mMaxRxPacketSize]);
+            if(VDBG) Log.v(TAG, "mL2capBuffer.remaining()" + mL2capBuffer.remaining());
+            mL2capBuffer.limit(0); // Ensure we do a real read at the first read-request
+            if(VDBG) Log.v(TAG, "mL2capBuffer.remaining() after limit(0):" +
+                    mL2capBuffer.remaining());
+        }
+    }
+
     private int readAll(InputStream is, byte[] b) throws IOException {
         int left = b.length;
         while(left > 0) {
             int ret = is.read(b, b.length - left, left);
             if(ret <= 0)
-                 throw new IOException("read failed, socket might closed or timeout, read ret: " + ret);
+                 throw new IOException("read failed, socket might closed or timeout, read ret: "
+                         + ret);
             left -= ret;
             if(left != 0)
                 Log.w(TAG, "readAll() looping, read partial size: " + (b.length - left) +
@@ -526,4 +661,18 @@
         bb.order(ByteOrder.nativeOrder());
         return bb.getInt();
     }
+
+    private int fillL2capRxBuffer() throws IOException {
+        mL2capBuffer.rewind();
+        int ret = mSocketIS.read(mL2capBuffer.array());
+        if(ret == -1) {
+            // reached end of stream - return -1
+            mL2capBuffer.limit(0);
+            return -1;
+        }
+        mL2capBuffer.limit(ret);
+        return ret;
+    }
+
+
 }
diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java
index 194a53e..2ded4c8 100644
--- a/core/java/android/bluetooth/BluetoothUuid.java
+++ b/core/java/android/bluetooth/BluetoothUuid.java
@@ -76,7 +76,9 @@
             ParcelUuid.fromString("00001133-0000-1000-8000-00805F9B34FB");
     public static final ParcelUuid MAS =
             ParcelUuid.fromString("00001132-0000-1000-8000-00805F9B34FB");
-
+  public static final ParcelUuid SAP =
+            ParcelUuid.fromString("0000112D-0000-1000-8000-00805F9B34FB");
+			
     public static final ParcelUuid BASE_UUID =
             ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB");
 
@@ -89,7 +91,7 @@
 
     public static final ParcelUuid[] RESERVED_UUIDS = {
         AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget,
-        ObexObjectPush, PANU, NAP, MAP, MNS, MAS};
+        ObexObjectPush, PANU, NAP, MAP, MNS, MAS, SAP};
 
     public static boolean isAudioSource(ParcelUuid uuid) {
         return uuid.equals(AudioSource);
@@ -143,6 +145,9 @@
     public static boolean isMas(ParcelUuid uuid) {
         return uuid.equals(MAS);
     }
+    public static boolean isSap(ParcelUuid uuid) {
+        return uuid.equals(SAP);
+    }
 
     /**
      * Returns true if ParcelUuid is present in uuidArray
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index dabb1ce..f6001bf 100644
--- a/core/java/android/bluetooth/IBluetooth.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -68,7 +68,7 @@
     int getRemoteClass(in BluetoothDevice device);
     ParcelUuid[] getRemoteUuids(in BluetoothDevice device);
     boolean fetchRemoteUuids(in BluetoothDevice device);
-    boolean fetchRemoteMasInstances(in BluetoothDevice device);
+    boolean sdpSearch(in BluetoothDevice device, in ParcelUuid uuid);
 
     boolean setPin(in BluetoothDevice device, boolean accept, int len, in byte[] pinCode);
     boolean setPasskey(in BluetoothDevice device, boolean accept, int len, in byte[]
@@ -79,6 +79,8 @@
     boolean setPhonebookAccessPermission(in BluetoothDevice device, int value);
     int getMessageAccessPermission(in BluetoothDevice device);
     boolean setMessageAccessPermission(in BluetoothDevice device, int value);
+    int getSimAccessPermission(in BluetoothDevice device);
+    boolean setSimAccessPermission(in BluetoothDevice device, int value);
 
     void sendConnectionStateChange(in BluetoothDevice device, int profile, int state, int prevState);
 
@@ -98,7 +100,10 @@
     boolean isActivityAndEnergyReportingSupported();
     void getActivityEnergyInfoFromController();
     BluetoothActivityEnergyInfo reportActivityInfo();
+    int numOfHwTrackFiltersAvailable();
 
     // for dumpsys support
     String dump();
+    void onLeServiceUp();
+    void onBrEdrDown();
 }
diff --git a/core/java/android/bluetooth/IBluetoothGatt.aidl b/core/java/android/bluetooth/IBluetoothGatt.aidl
index 7070bae..4ca57f8 100644
--- a/core/java/android/bluetooth/IBluetoothGatt.aidl
+++ b/core/java/android/bluetooth/IBluetoothGatt.aidl
@@ -101,4 +101,6 @@
                             in int srvcInstanceId, in ParcelUuid srvcId,
                             in int charInstanceId, in ParcelUuid charId,
                             in boolean confirm, in byte[] value);
+    void disconnectAll();
+    void unregAll();
 }
diff --git a/core/java/android/bluetooth/IBluetoothManager.aidl b/core/java/android/bluetooth/IBluetoothManager.aidl
index 7411d3f..8d1ce99 100644
--- a/core/java/android/bluetooth/IBluetoothManager.aidl
+++ b/core/java/android/bluetooth/IBluetoothManager.aidl
@@ -44,4 +44,6 @@
 
     String getAddress();
     String getName();
+    int updateBleAppCount(IBinder b, boolean enable);
+    boolean isBleAppPresent();
 }
diff --git a/core/java/android/bluetooth/IBluetoothManagerCallback.aidl b/core/java/android/bluetooth/IBluetoothManagerCallback.aidl
index 9551086..1385daf 100644
--- a/core/java/android/bluetooth/IBluetoothManagerCallback.aidl
+++ b/core/java/android/bluetooth/IBluetoothManagerCallback.aidl
@@ -26,4 +26,5 @@
 interface IBluetoothManagerCallback {
     void onBluetoothServiceUp(in IBluetooth bluetoothService);
     void onBluetoothServiceDown();
-}
\ No newline at end of file
+    void onBrEdrDown();
+}
diff --git a/core/java/android/bluetooth/IBluetoothSap.aidl b/core/java/android/bluetooth/IBluetoothSap.aidl
new file mode 100644
index 0000000..8970639
--- /dev/null
+++ b/core/java/android/bluetooth/IBluetoothSap.aidl
@@ -0,0 +1,37 @@
+/*
+ * 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;
+
+/**
+ * System private API for Bluetooth SAP service
+ *
+ * {@hide}
+ */
+interface IBluetoothSap {
+    int getState();
+    BluetoothDevice getClient();
+    boolean connect(in BluetoothDevice device);
+    boolean disconnect(in BluetoothDevice device);
+    boolean isConnected(in BluetoothDevice device);
+    List<BluetoothDevice> getConnectedDevices();
+    List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);
+    int getConnectionState(in BluetoothDevice device);
+    boolean setPriority(in BluetoothDevice device, int priority);
+    int getPriority(in BluetoothDevice device);
+}
diff --git a/core/java/android/bluetooth/SdpMasRecord.java b/core/java/android/bluetooth/SdpMasRecord.java
new file mode 100644
index 0000000..fa164c0
--- /dev/null
+++ b/core/java/android/bluetooth/SdpMasRecord.java
@@ -0,0 +1,147 @@
+/*
+* Copyright (C) 2015 Samsung System LSI
+* 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.os.Parcel;
+import android.os.Parcelable;
+
+/** @hide */
+public class SdpMasRecord implements Parcelable {
+    private final int mMasInstanceId;
+    private final int mL2capPsm;
+    private final int mRfcommChannelNumber;
+    private final int mProfileVersion;
+    private final int mSupportedFeatures;
+    private final int mSupportedMessageTypes;
+    private final String mServiceName;
+    public static final class MessageType {
+        public static final int EMAIL    = 0x01;
+        public static final int SMS_GSM  = 0x02;
+        public static final int SMS_CDMA = 0x04;
+        public static final int MMS      = 0x08;
+    }
+
+    public SdpMasRecord(int mas_instance_id,
+                                 int l2cap_psm,
+                                 int rfcomm_channel_number,
+                                 int profile_version,
+                                 int supported_features,
+                                 int supported_message_types,
+                                 String service_name){
+        this.mMasInstanceId = mas_instance_id;
+        this.mL2capPsm = l2cap_psm;
+        this.mRfcommChannelNumber = rfcomm_channel_number;
+        this.mProfileVersion = profile_version;
+        this.mSupportedFeatures = supported_features;
+        this.mSupportedMessageTypes = supported_message_types;
+        this.mServiceName = service_name;
+    }
+
+    public SdpMasRecord(Parcel in){
+        this.mMasInstanceId = in.readInt();
+        this.mL2capPsm = in.readInt();
+        this.mRfcommChannelNumber = in.readInt();
+        this.mProfileVersion = in.readInt();
+        this.mSupportedFeatures = in.readInt();
+        this.mSupportedMessageTypes = in.readInt();
+        this.mServiceName = in.readString();
+    }
+    @Override
+    public int describeContents() {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    public int getMasInstanceId() {
+        return mMasInstanceId;
+    }
+
+    public int getL2capPsm() {
+        return mL2capPsm;
+    }
+
+    public int getRfcommCannelNumber() {
+        return mRfcommChannelNumber;
+    }
+
+    public int getProfileVersion() {
+        return mProfileVersion;
+    }
+
+    public int getSupportedFeatures() {
+        return mSupportedFeatures;
+    }
+
+    public int getSupportedMessageTypes() {
+        return mSupportedMessageTypes;
+    }
+    
+    public boolean msgSupported(int msg) {
+        return (mSupportedMessageTypes & msg) != 0;
+    }
+    
+    public String getServiceName() {
+        return mServiceName;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+
+        dest.writeInt(this.mMasInstanceId);
+        dest.writeInt(this.mL2capPsm);
+        dest.writeInt(this.mRfcommChannelNumber);
+        dest.writeInt(this.mProfileVersion);
+        dest.writeInt(this.mSupportedFeatures);
+        dest.writeInt(this.mSupportedMessageTypes);
+        dest.writeString(this.mServiceName);
+
+    }
+    @Override
+    public String toString(){
+        String ret = "Bluetooth MAS SDP Record:\n";
+
+        if(mMasInstanceId != -1){
+            ret += "Mas Instance Id: " + mMasInstanceId + "\n";
+        }
+        if(mRfcommChannelNumber != -1){
+            ret += "RFCOMM Chan Number: " + mRfcommChannelNumber + "\n";
+        }
+        if(mL2capPsm != -1){
+            ret += "L2CAP PSM: " + mL2capPsm + "\n";
+        }
+        if(mServiceName != null){
+            ret += "Service Name: " + mServiceName + "\n";
+        }
+        if(mProfileVersion != -1){
+            ret += "Profile version: " + mProfileVersion + "\n";
+        }
+        if(mSupportedMessageTypes != -1){
+            ret += "Supported msg types: " + mSupportedMessageTypes + "\n";
+        }
+        if(mSupportedFeatures != -1){
+            ret += "Supported features: " + mSupportedFeatures + "\n";
+        }
+        return ret;
+    }
+
+    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
+        public SdpMasRecord createFromParcel(Parcel in) {
+            return new SdpMasRecord(in);
+        }
+        public SdpRecord[] newArray(int size) {
+            return new SdpRecord[size];
+        }
+    };
+}
diff --git a/core/java/android/bluetooth/SdpMnsRecord.java b/core/java/android/bluetooth/SdpMnsRecord.java
new file mode 100644
index 0000000..c02bb5a
--- /dev/null
+++ b/core/java/android/bluetooth/SdpMnsRecord.java
@@ -0,0 +1,112 @@
+/*
+* Copyright (C) 2015 Samsung System LSI
+* 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.os.Parcel;
+import android.os.Parcelable;
+
+/** @hide */
+public class SdpMnsRecord implements Parcelable {
+    private final int mL2capPsm;
+    private final int mRfcommChannelNumber;
+    private final int mSupportedFeatures;
+    private final int mProfileVersion;
+    private final String mServiceName;
+
+    public SdpMnsRecord(int l2cap_psm,
+            int rfcomm_channel_number,
+            int profile_version,
+            int supported_features,
+            String service_name){
+        this.mL2capPsm = l2cap_psm;
+        this.mRfcommChannelNumber = rfcomm_channel_number;
+        this.mSupportedFeatures = supported_features;
+        this.mServiceName = service_name;
+        this.mProfileVersion = profile_version;
+    }
+
+    public SdpMnsRecord(Parcel in){
+           this.mRfcommChannelNumber = in.readInt();
+           this.mL2capPsm = in.readInt();
+           this.mServiceName = in.readString();
+           this.mSupportedFeatures = in.readInt();
+           this.mProfileVersion = in.readInt();
+    }
+    @Override
+    public int describeContents() {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+
+    public int getL2capPsm() {
+        return mL2capPsm;
+    }
+
+    public int getRfcommChannelNumber() {
+        return mRfcommChannelNumber;
+    }
+
+    public int getSupportedFeatures() {
+        return mSupportedFeatures;
+    }
+
+    public String getServiceName() {
+        return mServiceName;
+    }
+
+    public int getProfileVersion() {
+        return mProfileVersion;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mRfcommChannelNumber);
+        dest.writeInt(mL2capPsm);
+        dest.writeString(mServiceName);
+        dest.writeInt(mSupportedFeatures);
+        dest.writeInt(mProfileVersion);
+    }
+
+    public String toString(){
+        String ret = "Bluetooth MNS SDP Record:\n";
+
+        if(mRfcommChannelNumber != -1){
+            ret += "RFCOMM Chan Number: " + mRfcommChannelNumber + "\n";
+        }
+        if(mL2capPsm != -1){
+            ret += "L2CAP PSM: " + mL2capPsm + "\n";
+        }
+        if(mServiceName != null){
+            ret += "Service Name: " + mServiceName + "\n";
+        }
+        if(mSupportedFeatures != -1){
+            ret += "Supported features: " + mSupportedFeatures + "\n";
+        }
+        if(mProfileVersion != -1){
+            ret += "Profile_version: " + mProfileVersion+"\n";
+        }
+        return ret;
+    }
+
+    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
+        public SdpMnsRecord createFromParcel(Parcel in) {
+            return new SdpMnsRecord(in);
+        }
+        public SdpMnsRecord[] newArray(int size) {
+            return new SdpMnsRecord[size];
+        }
+    };
+}
diff --git a/core/java/android/bluetooth/SdpOppOpsRecord.java b/core/java/android/bluetooth/SdpOppOpsRecord.java
new file mode 100644
index 0000000..e0e4007
--- /dev/null
+++ b/core/java/android/bluetooth/SdpOppOpsRecord.java
@@ -0,0 +1,118 @@
+/*
+* Copyright (C) 2015 Samsung System LSI
+* 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.Arrays;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Data representation of a Object Push Profile Server side SDP record.
+ */
+/** @hide */
+public class SdpOppOpsRecord implements Parcelable {
+
+    private final String mServiceName;
+    private final int mRfcommChannel;
+    private final int mL2capPsm;
+    private final int mProfileVersion;
+    private final byte[] mFormatsList;
+
+    public SdpOppOpsRecord(String serviceName, int rfcommChannel,
+            int l2capPsm, int version, byte[] formatsList) {
+        super();
+        this.mServiceName = serviceName;
+        this.mRfcommChannel = rfcommChannel;
+        this.mL2capPsm = l2capPsm;
+        this.mProfileVersion = version;
+        this.mFormatsList = formatsList;
+    }
+
+    public String getServiceName() {
+        return mServiceName;
+    }
+
+    public int getRfcommChannel() {
+        return mRfcommChannel;
+    }
+
+    public int getL2capPsm() {
+        return mL2capPsm;
+    }
+
+    public int getProfileVersion() {
+        return mProfileVersion;
+    }
+
+    public byte[] getFormatsList() {
+        return mFormatsList;
+    }
+
+    @Override
+    public int describeContents() {
+        /* No special objects */
+        return 0;
+    }
+
+    public SdpOppOpsRecord(Parcel in){
+        this.mRfcommChannel = in.readInt();
+        this.mL2capPsm = in.readInt();
+        this.mProfileVersion = in.readInt();
+        this.mServiceName = in.readString();
+        int arrayLength = in.readInt();
+        if(arrayLength > 0) {
+            byte[] bytes = new byte[arrayLength];
+            in.readByteArray(bytes);
+            this.mFormatsList = bytes;
+        } else {
+            this.mFormatsList = null;
+        }
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mRfcommChannel);
+        dest.writeInt(mL2capPsm);
+        dest.writeInt(mProfileVersion);
+        dest.writeString(mServiceName);
+        if(mFormatsList!= null && mFormatsList.length > 0) {
+            dest.writeInt(mFormatsList.length);
+            dest.writeByteArray(mFormatsList);
+        } else {
+            dest.writeInt(0);
+        }
+    }
+
+    public String toString(){
+        StringBuilder sb = new StringBuilder("Bluetooth OPP Server SDP Record:\n");
+        sb.append("  RFCOMM Chan Number: ").append(mRfcommChannel);
+        sb.append("\n  L2CAP PSM: ").append(mL2capPsm);
+        sb.append("\n  Profile version: ").append(mProfileVersion);
+        sb.append("\n  Service Name: ").append(mServiceName);
+        sb.append("\n  Formats List: ").append(Arrays.toString(mFormatsList));
+        return sb.toString();
+    }
+
+    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
+        public SdpOppOpsRecord createFromParcel(Parcel in) {
+            return new SdpOppOpsRecord(in);
+        }
+        public SdpOppOpsRecord[] newArray(int size) {
+            return new SdpOppOpsRecord[size];
+        }
+    };
+
+}
diff --git a/core/java/android/bluetooth/SdpPseRecord.java b/core/java/android/bluetooth/SdpPseRecord.java
new file mode 100644
index 0000000..2c159cc
--- /dev/null
+++ b/core/java/android/bluetooth/SdpPseRecord.java
@@ -0,0 +1,125 @@
+/*
+* Copyright (C) 2015 Samsung System LSI
+* 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.os.Parcel;
+import android.os.Parcelable;
+
+/** @hide */
+public class SdpPseRecord implements Parcelable {
+    private final int mL2capPsm;
+    private final int mRfcommChannelNumber;
+    private final int mProfileVersion;
+    private final int mSupportedFeatures;
+    private final int mSupportedRepositories;
+    private final String mServiceName;
+
+    public SdpPseRecord(int l2cap_psm,
+            int rfcomm_channel_number,
+            int profile_version,
+            int supported_features,
+            int supported_repositories,
+            String service_name){
+        this.mL2capPsm = l2cap_psm;
+        this.mRfcommChannelNumber = rfcomm_channel_number;
+        this.mProfileVersion = profile_version;
+        this.mSupportedFeatures = supported_features;
+        this.mSupportedRepositories = supported_repositories;
+        this.mServiceName = service_name;
+    }
+
+    public SdpPseRecord(Parcel in){
+           this.mRfcommChannelNumber = in.readInt();
+           this.mL2capPsm = in.readInt();
+           this.mProfileVersion = in.readInt();
+           this.mSupportedFeatures = in.readInt();
+           this.mSupportedRepositories = in.readInt();
+           this.mServiceName = in.readString();
+    }
+    @Override
+    public int describeContents() {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    public int getL2capPsm() {
+        return mL2capPsm;
+    }
+
+    public int getRfcommChannelNumber() {
+        return mRfcommChannelNumber;
+    }
+
+    public int getSupportedFeatures() {
+        return mSupportedFeatures;
+    }
+
+    public String getServiceName() {
+        return mServiceName;
+    }
+
+    public int getProfileVersion() {
+        return mProfileVersion;
+    }
+
+    public int getSupportedRepositories() {
+        return mSupportedRepositories;
+    }
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mRfcommChannelNumber);
+        dest.writeInt(mL2capPsm);
+        dest.writeInt(mProfileVersion);
+        dest.writeInt(mSupportedFeatures);
+        dest.writeInt(mSupportedRepositories);
+        dest.writeString(mServiceName);
+
+    }
+
+    public String toString(){
+        String ret = "Bluetooth MNS SDP Record:\n";
+
+        if(mRfcommChannelNumber != -1){
+            ret += "RFCOMM Chan Number: " + mRfcommChannelNumber + "\n";
+        }
+        if(mL2capPsm != -1){
+            ret += "L2CAP PSM: " + mL2capPsm + "\n";
+        }
+        if(mProfileVersion != -1){
+            ret += "profile version: " + mProfileVersion + "\n";
+        }
+        if(mServiceName != null){
+            ret += "Service Name: " + mServiceName + "\n";
+        }
+        if(mSupportedFeatures != -1){
+            ret += "Supported features: " + mSupportedFeatures + "\n";
+        }
+        if(mSupportedRepositories != -1){
+            ret += "Supported repositories: " + mSupportedRepositories + "\n";
+        }
+
+        return ret;
+    }
+
+    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
+        public SdpPseRecord createFromParcel(Parcel in) {
+            return new SdpPseRecord(in);
+        }
+        public SdpPseRecord[] newArray(int size) {
+            return new SdpPseRecord[size];
+        }
+    };
+}
diff --git a/core/java/android/bluetooth/SdpRecord.java b/core/java/android/bluetooth/SdpRecord.java
new file mode 100644
index 0000000..6f1065e
--- /dev/null
+++ b/core/java/android/bluetooth/SdpRecord.java
@@ -0,0 +1,76 @@
+/*
+* Copyright (C) 2015 Samsung System LSI
+* 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.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+
+/** @hide */
+public class SdpRecord implements Parcelable{
+
+    private final byte[] mRawData;
+    private final int mRawSize;
+
+    @Override
+    public String toString() {
+        return "BluetoothSdpRecord [rawData=" + Arrays.toString(mRawData)
+                + ", rawSize=" + mRawSize + "]";
+    }
+
+    public SdpRecord(int size_record, byte[] record){
+        this.mRawData = record;
+        this.mRawSize = size_record;
+    }
+
+    public SdpRecord(Parcel in){
+        this.mRawSize = in.readInt();
+        this.mRawData = new byte[mRawSize];
+        in.readByteArray(this.mRawData);
+
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(this.mRawSize);
+        dest.writeByteArray(this.mRawData);
+
+
+    }
+    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
+        public SdpRecord createFromParcel(Parcel in) {
+            return new SdpRecord(in);
+        }
+
+        public SdpRecord[] newArray(int size) {
+            return new SdpRecord[size];
+        }
+    };
+
+    public byte[] getRawData() {
+        return mRawData;
+    }
+
+    public int getRawSize() {
+        return mRawSize;
+    }
+}
diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java
index 93ea299..e184d1e2 100644
--- a/core/java/android/bluetooth/le/BluetoothLeScanner.java
+++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java
@@ -128,6 +128,16 @@
                         ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED);
                 return;
             }
+            if (!isHardwareResourcesAvailableForScan(settings)) {
+                postCallbackError(callback,
+                        ScanCallback.SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES);
+                return;
+            }
+            if (!isSettingsAndFilterComboAllowed(settings, filters)) {
+                postCallbackError(callback,
+                        ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED);
+                return;
+            }
             BleScanCallbackWrapper wrapper = new BleScanCallbackWrapper(gatt, filters,
                     settings, callback, resultStorages);
             wrapper.startRegisteration();
@@ -394,4 +404,33 @@
         }
         return false;
     }
+
+    private boolean isSettingsAndFilterComboAllowed(ScanSettings settings,
+                        List <ScanFilter> filterList) {
+        final int callbackType = settings.getCallbackType();
+        // If onlost/onfound is requested, a non-empty filter is expected
+        if ((callbackType & (ScanSettings.CALLBACK_TYPE_FIRST_MATCH
+                        | ScanSettings.CALLBACK_TYPE_MATCH_LOST)) != 0) {
+            if (filterList == null) {
+                return false;
+            }
+            for (ScanFilter filter : filterList) {
+                if (filter.isAllFieldsEmpty()) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    private boolean isHardwareResourcesAvailableForScan(ScanSettings settings) {
+        final int callbackType = settings.getCallbackType();
+        if ((callbackType & ScanSettings.CALLBACK_TYPE_FIRST_MATCH) != 0
+                || (callbackType & ScanSettings.CALLBACK_TYPE_MATCH_LOST) != 0) {
+            // For onlost/onfound, we required hw support be available
+            return (mBluetoothAdapter.isOffloadedFilteringSupported() &&
+                    mBluetoothAdapter.isHardwareTrackingFiltersAvailable());
+        }
+        return true;
+    }
 }
diff --git a/core/java/android/bluetooth/le/BluetoothLeUtils.java b/core/java/android/bluetooth/le/BluetoothLeUtils.java
index 4916bd9..c40256b 100644
--- a/core/java/android/bluetooth/le/BluetoothLeUtils.java
+++ b/core/java/android/bluetooth/le/BluetoothLeUtils.java
@@ -132,7 +132,7 @@
      *             {@link BluetoothAdapter#STATE_ON}.
      */
     static void checkAdapterStateOn(BluetoothAdapter adapter) {
-        if (adapter == null || adapter.getState() != BluetoothAdapter.STATE_ON) {
+        if (adapter == null || !adapter.isLeEnabled()) {//adapter.getState() != BluetoothAdapter.STATE_ON) {
             throw new IllegalStateException("BT Adapter is not turned ON");
         }
     }
diff --git a/core/java/android/bluetooth/le/ScanCallback.java b/core/java/android/bluetooth/le/ScanCallback.java
index 05782a8..27b96bd 100644
--- a/core/java/android/bluetooth/le/ScanCallback.java
+++ b/core/java/android/bluetooth/le/ScanCallback.java
@@ -45,10 +45,16 @@
     public static final int SCAN_FAILED_FEATURE_UNSUPPORTED = 4;
 
     /**
+     * Fails to start scan as it is out of hardware resources.
+     * @hide
+     */
+    public static final int SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES = 5;
+
+    /**
      * Callback when a BLE advertisement has been found.
      *
-     * @param callbackType Determines how this callback was triggered. Currently could only be
-     *            {@link ScanSettings#CALLBACK_TYPE_ALL_MATCHES}.
+     * @param callbackType Determines how this callback was triggered. Could be of
+     *            {@link ScanSettings#CALLBACK_TYPE_ALL_MATCHES}
      * @param result A Bluetooth LE scan result.
      */
     public void onScanResult(int callbackType, ScanResult result) {
diff --git a/core/java/android/bluetooth/le/ScanFilter.java b/core/java/android/bluetooth/le/ScanFilter.java
index 5025218..92a3817 100644
--- a/core/java/android/bluetooth/le/ScanFilter.java
+++ b/core/java/android/bluetooth/le/ScanFilter.java
@@ -67,6 +67,8 @@
     private final byte[] mManufacturerData;
     @Nullable
     private final byte[] mManufacturerDataMask;
+    private static final ScanFilter EMPTY = new ScanFilter.Builder().build() ;
+
 
     private ScanFilter(String name, String deviceAddress, ParcelUuid uuid,
             ParcelUuid uuidMask, ParcelUuid serviceDataUuid,
@@ -410,6 +412,14 @@
     }
 
     /**
+     * Checks if the scanfilter is empty
+     * @hide
+     */
+    public boolean isAllFieldsEmpty() {
+        return EMPTY.equals(this);
+    }
+
+    /**
      * Builder class for {@link ScanFilter}.
      */
     public static final class Builder {
diff --git a/core/java/android/bluetooth/le/ScanSettings.java b/core/java/android/bluetooth/le/ScanSettings.java
index 7eae439..f103cae 100644
--- a/core/java/android/bluetooth/le/ScanSettings.java
+++ b/core/java/android/bluetooth/le/ScanSettings.java
@@ -25,6 +25,13 @@
  * parameters for the scan.
  */
 public final class ScanSettings implements Parcelable {
+
+    /**
+     * A special Bluetooth LE scan mode. Applications using this scan mode will passively listen for
+     * other scan results without starting BLE scans themselves.
+     */
+    public static final int SCAN_MODE_OPPORTUNISTIC = -1;
+
     /**
      * Perform Bluetooth LE scan in low power mode. This is the default scan mode as it consumes the
      * least power.
@@ -52,7 +59,6 @@
     /**
      * A result callback is only triggered for the first advertisement packet received that matches
      * the filter criteria.
-     *
      * @hide
      */
     @SystemApi
@@ -61,15 +67,53 @@
     /**
      * Receive a callback when advertisements are no longer received from a device that has been
      * previously reported by a first match callback.
-     *
      * @hide
      */
     @SystemApi
     public static final int CALLBACK_TYPE_MATCH_LOST = 4;
 
+
     /**
-     * Request full scan results which contain the device, rssi, advertising data, scan response as
-     * well as the scan timestamp.
+     * Determines how many advertisements to match per filter, as this is scarce hw resource
+     */
+    /**
+     * Match one advertisement per filter
+     * @hide
+     */
+    public static final int MATCH_NUM_ONE_ADVERTISEMENT = 1;
+
+    /**
+     * Match few advertisement per filter, depends on current capability and availibility of
+     * the resources in hw
+     * @hide
+     */
+    public static final int MATCH_NUM_FEW_ADVERTISEMENT = 2;
+
+    /**
+     * Match as many advertisement per filter as hw could allow, depends on current
+     * capability and availibility of the resources in hw
+     * @hide
+     */
+    public static final int MATCH_NUM_MAX_ADVERTISEMENT = 3;
+
+
+    /**
+     * In Aggressive mode, hw will determine a match sooner even with feeble signal strength
+     * and few number of sightings/match in a duration.
+     * @hide
+     */
+    public static final int MATCH_MODE_AGGRESSIVE = 1;
+
+    /**
+     * For sticky mode, higher threshold of signal strength and sightings is required
+     * before reporting by hw
+     * @hide
+     */
+    public static final int MATCH_MODE_STICKY = 2;
+
+    /**
+     * Request full scan results which contain the device, rssi, advertising data, scan response
+     * as well as the scan timestamp.
      *
      * @hide
      */
@@ -99,6 +143,10 @@
     // Time of delay for reporting the scan result
     private long mReportDelayMillis;
 
+    private int mMatchMode;
+
+    private int mNumOfMatchesPerFilter;
+
     public int getScanMode() {
         return mScanMode;
     }
@@ -112,6 +160,20 @@
     }
 
     /**
+     * @hide
+     */
+    public int getMatchMode() {
+        return mMatchMode;
+    }
+
+    /**
+     * @hide
+     */
+    public int getNumOfMatches() {
+        return mNumOfMatchesPerFilter;
+    }
+
+    /**
      * Returns report delay timestamp based on the device clock.
      */
     public long getReportDelayMillis() {
@@ -119,11 +181,13 @@
     }
 
     private ScanSettings(int scanMode, int callbackType, int scanResultType,
-            long reportDelayMillis) {
+            long reportDelayMillis, int matchMode, int numOfMatchesPerFilter) {
         mScanMode = scanMode;
         mCallbackType = callbackType;
         mScanResultType = scanResultType;
         mReportDelayMillis = reportDelayMillis;
+        mNumOfMatchesPerFilter = numOfMatchesPerFilter;
+        mMatchMode = numOfMatchesPerFilter;
     }
 
     private ScanSettings(Parcel in) {
@@ -131,6 +195,8 @@
         mCallbackType = in.readInt();
         mScanResultType = in.readInt();
         mReportDelayMillis = in.readLong();
+        mMatchMode = in.readInt();
+        mNumOfMatchesPerFilter = in.readInt();
     }
 
     @Override
@@ -139,6 +205,8 @@
         dest.writeInt(mCallbackType);
         dest.writeInt(mScanResultType);
         dest.writeLong(mReportDelayMillis);
+        dest.writeInt(mMatchMode);
+        dest.writeInt(mNumOfMatchesPerFilter);
     }
 
     @Override
@@ -167,7 +235,8 @@
         private int mCallbackType = CALLBACK_TYPE_ALL_MATCHES;
         private int mScanResultType = SCAN_RESULT_TYPE_FULL;
         private long mReportDelayMillis = 0;
-
+        private int mMatchMode = MATCH_MODE_AGGRESSIVE;
+        private int mNumOfMatchesPerFilter  = MATCH_NUM_ONE_ADVERTISEMENT;
         /**
          * Set scan mode for Bluetooth LE scan.
          *
@@ -177,7 +246,7 @@
          * @throws IllegalArgumentException If the {@code scanMode} is invalid.
          */
         public Builder setScanMode(int scanMode) {
-            if (scanMode < SCAN_MODE_LOW_POWER || scanMode > SCAN_MODE_LOW_LATENCY) {
+            if (scanMode < SCAN_MODE_OPPORTUNISTIC || scanMode > SCAN_MODE_LOW_LATENCY) {
                 throw new IllegalArgumentException("invalid scan mode " + scanMode);
             }
             mScanMode = scanMode;
@@ -248,11 +317,48 @@
         }
 
         /**
+         * Set the number of matches for Bluetooth LE scan filters hardware match
+         *
+         * @param numOfMatches The num of matches can be one of
+         *              {@link ScanSettings#MATCH_NUM_ONE_ADVERTISEMENT} or
+         *              {@link ScanSettings#MATCH_NUM_FEW_ADVERTISEMENT} or
+         *              {@link ScanSettings#MATCH_NUM_MAX_ADVERTISEMENT}
+         * @throws IllegalArgumentException If the {@code matchMode} is invalid.
+         * @hide
+         */
+        public Builder setNumOfMatches(int numOfMatches) {
+            if (numOfMatches < MATCH_NUM_ONE_ADVERTISEMENT
+                    || numOfMatches > MATCH_NUM_MAX_ADVERTISEMENT) {
+                throw new IllegalArgumentException("invalid numOfMatches " + numOfMatches);
+            }
+            mNumOfMatchesPerFilter = numOfMatches;
+            return this;
+        }
+
+        /**
+         * Set match mode for Bluetooth LE scan filters hardware match
+         *
+         * @param matchMode The match mode can be one of
+         *              {@link ScanSettings#MATCH_MODE_AGGRESSIVE} or
+         *              {@link ScanSettings#MATCH_MODE_STICKY}
+         * @throws IllegalArgumentException If the {@code matchMode} is invalid.
+         * @hide
+         */
+        public Builder setMatchMode(int matchMode) {
+            if (matchMode < MATCH_MODE_AGGRESSIVE
+                    || matchMode > MATCH_MODE_STICKY) {
+                throw new IllegalArgumentException("invalid matchMode " + matchMode);
+            }
+            mMatchMode = matchMode;
+            return this;
+        }
+
+        /**
          * Build {@link ScanSettings}.
          */
         public ScanSettings build() {
             return new ScanSettings(mScanMode, mCallbackType, mScanResultType,
-                    mReportDelayMillis);
+                    mReportDelayMillis, mMatchMode, mNumOfMatchesPerFilter);
         }
     }
 }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 26735a6..d689ce9 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2116,6 +2116,7 @@
             AUDIO_SERVICE,
             MEDIA_ROUTER_SERVICE,
             TELEPHONY_SERVICE,
+            CARRIER_CONFIG_SERVICE,
             TELECOM_SERVICE,
             CLIPBOARD_SERVICE,
             INPUT_METHOD_SERVICE,
@@ -2248,6 +2249,8 @@
      * @see android.telephony.TelephonyManager
      * @see #TELEPHONY_SUBSCRIPTION_SERVICE
      * @see android.telephony.SubscriptionManager
+     * @see #CARRIER_CONFIG_SERVICE
+     * @see android.telephony.CarrierConfigManager
      * @see #INPUT_METHOD_SERVICE
      * @see android.view.inputmethod.InputMethodManager
      * @see #UI_MODE_SERVICE
@@ -2612,6 +2615,15 @@
 
     /**
      * Use with {@link #getSystemService} to retrieve a
+     * {@link android.telephony.CarrierConfigManager} for reading carrier configuration values.
+     *
+     * @see #getSystemService
+     * @see android.telephony.CarrierConfigManager
+     */
+    public static final String CARRIER_CONFIG_SERVICE = "carrier_config";
+
+    /**
+     * Use with {@link #getSystemService} to retrieve a
      * {@link android.text.ClipboardManager} for accessing and modifying
      * the contents of the global clipboard.
      *
diff --git a/core/java/android/hardware/ICameraService.aidl b/core/java/android/hardware/ICameraService.aidl
index 2bc3dd4..9aede01 100644
--- a/core/java/android/hardware/ICameraService.aidl
+++ b/core/java/android/hardware/ICameraService.aidl
@@ -81,4 +81,6 @@
                     int clientUid,
                     // Container for an ICamera object
                     out BinderHolder device);
+
+    int setTorchMode(String CameraId, boolean enabled, IBinder clientBinder);
 }
diff --git a/core/java/android/hardware/ICameraServiceListener.aidl b/core/java/android/hardware/ICameraServiceListener.aidl
index c5484965..49278b6 100644
--- a/core/java/android/hardware/ICameraServiceListener.aidl
+++ b/core/java/android/hardware/ICameraServiceListener.aidl
@@ -23,4 +23,6 @@
      * Keep up-to-date with frameworks/av/include/camera/ICameraServiceListener.h
      */
     void onStatusChanged(int status, int cameraId);
+
+    void onTorchStatusChanged(int status, String cameraId);
 }
diff --git a/core/java/android/hardware/camera2/CameraAccessException.java b/core/java/android/hardware/camera2/CameraAccessException.java
index 91ef6ca..84e9912 100644
--- a/core/java/android/hardware/camera2/CameraAccessException.java
+++ b/core/java/android/hardware/camera2/CameraAccessException.java
@@ -28,16 +28,14 @@
  */
 public class CameraAccessException extends AndroidException {
     /**
-     * The camera device is in use already
-     * @hide
+     * The camera device is in use already.
      */
     public static final int CAMERA_IN_USE = 4;
 
     /**
-     * The system-wide limit for number of open cameras has been reached,
-     * and more camera devices cannot be opened until previous instances are
-     * closed.
-     * @hide
+     * The system-wide limit for number of open cameras or camera resources has
+     * been reached, and more camera devices cannot be opened or torch mode
+     * cannot be turned on until previous instances are closed.
      */
     public static final int MAX_CAMERAS_IN_USE = 5;
 
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 7cf8fb0..9a29897 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -638,6 +638,44 @@
             new Key<android.hardware.camera2.params.HighSpeedVideoConfiguration[]>("android.control.availableHighSpeedVideoConfigurations", android.hardware.camera2.params.HighSpeedVideoConfiguration[].class);
 
     /**
+     * <p>Whether the camera device supports {@link CaptureRequest#CONTROL_AE_LOCK android.control.aeLock}</p>
+     * <p>LIMITED or FULL devices will always list <code>true</code></p>
+     * <p>This key is available on all devices.</p>
+     *
+     * @see CaptureRequest#CONTROL_AE_LOCK
+     */
+    @PublicKey
+    public static final Key<Boolean> CONTROL_AE_LOCK_AVAILABLE =
+            new Key<Boolean>("android.control.aeLockAvailable", boolean.class);
+
+    /**
+     * <p>Whether the camera device supports {@link CaptureRequest#CONTROL_AWB_LOCK android.control.awbLock}</p>
+     * <p>LIMITED or FULL devices will always list <code>true</code></p>
+     * <p>This key is available on all devices.</p>
+     *
+     * @see CaptureRequest#CONTROL_AWB_LOCK
+     */
+    @PublicKey
+    public static final Key<Boolean> CONTROL_AWB_LOCK_AVAILABLE =
+            new Key<Boolean>("android.control.awbLockAvailable", boolean.class);
+
+    /**
+     * <p>List of control modes for {@link CaptureRequest#CONTROL_MODE android.control.mode} that are supported by this camera
+     * device.</p>
+     * <p>This list contains control modes that can be set for the camera device.
+     * LEGACY mode devices will always support AUTO mode. LIMITED and FULL
+     * devices will always support OFF, AUTO modes.</p>
+     * <p><b>Range of valid values:</b><br>
+     * Any value listed in {@link CaptureRequest#CONTROL_MODE android.control.mode}</p>
+     * <p>This key is available on all devices.</p>
+     *
+     * @see CaptureRequest#CONTROL_MODE
+     */
+    @PublicKey
+    public static final Key<int[]> CONTROL_AVAILABLE_MODES =
+            new Key<int[]>("android.control.availableModes", int[].class);
+
+    /**
      * <p>List of edge enhancement modes for {@link CaptureRequest#EDGE_MODE android.edge.mode} that are supported by this camera
      * device.</p>
      * <p>Full-capability camera devices must always support OFF; all devices will list FAST.</p>
@@ -889,16 +927,141 @@
      * <ul>
      *   <li>{@link #LENS_FACING_FRONT FRONT}</li>
      *   <li>{@link #LENS_FACING_BACK BACK}</li>
+     *   <li>{@link #LENS_FACING_EXTERNAL EXTERNAL}</li>
      * </ul></p>
      * <p>This key is available on all devices.</p>
      * @see #LENS_FACING_FRONT
      * @see #LENS_FACING_BACK
+     * @see #LENS_FACING_EXTERNAL
      */
     @PublicKey
     public static final Key<Integer> LENS_FACING =
             new Key<Integer>("android.lens.facing", int.class);
 
     /**
+     * <p>The orientation of the camera relative to the sensor
+     * coordinate system.</p>
+     * <p>The four coefficients that describe the quarternion
+     * rotation from the Android sensor coordinate system to a
+     * camera-aligned coordinate system where the X-axis is
+     * aligned with the long side of the image sensor, the Y-axis
+     * is aligned with the short side of the image sensor, and
+     * the Z-axis is aligned with the optical axis of the sensor.</p>
+     * <p>To convert from the quarternion coefficients <code>(x,y,z,w)</code>
+     * to the axis of rotation <code>(a_x, a_y, a_z)</code> and rotation
+     * amount <code>theta</code>, the following formulas can be used:</p>
+     * <pre><code> theta = 2 * acos(w)
+     * a_x = x / sin(theta/2)
+     * a_y = y / sin(theta/2)
+     * a_z = z / sin(theta/2)
+     * </code></pre>
+     * <p>To create a 3x3 rotation matrix that applies the rotation
+     * defined by this quarternion, the following matrix can be
+     * used:</p>
+     * <pre><code>R = [ 1 - 2y^2 - 2z^2,       2xy - 2zw,       2xz + 2yw,
+     *            2xy + 2zw, 1 - 2x^2 - 2z^2,       2yz - 2xw,
+     *            2xz - 2yw,       2yz + 2xw, 1 - 2x^2 - 2y^2 ]
+     * </code></pre>
+     * <p>This matrix can then be used to apply the rotation to a
+     *  column vector point with</p>
+     * <p><code>p' = Rp</code></p>
+     * <p>where <code>p</code> is in the device sensor coordinate system, and
+     *  <code>p'</code> is in the camera-oriented coordinate system.</p>
+     * <p><b>Units</b>:
+     * Quarternion coefficients</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     */
+    @PublicKey
+    public static final Key<float[]> LENS_POSE_ROTATION =
+            new Key<float[]>("android.lens.poseRotation", float[].class);
+
+    /**
+     * <p>Position of the camera optical center.</p>
+     * <p>As measured in the device sensor coordinate system, the
+     * position of the camera device's optical center, as a
+     * three-dimensional vector <code>(x,y,z)</code>.</p>
+     * <p>To transform a world position to a camera-device centered
+     * coordinate system, the position must be translated by this
+     * vector and then rotated by {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}.</p>
+     * <p><b>Units</b>: Meters</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CameraCharacteristics#LENS_POSE_ROTATION
+     */
+    @PublicKey
+    public static final Key<float[]> LENS_POSE_TRANSLATION =
+            new Key<float[]>("android.lens.poseTranslation", float[].class);
+
+    /**
+     * <p>The parameters for this camera device's intrinsic
+     * calibration.</p>
+     * <p>The five calibration parameters that describe the
+     * transform from camera-centric 3D coordinates to sensor
+     * pixel coordinates:</p>
+     * <pre><code>[f_x, f_y, c_x, c_y, s]
+     * </code></pre>
+     * <p>Where <code>f_x</code> and <code>f_y</code> are the horizontal and vertical
+     * focal lengths, <code>[c_x, c_y]</code> is the position of the optical
+     * axis, and <code>s</code> is a skew parameter for the sensor plane not
+     * being aligned with the lens plane.</p>
+     * <p>These are typically used within a transformation matrix K:</p>
+     * <pre><code>K = [ f_x,   s, c_x,
+     *        0, f_y, c_y,
+     *        0    0,   1 ]
+     * </code></pre>
+     * <p>which can then be combined with the camera pose rotation
+     * <code>R</code> and translation <code>t</code> ({@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} and
+     * {@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}, respective) to calculate the
+     * complete transform from world coordinates to pixel
+     * coordinates:</p>
+     * <pre><code>P = [ K 0   * [ R t
+     *      0 1 ]     0 1 ]
+     * </code></pre>
+     * <p>and with <code>p_w</code> being a point in the world coordinate system
+     * and <code>p_s</code> being a point in the camera active pixel array
+     * coordinate system, and with the mapping including the
+     * homogeneous division by z:</p>
+     * <pre><code> p_h = (x_h, y_h, z_h) = P p_w
+     * p_s = p_h / z_h
+     * </code></pre>
+     * <p>so <code>[x_s, y_s]</code> is the pixel coordinates of the world
+     * point, <code>z_s = 1</code>, and <code>w_s</code> is a measurement of disparity
+     * (depth) in pixel coordinates.</p>
+     * <p><b>Units</b>:
+     * Pixels in the android.sensor.activeArraySize coordinate
+     * system.</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CameraCharacteristics#LENS_POSE_ROTATION
+     * @see CameraCharacteristics#LENS_POSE_TRANSLATION
+     */
+    @PublicKey
+    public static final Key<float[]> LENS_INTRINSIC_CALIBRATION =
+            new Key<float[]>("android.lens.intrinsicCalibration", float[].class);
+
+    /**
+     * <p>The correction coefficients to correct for this camera device's
+     * radial lens distortion.</p>
+     * <p>Three cofficients <code>[kappa_1, kappa_2, kappa_3]</code> that
+     * can be used to correct the lens's radial geometric
+     * distortion with the mapping equations:</p>
+     * <pre><code> x_c = x_i * ( 1 + kappa_1 * r^2 + kappa_2 * r^4 + kappa_3 * r^6 )
+     * y_c = y_i * ( 1 + kappa_1 * r^2 + kappa_2 * r^4 + kappa_3 * r^6 )
+     * </code></pre>
+     * <p>where <code>[x_i, y_i]</code> are normalized coordinates with <code>(0,0)</code>
+     * at the lens optical center, and <code>[-1, 1]</code> are the edges of
+     * the active pixel array; and where <code>[x_c, y_c]</code> are the
+     * corrected normalized coordinates with radial distortion
+     * removed; and <code>r^2 = x_i^2 + y_i^2</code>.</p>
+     * <p><b>Units</b>:
+     * Coefficients for a 6th-degree even radial polynomial.</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     */
+    @PublicKey
+    public static final Key<float[]> LENS_RADIAL_DISTORTION =
+            new Key<float[]>("android.lens.radialDistortion", float[].class);
+
+    /**
      * <p>List of noise reduction modes for {@link CaptureRequest#NOISE_REDUCTION_MODE android.noiseReduction.mode} that are supported
      * by this camera device.</p>
      * <p>Full-capability camera devices will always support OFF and FAST.</p>
@@ -1069,8 +1232,11 @@
      * android.scaler.availableInputOutputFormatsMap. When using an
      * input stream, there must be at least one output stream
      * configured to to receive the reprocessed images.</p>
+     * <p>When an input stream and some output streams are used in a reprocessing request,
+     * only the input buffer will be used to produce these output stream buffers, and a
+     * new sensor image will not be captured.</p>
      * <p>For example, for Zero Shutter Lag (ZSL) still capture use case, the input
-     * stream image format will be RAW_OPAQUE, the associated output stream image format
+     * stream image format will be OPAQUE, the associated output stream image format
      * should be JPEG.</p>
      * <p><b>Range of valid values:</b><br></p>
      * <p>0 or 1.</p>
@@ -1080,8 +1246,8 @@
      * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
      *
      * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
-     * @hide
      */
+    @PublicKey
     public static final Key<Integer> REQUEST_MAX_NUM_INPUT_STREAMS =
             new Key<Integer>("android.request.maxNumInputStreams", int.class);
 
@@ -1157,8 +1323,10 @@
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR MANUAL_SENSOR}</li>
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING MANUAL_POST_PROCESSING}</li>
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_RAW RAW}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_OPAQUE_REPROCESSING OPAQUE_REPROCESSING}</li>
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS READ_SENSOR_SETTINGS}</li>
      *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE BURST_CAPTURE}</li>
+     *   <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING YUV_REPROCESSING}</li>
      * </ul></p>
      * <p>This key is available on all devices.</p>
      *
@@ -1167,8 +1335,10 @@
      * @see #REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR
      * @see #REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING
      * @see #REQUEST_AVAILABLE_CAPABILITIES_RAW
+     * @see #REQUEST_AVAILABLE_CAPABILITIES_OPAQUE_REPROCESSING
      * @see #REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS
      * @see #REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE
+     * @see #REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING
      */
     @PublicKey
     public static final Key<int[]> REQUEST_AVAILABLE_CAPABILITIES =
@@ -1345,10 +1515,10 @@
      * <p>The mapping of image formats that are supported by this
      * camera device for input streams, to their corresponding output formats.</p>
      * <p>All camera devices with at least 1
-     * android.request.maxNumInputStreams will have at least one
+     * {@link CameraCharacteristics#REQUEST_MAX_NUM_INPUT_STREAMS android.request.maxNumInputStreams} will have at least one
      * available input format.</p>
      * <p>The camera device will support the following map of formats,
-     * if its dependent capability is supported:</p>
+     * if its dependent capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}) is supported:</p>
      * <table>
      * <thead>
      * <tr>
@@ -1359,45 +1529,42 @@
      * </thead>
      * <tbody>
      * <tr>
-     * <td align="left">RAW_OPAQUE</td>
+     * <td align="left">OPAQUE</td>
      * <td align="left">JPEG</td>
-     * <td align="left">ZSL</td>
+     * <td align="left">OPAQUE_REPROCESSING</td>
      * </tr>
      * <tr>
-     * <td align="left">RAW_OPAQUE</td>
+     * <td align="left">OPAQUE</td>
      * <td align="left">YUV_420_888</td>
-     * <td align="left">ZSL</td>
+     * <td align="left">OPAQUE_REPROCESSING</td>
      * </tr>
      * <tr>
-     * <td align="left">RAW_OPAQUE</td>
-     * <td align="left">RAW16</td>
-     * <td align="left">RAW</td>
-     * </tr>
-     * <tr>
-     * <td align="left">RAW16</td>
      * <td align="left">YUV_420_888</td>
-     * <td align="left">RAW</td>
-     * </tr>
-     * <tr>
-     * <td align="left">RAW16</td>
      * <td align="left">JPEG</td>
-     * <td align="left">RAW</td>
+     * <td align="left">YUV_REPROCESSING</td>
+     * </tr>
+     * <tr>
+     * <td align="left">YUV_420_888</td>
+     * <td align="left">YUV_420_888</td>
+     * <td align="left">YUV_REPROCESSING</td>
      * </tr>
      * </tbody>
      * </table>
-     * <p>For ZSL-capable camera devices, using the RAW_OPAQUE format
+     * <p>OPAQUE refers to a device-internal format that is not directly application-visible.
+     * An OPAQUE input or output surface can be acquired by
+     * OpaqueImageRingBufferQueue#getInputSurface() or
+     * OpaqueImageRingBufferQueue#getOutputSurface().
+     * For a OPAQUE_REPROCESSING-capable camera device, using the OPAQUE format
      * as either input or output will never hurt maximum frame rate (i.e.
-     * StreamConfigurationMap#getOutputStallDuration(int,Size)
-     * for a <code>format =</code> RAW_OPAQUE is always 0).</p>
+     * StreamConfigurationMap#getOutputStallDuration(klass,Size) is always 0),
+     * where klass is android.media.OpaqueImageRingBufferQueue.class.</p>
      * <p>Attempting to configure an input stream with output streams not
      * listed as available in this map is not valid.</p>
      * <p>TODO: typedef to ReprocessFormatMap</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
-     * <p><b>Full capability</b> -
-     * Present on all camera devices that report being {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_FULL HARDWARE_LEVEL_FULL} devices in the
-     * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
      *
-     * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+     * @see CameraCharacteristics#REQUEST_MAX_NUM_INPUT_STREAMS
      * @hide
      */
     public static final Key<int[]> SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP =
@@ -1899,6 +2066,23 @@
             new Key<Integer>("android.sensor.info.timestampSource", int.class);
 
     /**
+     * <p>Whether the RAW images output from this camera device are subject to
+     * lens shading correction.</p>
+     * <p>If TRUE, all images produced by the camera device in the RAW image formats will
+     * have lens shading correction already applied to it. If FALSE, the images will
+     * not be adjusted for lens shading correction.
+     * See {@link CameraCharacteristics#REQUEST_MAX_NUM_OUTPUT_RAW android.request.maxNumOutputRaw} for a list of RAW image formats.</p>
+     * <p>This key will be <code>null</code> for all devices do not report this information.
+     * Devices with RAW capability will always report this information in this key.</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CameraCharacteristics#REQUEST_MAX_NUM_OUTPUT_RAW
+     */
+    @PublicKey
+    public static final Key<Boolean> SENSOR_INFO_LENS_SHADING_APPLIED =
+            new Key<Boolean>("android.sensor.info.lensShadingApplied", boolean.class);
+
+    /**
      * <p>The standard reference illuminant used as the scene light source when
      * calculating the {@link CameraCharacteristics#SENSOR_COLOR_TRANSFORM1 android.sensor.colorTransform1},
      * {@link CameraCharacteristics#SENSOR_CALIBRATION_TRANSFORM1 android.sensor.calibrationTransform1}, and
@@ -2189,6 +2373,22 @@
             new Key<int[]>("android.sensor.availableTestPatternModes", int[].class);
 
     /**
+     * <p>List of lens shading modes for {@link CaptureRequest#SHADING_MODE android.shading.mode} that are supported by this camera device.</p>
+     * <p>This list contains lens shading modes that can be set for the camera device.
+     * Camera devices that support the MANUAL_POST_PROCESSING capability will always
+     * list OFF and FAST mode. This includes all FULL level devices.
+     * LEGACY devices will always only support FAST mode.</p>
+     * <p><b>Range of valid values:</b><br>
+     * Any value listed in {@link CaptureRequest#SHADING_MODE android.shading.mode}</p>
+     * <p>This key is available on all devices.</p>
+     *
+     * @see CaptureRequest#SHADING_MODE
+     */
+    @PublicKey
+    public static final Key<int[]> SHADING_AVAILABLE_MODES =
+            new Key<int[]>("android.shading.availableModes", int[].class);
+
+    /**
      * <p>List of face detection modes for {@link CaptureRequest#STATISTICS_FACE_DETECT_MODE android.statistics.faceDetectMode} that are
      * supported by this camera device.</p>
      * <p>OFF is always supported.</p>
@@ -2232,6 +2432,23 @@
             new Key<boolean[]>("android.statistics.info.availableHotPixelMapModes", boolean[].class);
 
     /**
+     * <p>List of lens shading map output modes for {@link CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE android.statistics.lensShadingMapMode} that
+     * are supported by this camera device.</p>
+     * <p>If no lens shading map output is available for this camera device, this key will
+     * contain only OFF.</p>
+     * <p>ON is always supported on devices with the RAW capability.
+     * LEGACY mode devices will always only support OFF.</p>
+     * <p><b>Range of valid values:</b><br>
+     * Any value listed in {@link CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE android.statistics.lensShadingMapMode}</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE
+     */
+    @PublicKey
+    public static final Key<byte[]> STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES =
+            new Key<byte[]>("android.statistics.info.availableLensShadingMapModes", byte[].class);
+
+    /**
      * <p>Maximum number of supported points in the
      * tonemap curve that can be used for {@link CaptureRequest#TONEMAP_CURVE android.tonemap.curve}.</p>
      * <p>If the actual number of points provided by the application (in {@link CaptureRequest#TONEMAP_CURVE android.tonemap.curve}*) is
@@ -2255,8 +2472,13 @@
     /**
      * <p>List of tonemapping modes for {@link CaptureRequest#TONEMAP_MODE android.tonemap.mode} that are supported by this camera
      * device.</p>
-     * <p>Camera devices that support the MANUAL_POST_PROCESSING capability will always list
-     * CONTRAST_CURVE and FAST. This includes all FULL level devices.</p>
+     * <p>Camera devices that support the MANUAL_POST_PROCESSING capability will always contain
+     * at least one of below mode combinations:</p>
+     * <ul>
+     * <li>CONTRAST_CURVE and FAST</li>
+     * <li>GAMMA_VALUE, PRESET_CURVE, and FAST</li>
+     * </ul>
+     * <p>This includes all FULL level devices.</p>
      * <p><b>Range of valid values:</b><br>
      * Any value listed in {@link CaptureRequest#TONEMAP_MODE android.tonemap.mode}</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
@@ -2394,6 +2616,83 @@
     public static final Key<Integer> SYNC_MAX_LATENCY =
             new Key<Integer>("android.sync.maxLatency", int.class);
 
+    /**
+     * <p>The available depth dataspace stream
+     * configurations that this camera device supports
+     * (i.e. format, width, height, output/input stream).</p>
+     * <p>These are output stream configurations for use with
+     * dataSpace HAL_DATASPACE_DEPTH. The configurations are
+     * listed as <code>(format, width, height, input?)</code> tuples.</p>
+     * <p>Only devices that support depth output for at least
+     * the HAL_PIXEL_FORMAT_Y16 dense depth map may include
+     * this entry.</p>
+     * <p>A device that also supports the HAL_PIXEL_FORMAT_BLOB
+     * sparse depth point cloud must report a single entry for
+     * the format in this list as <code>(HAL_PIXEL_FORMAT_BLOB,
+     * android.depth.maxDepthSamples, 1, OUTPUT)</code> in addition to
+     * the entries for HAL_PIXEL_FORMAT_Y16.</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     * <p><b>Limited capability</b> -
+     * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
+     * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
+     *
+     * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+     * @hide
+     */
+    public static final Key<android.hardware.camera2.params.StreamConfiguration[]> DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS =
+            new Key<android.hardware.camera2.params.StreamConfiguration[]>("android.depth.availableDepthStreamConfigurations", android.hardware.camera2.params.StreamConfiguration[].class);
+
+    /**
+     * <p>This lists the minimum frame duration for each
+     * format/size combination for depth output formats.</p>
+     * <p>This should correspond to the frame duration when only that
+     * stream is active, with all processing (typically in android.*.mode)
+     * set to either OFF or FAST.</p>
+     * <p>When multiple streams are used in a request, the minimum frame
+     * duration will be max(individual stream min durations).</p>
+     * <p>The minimum frame duration of a stream (of a particular format, size)
+     * is the same regardless of whether the stream is input or output.</p>
+     * <p>See {@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration} and
+     * android.scaler.availableStallDurations for more details about
+     * calculating the max frame rate.</p>
+     * <p>(Keep in sync with
+     * StreamConfigurationMap#getOutputMinFrameDuration)</p>
+     * <p><b>Units</b>: (format, width, height, ns) x n</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     * <p><b>Limited capability</b> -
+     * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
+     * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
+     *
+     * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+     * @see CaptureRequest#SENSOR_FRAME_DURATION
+     * @hide
+     */
+    public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS =
+            new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.depth.availableDepthMinFrameDurations", android.hardware.camera2.params.StreamConfigurationDuration[].class);
+
+    /**
+     * <p>This lists the maximum stall duration for each
+     * format/size combination for depth streams.</p>
+     * <p>A stall duration is how much extra time would get added
+     * to the normal minimum frame duration for a repeating request
+     * that has streams with non-zero stall.</p>
+     * <p>This functions similarly to
+     * android.scaler.availableStallDurations for depth
+     * streams.</p>
+     * <p>All depth output stream formats may have a nonzero stall
+     * duration.</p>
+     * <p><b>Units</b>: (format, width, height, ns) x n</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     * <p><b>Limited capability</b> -
+     * Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
+     * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
+     *
+     * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+     * @hide
+     */
+    public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS =
+            new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.depth.availableDepthStallDurations", android.hardware.camera2.params.StreamConfigurationDuration[].class);
+
     /*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
      * End generated code
      *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 482b1f0..444419d 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -18,6 +18,7 @@
 
 import android.hardware.camera2.params.StreamConfigurationMap;
 import android.graphics.ImageFormat;
+import android.hardware.camera2.params.OutputConfiguration;
 import android.os.Handler;
 import android.view.Surface;
 
@@ -183,7 +184,7 @@
      *   Then obtain the Surface with
      *   {@link android.renderscript.Allocation#getSurface}.</li>
      *
-     * <li>For access to raw, uncompressed JPEG data in the application: Create an
+     * <li>For access to RAW, uncompressed YUV, or compressed JPEG data in the application: Create an
      *   {@link android.media.ImageReader} object with one of the supported output formats given by
      *   {@link StreamConfigurationMap#getOutputFormats()}, setting its size to one of the
      *   corresponding supported sizes by passing the chosen output format into
@@ -381,6 +382,20 @@
             throws CameraAccessException;
 
     /**
+     * <p>Create a new camera capture session by providing the target output set of Surfaces and
+     * its corresponding surface configuration to the camera device.</p>
+     *
+     * @see #createCaptureSession
+     * @see OutputConfiguration
+     *
+     * @hide
+     */
+    public abstract void createCaptureSessionByOutputConfiguration(
+            List<OutputConfiguration> outputConfigurations,
+            CameraCaptureSession.StateCallback callback, Handler handler)
+            throws CameraAccessException;
+
+    /**
      * <p>Create a {@link CaptureRequest.Builder} for new capture requests,
      * initialized with template for a target use case. The settings are chosen
      * to be the best options for the specific camera device, so it is not
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index b6bb33b..faa782a 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -27,6 +27,7 @@
 import android.hardware.camera2.utils.CameraRuntimeException;
 import android.hardware.camera2.utils.BinderHolder;
 import android.os.IBinder;
+import android.os.Binder;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.RemoteException;
@@ -109,8 +110,11 @@
      * of the state of individual CameraManager instances.</p>
      *
      * @param callback the new callback to send camera availability notices to
-     * @param handler The handler on which the callback should be invoked, or
-     * {@code null} to use the current thread's {@link android.os.Looper looper}.
+     * @param handler The handler on which the callback should be invoked, or {@code null} to use
+     *             the current thread's {@link android.os.Looper looper}.
+     *
+     * @throws IllegalArgumentException if the handler is {@code null} but the current thread has
+     *             no looper.
      */
     public void registerAvailabilityCallback(AvailabilityCallback callback, Handler handler) {
         if (handler == null) {
@@ -138,6 +142,52 @@
     }
 
     /**
+     * Register a callback to be notified about torch mode status.
+     *
+     * <p>Registering the same callback again will replace the handler with the
+     * new one provided.</p>
+     *
+     * <p>The first time a callback is registered, it is immediately called
+     * with the torch mode status of all currently known camera devices.</p>
+     *
+     * <p>Since this callback will be registered with the camera service, remember to unregister it
+     * once it is no longer needed; otherwise the callback will continue to receive events
+     * indefinitely and it may prevent other resources from being released. Specifically, the
+     * callbacks will be invoked independently of the general activity lifecycle and independently
+     * of the state of individual CameraManager instances.</p>
+     *
+     * @param callback The new callback to send torch mode status to
+     * @param handler The handler on which the callback should be invoked, or {@code null} to use
+     *             the current thread's {@link android.os.Looper looper}.
+     *
+     * @throws IllegalArgumentException if the handler is {@code null} but the current thread has
+     *             no looper.
+     */
+    public void registerTorchCallback(TorchCallback callback, Handler handler) {
+        if (handler == null) {
+            Looper looper = Looper.myLooper();
+            if (looper == null) {
+                throw new IllegalArgumentException(
+                        "No handler given, and current thread has no looper!");
+            }
+            handler = new Handler(looper);
+        }
+        CameraManagerGlobal.get().registerTorchCallback(callback, handler);
+    }
+
+    /**
+     * Remove a previously-added callback; the callback will no longer receive torch mode status
+     * callbacks.
+     *
+     * <p>Removing a callback that isn't registered has no effect.</p>
+     *
+     * @param callback The callback to remove from the notification list
+     */
+    public void unregisterTorchCallback(TorchCallback callback) {
+        CameraManagerGlobal.get().unregisterTorchCallback(callback);
+    }
+
+    /**
      * <p>Query the capabilities of a camera device. These capabilities are
      * immutable for a given camera.</p>
      *
@@ -384,6 +434,49 @@
     }
 
     /**
+     * Set the flash unit's torch mode of the camera of the given ID without opening the camera
+     * device.
+     *
+     * <p>Use {@link #getCameraIdList} to get the list of available camera devices and use
+     * {@link #getCameraCharacteristics} to check whether the camera device has a flash unit.
+     * Note that even if a camera device has a flash unit, turning on the torch mode may fail
+     * if the camera device or other camera resources needed to turn on the torch mode are in use.
+     * </p>
+     *
+     * <p> If {@link #setTorchMode} is called to turn on or off the torch mode successfully,
+     * {@link CameraManager.TorchCallback#onTorchModeChanged} will be invoked.
+     * However, even if turning on the torch mode is successful, the application does not have the
+     * exclusive ownership of the flash unit or the camera device. The torch mode will be turned
+     * off and becomes unavailable when the camera device that the flash unit belongs to becomes
+     * unavailable or when other camera resources to keep the torch on become unavailable (
+     * {@link CameraManager.TorchCallback#onTorchModeUnavailable} will be invoked). Also,
+     * other applications are free to call {@link #setTorchMode} to turn off the torch mode (
+     * {@link CameraManager.TorchCallback#onTorchModeChanged} will be invoked). If the latest
+     * application that turned on the torch mode exits, the torch mode will be turned off.
+     *
+     * @param cameraId
+     *             The unique identifier of the camera device that the flash unit belongs to.
+     * @param enabled
+     *             The desired state of the torch mode for the target camera device. Set to
+     *             {@code true} to turn on the torch mode. Set to {@code false} to turn off the
+     *             torch mode.
+     *
+     * @throws CameraAccessException if it failed to access the flash unit.
+     *             {@link CameraAccessException#CAMERA_IN_USE} will be thrown if the camera device
+     *             is in use. {@link CameraAccessException#MAX_CAMERAS_IN_USE} will be thrown if
+     *             other camera resources needed to turn on the torch mode are in use.
+     *             {@link CameraAccessException#CAMERA_DISCONNECTED} will be thrown if camera
+     *             service is not available.
+     *
+     * @throws IllegalArgumentException if cameraId was null, cameraId doesn't match any currently
+     *             or previously available camera device, or the camera device doesn't have a
+     *             flash unit.
+     */
+    public void setTorchMode(String cameraId, boolean enabled) throws CameraAccessException {
+        CameraManagerGlobal.get().setTorchMode(cameraId, enabled);
+    }
+
+    /**
      * A callback for camera devices becoming available or
      * unavailable to open.
      *
@@ -428,6 +521,63 @@
     }
 
     /**
+     * A callback for camera flash torch modes becoming unavailable, disabled, or enabled.
+     *
+     * <p>The torch mode becomes unavailable when the camera device it belongs to becomes
+     * unavailable or other camera resouces it needs become busy due to other higher priority
+     * camera activities. The torch mode becomes disabled when it was turned off or when the camera
+     * device it belongs to is no longer in use and other camera resources it needs are no longer
+     * busy. A camera's torch mode is turned off when an application calls {@link #setTorchMode} to
+     * turn off the camera's torch mode, or when an application turns on another camera's torch mode
+     * if keeping multiple torch modes on simultaneously is not supported. The torch mode becomes
+     * enabled when it is turned on via {@link #setTorchMode}.</p>
+     *
+     * <p>The torch mode is available to set via {@link #setTorchMode} only when it's in a disabled
+     * or enabled state.</p>
+     *
+     * <p>Extend this callback and pass an instance of the subclass to
+     * {@link CameraManager#registerTorchCallback} to be notified of such status changes.
+     * </p>
+     *
+     * @see registerTorchCallback
+     */
+    public static abstract class TorchCallback {
+        /**
+         * A camera's torch mode has become unavailable to set via {@link #setTorchMode}.
+         *
+         * <p>If torch mode was previously turned on by calling {@link #setTorchMode}, it will be
+         * turned off before {@link CameraManager.TorchCallback#onTorchModeUnavailable} is
+         * invoked. {@link #setTorchMode} will fail until the torch mode has entered a disabled or
+         * enabled state again.</p>
+         *
+         * <p>The default implementation of this method does nothing.</p>
+         *
+         * @param cameraId The unique identifier of the camera whose torch mode has become
+         *                 unavailable.
+         */
+        public void onTorchModeUnavailable(String cameraId) {
+            // default empty implementation
+        }
+
+        /**
+         * A camera's torch mode has become enabled or disabled and can be changed via
+         * {@link #setTorchMode}.
+         *
+         * <p>The default implementation of this method does nothing.</p>
+         *
+         * @param cameraId The unique identifier of the camera whose torch mode has been changed.
+         *
+         * @param enabled The state that the torch mode of the camera has been changed to.
+         *                {@code true} when the torch mode has become on and available to be turned
+         *                off. {@code false} when the torch mode has becomes off and available to
+         *                be turned on.
+         */
+        public void onTorchModeChanged(String cameraId, boolean enabled) {
+            // default empty implementation
+        }
+    }
+
+    /**
      * Return or create the list of currently connected camera devices.
      *
      * <p>In case of errors connecting to the camera service, will return an empty list.</p>
@@ -583,6 +733,27 @@
         private final ArrayMap<AvailabilityCallback, Handler> mCallbackMap =
             new ArrayMap<AvailabilityCallback, Handler>();
 
+        // Keep up-to-date with ICameraServiceListener.h
+
+        // torch mode has become not available to set via setTorchMode().
+        public static final int TORCH_STATUS_NOT_AVAILABLE = 0;
+        // torch mode is off and available to be turned on via setTorchMode().
+        public static final int TORCH_STATUS_AVAILABLE_OFF = 1;
+        // torch mode is on and available to be turned off via setTorchMode().
+        public static final int TORCH_STATUS_AVAILABLE_ON = 2;
+
+        // End enums shared with ICameraServiceListener.h
+
+        // torch client binder to set the torch mode with.
+        private Binder mTorchClientBinder = new Binder();
+
+        // Camera ID -> Torch status map
+        private final ArrayMap<String, Integer> mTorchStatus = new ArrayMap<String, Integer>();
+
+        // Registered torch callbacks and their handlers
+        private final ArrayMap<TorchCallback, Handler> mTorchCallbackMap =
+                new ArrayMap<TorchCallback, Handler>();
+
         private final Object mLock = new Object();
 
         // Access only through getCameraService to deal with binder death
@@ -668,15 +839,46 @@
             }
         }
 
+        public void setTorchMode(String cameraId, boolean enabled) throws CameraAccessException {
+            synchronized(mLock) {
+
+                if (cameraId == null) {
+                    throw new IllegalArgumentException("cameraId was null");
+                }
+
+                ICameraService cameraService = getCameraService();
+                if (cameraService == null) {
+                    throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
+                        "Camera service is currently unavailable");
+                }
+
+                try {
+                    int status = cameraService.setTorchMode(cameraId, enabled, mTorchClientBinder);
+                } catch(CameraRuntimeException e) {
+                    int problem = e.getReason();
+                    switch (problem) {
+                        case CameraAccessException.CAMERA_ERROR:
+                            throw new IllegalArgumentException(
+                                    "the camera device doesn't have a flash unit.");
+                        default:
+                            throw e.asChecked();
+                    }
+                } catch (RemoteException e) {
+                    throw new CameraAccessException(CameraAccessException.CAMERA_DISCONNECTED,
+                            "Camera service is currently unavailable");
+                }
+            }
+        }
+
         private void handleRecoverableSetupErrors(CameraRuntimeException e, String msg) {
             int problem = e.getReason();
             switch (problem) {
-            case CameraAccessException.CAMERA_DISCONNECTED:
-                String errorMsg = CameraAccessException.getDefaultMessage(problem);
-                Log.w(TAG, msg + ": " + errorMsg);
-                break;
-            default:
-                throw new IllegalStateException(msg, e.asChecked());
+                case CameraAccessException.CAMERA_DISCONNECTED:
+                    String errorMsg = CameraAccessException.getDefaultMessage(problem);
+                    Log.w(TAG, msg + ": " + errorMsg);
+                    break;
+                default:
+                    throw new IllegalStateException(msg, e.asChecked());
             }
         }
 
@@ -701,6 +903,17 @@
             }
         }
 
+        private boolean validTorchStatus(int status) {
+            switch (status) {
+                case TORCH_STATUS_NOT_AVAILABLE:
+                case TORCH_STATUS_AVAILABLE_ON:
+                case TORCH_STATUS_AVAILABLE_OFF:
+                    return true;
+                default:
+                    return false;
+            }
+        }
+
         private void postSingleUpdate(final AvailabilityCallback callback, final Handler handler,
                 final String id, final int status) {
             if (isAvailable(status)) {
@@ -722,6 +935,32 @@
             }
         }
 
+        private void postSingleTorchUpdate(final TorchCallback callback, final Handler handler,
+                final String id, final int status) {
+            switch(status) {
+                case TORCH_STATUS_AVAILABLE_ON:
+                case TORCH_STATUS_AVAILABLE_OFF:
+                    handler.post(
+                            new Runnable() {
+                                @Override
+                                public void run() {
+                                    callback.onTorchModeChanged(id, status ==
+                                            TORCH_STATUS_AVAILABLE_ON);
+                                }
+                            });
+                    break;
+                default:
+                    handler.post(
+                            new Runnable() {
+                                @Override
+                                public void run() {
+                                    callback.onTorchModeUnavailable(id);
+                                }
+                            });
+                    break;
+            }
+        }
+
         /**
          * Send the state of all known cameras to the provided listener, to initialize
          * the listener's knowledge of camera state.
@@ -791,6 +1030,44 @@
             }
         } // onStatusChangedLocked
 
+        private void updateTorchCallbackLocked(TorchCallback callback, Handler handler) {
+            for (int i = 0; i < mTorchStatus.size(); i++) {
+                String id = mTorchStatus.keyAt(i);
+                Integer status = mTorchStatus.valueAt(i);
+                postSingleTorchUpdate(callback, handler, id, status);
+            }
+        }
+
+        private void onTorchStatusChangedLocked(int status, String id) {
+            if (DEBUG) {
+                Log.v(TAG,
+                        String.format("Camera id %s has torch status changed to 0x%x", id, status));
+            }
+
+            if (!validTorchStatus(status)) {
+                Log.e(TAG, String.format("Ignoring invalid device %s torch status 0x%x", id,
+                                status));
+                return;
+            }
+
+            Integer oldStatus = mTorchStatus.put(id, status);
+            if (oldStatus != null && oldStatus == status) {
+                if (DEBUG) {
+                    Log.v(TAG, String.format(
+                        "Torch status changed to 0x%x, which is what it already was",
+                        status));
+                }
+                return;
+            }
+
+            final int callbackCount = mTorchCallbackMap.size();
+            for (int i = 0; i < callbackCount; i++) {
+                final Handler handler = mTorchCallbackMap.valueAt(i);
+                final TorchCallback callback = mTorchCallbackMap.keyAt(i);
+                postSingleTorchUpdate(callback, handler, id, status);
+            }
+        } // onTorchStatusChangedLocked
+
         /**
          * Register a callback to be notified about camera device availability with the
          * global listener singleton.
@@ -820,6 +1097,22 @@
             }
         }
 
+        public void registerTorchCallback(TorchCallback callback, Handler handler) {
+            synchronized(mLock) {
+                Handler oldHandler = mTorchCallbackMap.put(callback, handler);
+                // For new callbacks, provide initial torch information
+                if (oldHandler == null) {
+                    updateTorchCallbackLocked(callback, handler);
+                }
+            }
+        }
+
+        public void unregisterTorchCallback(TorchCallback callback) {
+            synchronized(mLock) {
+                mTorchCallbackMap.remove(callback);
+            }
+        }
+
         /**
          * Callback from camera service notifying the process about camera availability changes
          */
@@ -830,6 +1123,13 @@
             }
         }
 
+        @Override
+        public void onTorchStatusChanged(int status, String cameraId) throws RemoteException {
+            synchronized (mLock) {
+                onTorchStatusChangedLocked(status, cameraId);
+            }
+        }
+
         /**
          * Listener for camera service death.
          *
@@ -844,9 +1144,9 @@
 
                 mCameraService = null;
 
-                // Tell listeners that the cameras are _available_, because any existing clients
-                // will have gotten disconnected. This is optimistic under the assumption that
-                // the service will be back shortly.
+                // Tell listeners that the cameras and torch modes are _available_, because any
+                // existing clients will have gotten disconnected. This is optimistic under the
+                // assumption that the service will be back shortly.
                 //
                 // Without this, a camera service crash while a camera is open will never signal
                 // to listeners that previously in-use cameras are now available.
@@ -854,6 +1154,11 @@
                     String cameraId = mDeviceStatus.keyAt(i);
                     onStatusChangedLocked(STATUS_PRESENT, cameraId);
                 }
+                for (int i = 0; i < mTorchStatus.size(); i++) {
+                    String cameraId = mTorchStatus.keyAt(i);
+                    onTorchStatusChangedLocked(TORCH_STATUS_AVAILABLE_OFF, cameraId);
+                }
+
             }
         }
 
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 1b10858..7f901c8 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -288,6 +288,13 @@
      */
     public static final int LENS_FACING_BACK = 1;
 
+    /**
+     * <p>The camera device is an external camera, and has no fixed facing relative to the
+     * device's screen.</p>
+     * @see CameraCharacteristics#LENS_FACING
+     */
+    public static final int LENS_FACING_EXTERNAL = 2;
+
     //
     // Enumeration values for CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
     //
@@ -367,13 +374,19 @@
      * The camera device supports basic manual control of the image post-processing
      * stages. This means the following controls are guaranteed to be supported:</p>
      * <ul>
-     * <li>Manual tonemap control<ul>
+     * <li>
+     * <p>Manual tonemap control</p>
+     * <ul>
      * <li>{@link CaptureRequest#TONEMAP_CURVE android.tonemap.curve}</li>
      * <li>{@link CaptureRequest#TONEMAP_MODE android.tonemap.mode}</li>
      * <li>{@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS android.tonemap.maxCurvePoints}</li>
+     * <li>android.tonemap.gamma</li>
+     * <li>android.tonemap.presetCurve</li>
      * </ul>
      * </li>
-     * <li>Manual white balance control<ul>
+     * <li>
+     * <p>Manual white balance control</p>
+     * <ul>
      * <li>{@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform}</li>
      * <li>{@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains}</li>
      * </ul>
@@ -432,23 +445,40 @@
     public static final int REQUEST_AVAILABLE_CAPABILITIES_RAW = 3;
 
     /**
-     * <p>The camera device supports the Zero Shutter Lag use case.</p>
+     * <p>The camera device supports the Zero Shutter Lag reprocessing use case.</p>
      * <ul>
-     * <li>At least one input stream can be used.</li>
-     * <li>RAW_OPAQUE is supported as an output/input format</li>
-     * <li>Using RAW_OPAQUE does not cause a frame rate drop
+     * <li>One input stream is supported, that is, <code>{@link CameraCharacteristics#REQUEST_MAX_NUM_INPUT_STREAMS android.request.maxNumInputStreams} == 1</code>.</li>
+     * <li>OPAQUE is supported as an output/input format, that is,
+     *   StreamConfigurationMap#getOutputSizes(klass) and
+     *   StreamConfigurationMap#getInputSizes(klass) return non empty Size[] and have common
+     *   sizes, where klass is android.media.OpaqueImageRingBufferQueue.class. See
+     *   android.scaler.availableInputOutputFormatsMap for detailed information about
+     *   OPAQUE format.</li>
+     * <li>android.scaler.availableInputOutputFormatsMap has the required map entries.</li>
+     * <li>Using OPAQUE does not cause a frame rate drop
      *   relative to the sensor's maximum capture rate (at that
-     *   resolution).</li>
-     * <li>RAW_OPAQUE will be reprocessable into both YUV_420_888
+     *   resolution), see android.scaler.availableInputOutputFormatsMap for more details.</li>
+     * <li>OPAQUE will be reprocessable into both YUV_420_888
      *   and JPEG formats.</li>
-     * <li>The maximum available resolution for RAW_OPAQUE streams
+     * <li>The maximum available resolution for OPAQUE streams
      *   (both input/output) will match the maximum available
      *   resolution of JPEG streams.</li>
+     * <li>Only below controls are effective for reprocessing requests and
+     *   will be present in capture results, other controls in reprocess
+     *   requests will be ignored by the camera device.<ul>
+     * <li>android.jpeg.*</li>
+     * <li>{@link CaptureRequest#NOISE_REDUCTION_MODE android.noiseReduction.mode}</li>
+     * <li>{@link CaptureRequest#EDGE_MODE android.edge.mode}</li>
      * </ul>
+     * </li>
+     * </ul>
+     *
+     * @see CaptureRequest#EDGE_MODE
+     * @see CaptureRequest#NOISE_REDUCTION_MODE
+     * @see CameraCharacteristics#REQUEST_MAX_NUM_INPUT_STREAMS
      * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
-     * @hide
      */
-    public static final int REQUEST_AVAILABLE_CAPABILITIES_ZSL = 4;
+    public static final int REQUEST_AVAILABLE_CAPABILITIES_OPAQUE_REPROCESSING = 4;
 
     /**
      * <p>The camera device supports accurately reporting the sensor settings for many of
@@ -508,6 +538,45 @@
      */
     public static final int REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE = 6;
 
+    /**
+     * <p>The camera device supports the YUV420_888 reprocessing use case, similar as
+     * OPAQUE_REPROCESSING, This capability requires the camera device to support the
+     * following:</p>
+     * <ul>
+     * <li>One input stream is supported, that is, <code>{@link CameraCharacteristics#REQUEST_MAX_NUM_INPUT_STREAMS android.request.maxNumInputStreams} == 1</code>.</li>
+     * <li>YUV420_888 is supported as a common format for both input and output, that is,
+     *   StreamConfigurationMap#getOutputSizes(YUV420_888) and
+     *   StreamConfigurationMap#getInputSizes(YUV420_888) return non empty Size[] and have
+     *   common sizes.</li>
+     * <li>android.scaler.availableInputOutputFormatsMap has the required map entries.</li>
+     * <li>Using YUV420_888 does not cause a frame rate drop
+     *   relative to the sensor's maximum capture rate (at that
+     *   resolution), see android.scaler.availableInputOutputFormatsMap for more details.</li>
+     * <li>YUV420_888 will be reprocessable into both YUV_420_888
+     *   and JPEG formats.</li>
+     * <li>The maximum available resolution for YUV420_888 streams
+     *   (both input/output) will match the maximum available
+     *   resolution of JPEG streams.</li>
+     * <li>Only the below controls are effective for reprocessing requests and will be
+     *   present in capture results. The reprocess requests are from the original capture
+     *   results that are assocaited with the intermidate YUV420_888 output buffers.
+     *   All other controls in the reprocess requests will be ignored by the camera device.<ul>
+     * <li>android.jpeg.*</li>
+     * <li>{@link CaptureRequest#NOISE_REDUCTION_MODE android.noiseReduction.mode}</li>
+     * <li>{@link CaptureRequest#EDGE_MODE android.edge.mode}</li>
+     * <li>{@link CaptureRequest#REPROCESS_EFFECTIVE_EXPOSURE_FACTOR android.reprocess.effectiveExposureFactor}</li>
+     * </ul>
+     * </li>
+     * </ul>
+     *
+     * @see CaptureRequest#EDGE_MODE
+     * @see CaptureRequest#NOISE_REDUCTION_MODE
+     * @see CaptureRequest#REPROCESS_EFFECTIVE_EXPOSURE_FACTOR
+     * @see CameraCharacteristics#REQUEST_MAX_NUM_INPUT_STREAMS
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+     */
+    public static final int REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING = 7;
+
     //
     // Enumeration values for CameraCharacteristics#SCALER_CROPPING_TYPE
     //
@@ -966,6 +1035,14 @@
      */
     public static final int CONTROL_AE_PRECAPTURE_TRIGGER_START = 1;
 
+    /**
+     * <p>The camera device will cancel any currently active or completed
+     * precapture metering sequence, the auto-exposure routine will return to its
+     * initial state.</p>
+     * @see CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER
+     */
+    public static final int CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL = 2;
+
     //
     // Enumeration values for CaptureRequest#CONTROL_AF_MODE
     //
@@ -1823,6 +1900,13 @@
      */
     public static final int NOISE_REDUCTION_MODE_HIGH_QUALITY = 2;
 
+    /**
+     * <p>MINIMAL noise reduction is applied without reducing frame rate relative to
+     * sensor output. </p>
+     * @see CaptureRequest#NOISE_REDUCTION_MODE
+     */
+    public static final int NOISE_REDUCTION_MODE_MINIMAL = 3;
+
     //
     // Enumeration values for CaptureRequest#SENSOR_TEST_PATTERN_MODE
     //
@@ -2026,6 +2110,43 @@
      */
     public static final int TONEMAP_MODE_HIGH_QUALITY = 2;
 
+    /**
+     * <p>Use the gamma value specified in android.tonemap.gamma to peform
+     * tonemapping.</p>
+     * <p>All color enhancement and tonemapping must be disabled, except
+     * for applying the tonemapping curve specified by android.tonemap.gamma.</p>
+     * <p>Must not slow down frame rate relative to raw sensor output.</p>
+     * @see CaptureRequest#TONEMAP_MODE
+     */
+    public static final int TONEMAP_MODE_GAMMA_VALUE = 3;
+
+    /**
+     * <p>Use the preset tonemapping curve specified in
+     * android.tonemap.presetCurve to peform tonemapping.</p>
+     * <p>All color enhancement and tonemapping must be disabled, except
+     * for applying the tonemapping curve specified by
+     * android.tonemap.presetCurve.</p>
+     * <p>Must not slow down frame rate relative to raw sensor output.</p>
+     * @see CaptureRequest#TONEMAP_MODE
+     */
+    public static final int TONEMAP_MODE_PRESET_CURVE = 4;
+
+    //
+    // Enumeration values for CaptureRequest#TONEMAP_PRESET_CURVE
+    //
+
+    /**
+     * <p>Tonemapping curve is defined by sRGB</p>
+     * @see CaptureRequest#TONEMAP_PRESET_CURVE
+     */
+    public static final int TONEMAP_PRESET_CURVE_SRGB = 0;
+
+    /**
+     * <p>Tonemapping curve is defined by ITU-R BT.709</p>
+     * @see CaptureRequest#TONEMAP_PRESET_CURVE
+     */
+    public static final int TONEMAP_PRESET_CURVE_REC709 = 1;
+
     //
     // Enumeration values for CaptureResult#CONTROL_AE_STATE
     //
@@ -2073,7 +2194,10 @@
      * <p>AE has been asked to do a precapture sequence
      * and is currently executing it.</p>
      * <p>Precapture can be triggered through setting
-     * {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger} to START.</p>
+     * {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger} to START. Currently
+     * active and completed (if it causes camera device internal AE lock) precapture
+     * metering sequence can be canceled through setting
+     * {@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger} to CANCEL.</p>
      * <p>Once PRECAPTURE completes, AE will transition to CONVERGED
      * or FLASH_REQUIRED as appropriate. This is a transient
      * state, the camera device may skip reporting this state in
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index b417496..7569ea5 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -552,6 +552,8 @@
      * in this matrix result metadata. The transform should keep the magnitude
      * of the output color values within <code>[0, 1.0]</code> (assuming input color
      * values is within the normalized range <code>[0, 1.0]</code>), or clipping may occur.</p>
+     * <p>The valid range of each matrix element varies on different devices, but
+     * values within [-1.5, 3.0] are guaranteed not to be clipped.</p>
      * <p><b>Units</b>: Unitless scale factors</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * <p><b>Full capability</b> -
@@ -575,6 +577,10 @@
      * TRANSFORM_MATRIX.</p>
      * <p>The gains in the result metadata are the gains actually
      * applied by the camera device to the current frame.</p>
+     * <p>The valid range of gains varies on different devices, but gains
+     * between [1.0, 3.0] are guaranteed not to be clipped. Even if a given
+     * device allows gains below 1.0, this is usually not recommended because
+     * this can create color artifacts.</p>
      * <p><b>Units</b>: Unitless gain factors</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * <p><b>Full capability</b> -
@@ -724,7 +730,14 @@
      * ({@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}) and sensitivity ({@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity})
      * parameters. The flash may be fired if the {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode}
      * is ON_AUTO_FLASH/ON_AUTO_FLASH_REDEYE and the scene is too dark. If the
-     * {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is ON_ALWAYS_FLASH, the scene may become overexposed.</p>
+     * {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is ON_ALWAYS_FLASH, the scene may become overexposed.
+     * Similarly, AE precapture trigger CANCEL has no effect when AE is already locked.</p>
+     * <p>When an AE precapture sequence is triggered, AE unlock will not be able to unlock
+     * the AE if AE is locked by the camera device internally during precapture metering
+     * sequence In other words, submitting requests with AE unlock has no effect for an
+     * ongoing precapture metering sequence. Otherwise, the precapture metering sequence
+     * will never succeed in a sequence of preview requests where AE lock is always set
+     * to <code>false</code>.</p>
      * <p>Since the camera device has a pipeline of in-flight requests, the settings that
      * get locked do not necessarily correspond to the settings that were present in the
      * latest capture result received from the camera device, since additional captures
@@ -869,6 +882,11 @@
      * included at all in the request settings. When included and
      * set to START, the camera device will trigger the auto-exposure (AE)
      * precapture metering sequence.</p>
+     * <p>When set to CANCEL, the camera device will cancel any active
+     * precapture metering trigger, and return to its initial AE state.
+     * If a precapture metering sequence is already completed, and the camera
+     * device has implicitly locked the AE for subsequent still capture, the
+     * CANCEL trigger will unlock the AE and return to its initial AE state.</p>
      * <p>The precapture sequence should be triggered before starting a
      * high-quality still capture for final metering decisions to
      * be made, and for firing pre-capture flash pulses to estimate
@@ -884,7 +902,11 @@
      * submitted. To ensure that the AE routine restarts normal scan, the application should
      * submit a request with <code>{@link CaptureRequest#CONTROL_AE_LOCK android.control.aeLock} == true</code>, followed by a request
      * with <code>{@link CaptureRequest#CONTROL_AE_LOCK android.control.aeLock} == false</code>, if the application decides not to submit a
-     * still capture request after the precapture sequence completes.</p>
+     * still capture request after the precapture sequence completes. Alternatively, for
+     * API level 23 or newer devices, the CANCEL can be used to unlock the camera device
+     * internally locked AE if the application doesn't submit a still capture request after
+     * the AE precapture trigger. Note that, the CANCEL was added in API level 23, and must not
+     * be used in devices that have earlier API levels.</p>
      * <p>The exact effect of auto-exposure (AE) precapture trigger
      * depends on the current AE mode and state; see
      * {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} for AE precapture state transition
@@ -897,6 +919,7 @@
      * <ul>
      *   <li>{@link #CONTROL_AE_PRECAPTURE_TRIGGER_IDLE IDLE}</li>
      *   <li>{@link #CONTROL_AE_PRECAPTURE_TRIGGER_START START}</li>
+     *   <li>{@link #CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL CANCEL}</li>
      * </ul></p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * <p><b>Limited capability</b> -
@@ -909,6 +932,7 @@
      * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
      * @see #CONTROL_AE_PRECAPTURE_TRIGGER_IDLE
      * @see #CONTROL_AE_PRECAPTURE_TRIGGER_START
+     * @see #CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL
      */
     @PublicKey
     public static final Key<Integer> CONTROL_AE_PRECAPTURE_TRIGGER =
@@ -1164,7 +1188,7 @@
      * <p>This control (except for MANUAL) is only effective if
      * <code>{@link CaptureRequest#CONTROL_MODE android.control.mode} != OFF</code> and any 3A routine is active.</p>
      * <p>ZERO_SHUTTER_LAG will be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}
-     * contains ZSL. MANUAL will be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}
+     * contains OPAQUE_REPROCESSING. MANUAL will be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}
      * contains MANUAL_SENSOR. Other intent values are always supported.</p>
      * <p><b>Possible values:</b>
      * <ul>
@@ -1249,10 +1273,6 @@
      * update, as if this frame is never captured. This mode can be used in the scenario
      * where the application doesn't want a 3A manual control capture to affect
      * the subsequent auto 3A capture results.</p>
-     * <p>LEGACY mode devices will only support AUTO and USE_SCENE_MODE modes.
-     * LIMITED mode devices will only support OFF and OFF_KEEP_STATE if they
-     * support the MANUAL_SENSOR and MANUAL_POST_PROCSESING capabilities.
-     * FULL mode devices will always support OFF and OFF_KEEP_STATE.</p>
      * <p><b>Possible values:</b>
      * <ul>
      *   <li>{@link #CONTROL_MODE_OFF OFF}</li>
@@ -1260,9 +1280,12 @@
      *   <li>{@link #CONTROL_MODE_USE_SCENE_MODE USE_SCENE_MODE}</li>
      *   <li>{@link #CONTROL_MODE_OFF_KEEP_STATE OFF_KEEP_STATE}</li>
      * </ul></p>
+     * <p><b>Available values for this device:</b><br>
+     * {@link CameraCharacteristics#CONTROL_AVAILABLE_MODES android.control.availableModes}</p>
      * <p>This key is available on all devices.</p>
      *
      * @see CaptureRequest#CONTROL_AF_MODE
+     * @see CameraCharacteristics#CONTROL_AVAILABLE_MODES
      * @see #CONTROL_MODE_OFF
      * @see #CONTROL_MODE_AUTO
      * @see #CONTROL_MODE_USE_SCENE_MODE
@@ -1382,6 +1405,10 @@
      * camera device will use the highest-quality enhancement algorithms,
      * even if it slows down capture rate. FAST means the camera device will
      * not slow down capture rate when applying edge enhancement.</p>
+     * <p>For YUV_REPROCESSING, these FAST/HIGH_QUALITY modes both mean that the camera
+     * device will apply FAST/HIGH_QUALITY YUV-domain edge enhancement, respectively.
+     * The camera device may adjust its internal noise reduction parameters for best
+     * image quality based on the {@link CaptureRequest#REPROCESS_EFFECTIVE_EXPOSURE_FACTOR android.reprocess.effectiveExposureFactor}, if it is set.</p>
      * <p><b>Possible values:</b>
      * <ul>
      *   <li>{@link #EDGE_MODE_OFF OFF}</li>
@@ -1397,6 +1424,7 @@
      *
      * @see CameraCharacteristics#EDGE_AVAILABLE_EDGE_MODES
      * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+     * @see CaptureRequest#REPROCESS_EFFECTIVE_EXPOSURE_FACTOR
      * @see #EDGE_MODE_OFF
      * @see #EDGE_MODE_FAST
      * @see #EDGE_MODE_HIGH_QUALITY
@@ -1761,18 +1789,28 @@
     /**
      * <p>Mode of operation for the noise reduction algorithm.</p>
      * <p>The noise reduction algorithm attempts to improve image quality by removing
-     * excessive noise added by the capture process, especially in dark conditions.
-     * OFF means no noise reduction will be applied by the camera device.</p>
+     * excessive noise added by the capture process, especially in dark conditions.</p>
+     * <p>OFF means no noise reduction will be applied by the camera device, for both raw and
+     * YUV domain.</p>
+     * <p>MINIMAL means that only sensor raw domain basic noise reduction is enabled ,to remove
+     * demosaicing or other processing artifacts. For YUV_REPROCESSING, MINIMAL is same as OFF.
+     * This mode is optional, may not be support by all devices. The application should check
+     * {@link CameraCharacteristics#NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES android.noiseReduction.availableNoiseReductionModes} before using it.</p>
      * <p>FAST/HIGH_QUALITY both mean camera device determined noise filtering
      * will be applied. HIGH_QUALITY mode indicates that the camera device
      * will use the highest-quality noise filtering algorithms,
      * even if it slows down capture rate. FAST means the camera device will not
      * slow down capture rate when applying noise filtering.</p>
+     * <p>For YUV_REPROCESSING, these FAST/HIGH_QUALITY modes both mean that the camera device
+     * will apply FAST/HIGH_QUALITY YUV domain noise reduction, respectively. The camera device
+     * may adjust the noise reduction parameters for best image quality based on the
+     * {@link CaptureRequest#REPROCESS_EFFECTIVE_EXPOSURE_FACTOR android.reprocess.effectiveExposureFactor} if it is set.</p>
      * <p><b>Possible values:</b>
      * <ul>
      *   <li>{@link #NOISE_REDUCTION_MODE_OFF OFF}</li>
      *   <li>{@link #NOISE_REDUCTION_MODE_FAST FAST}</li>
      *   <li>{@link #NOISE_REDUCTION_MODE_HIGH_QUALITY HIGH_QUALITY}</li>
+     *   <li>{@link #NOISE_REDUCTION_MODE_MINIMAL MINIMAL}</li>
      * </ul></p>
      * <p><b>Available values for this device:</b><br>
      * {@link CameraCharacteristics#NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES android.noiseReduction.availableNoiseReductionModes}</p>
@@ -1783,9 +1821,11 @@
      *
      * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
      * @see CameraCharacteristics#NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES
+     * @see CaptureRequest#REPROCESS_EFFECTIVE_EXPOSURE_FACTOR
      * @see #NOISE_REDUCTION_MODE_OFF
      * @see #NOISE_REDUCTION_MODE_FAST
      * @see #NOISE_REDUCTION_MODE_HIGH_QUALITY
+     * @see #NOISE_REDUCTION_MODE_MINIMAL
      */
     @PublicKey
     public static final Key<Integer> NOISE_REDUCTION_MODE =
@@ -2078,6 +2118,8 @@
      *   <li>{@link #SHADING_MODE_FAST FAST}</li>
      *   <li>{@link #SHADING_MODE_HIGH_QUALITY HIGH_QUALITY}</li>
      * </ul></p>
+     * <p><b>Available values for this device:</b><br>
+     * {@link CameraCharacteristics#SHADING_AVAILABLE_MODES android.shading.availableModes}</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * <p><b>Full capability</b> -
      * Present on all camera devices that report being {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_FULL HARDWARE_LEVEL_FULL} devices in the
@@ -2086,6 +2128,7 @@
      * @see CaptureRequest#CONTROL_AE_MODE
      * @see CaptureRequest#CONTROL_AWB_MODE
      * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+     * @see CameraCharacteristics#SHADING_AVAILABLE_MODES
      * @see CaptureResult#STATISTICS_LENS_SHADING_CORRECTION_MAP
      * @see CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE
      * @see #SHADING_MODE_OFF
@@ -2148,12 +2191,15 @@
      *   <li>{@link #STATISTICS_LENS_SHADING_MAP_MODE_OFF OFF}</li>
      *   <li>{@link #STATISTICS_LENS_SHADING_MAP_MODE_ON ON}</li>
      * </ul></p>
+     * <p><b>Available values for this device:</b><br>
+     * {@link CameraCharacteristics#STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES android.statistics.info.availableLensShadingMapModes}</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * <p><b>Full capability</b> -
      * Present on all camera devices that report being {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_FULL HARDWARE_LEVEL_FULL} devices in the
      * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
      *
      * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+     * @see CameraCharacteristics#STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES
      * @see #STATISTICS_LENS_SHADING_MAP_MODE_OFF
      * @see #STATISTICS_LENS_SHADING_MAP_MODE_ON
      */
@@ -2340,6 +2386,8 @@
      *   <li>{@link #TONEMAP_MODE_CONTRAST_CURVE CONTRAST_CURVE}</li>
      *   <li>{@link #TONEMAP_MODE_FAST FAST}</li>
      *   <li>{@link #TONEMAP_MODE_HIGH_QUALITY HIGH_QUALITY}</li>
+     *   <li>{@link #TONEMAP_MODE_GAMMA_VALUE GAMMA_VALUE}</li>
+     *   <li>{@link #TONEMAP_MODE_PRESET_CURVE PRESET_CURVE}</li>
      * </ul></p>
      * <p><b>Available values for this device:</b><br>
      * {@link CameraCharacteristics#TONEMAP_AVAILABLE_TONE_MAP_MODES android.tonemap.availableToneMapModes}</p>
@@ -2355,12 +2403,60 @@
      * @see #TONEMAP_MODE_CONTRAST_CURVE
      * @see #TONEMAP_MODE_FAST
      * @see #TONEMAP_MODE_HIGH_QUALITY
+     * @see #TONEMAP_MODE_GAMMA_VALUE
+     * @see #TONEMAP_MODE_PRESET_CURVE
      */
     @PublicKey
     public static final Key<Integer> TONEMAP_MODE =
             new Key<Integer>("android.tonemap.mode", int.class);
 
     /**
+     * <p>Tonemapping curve to use when {@link CaptureRequest#TONEMAP_MODE android.tonemap.mode} is
+     * GAMMA_VALUE</p>
+     * <p>The tonemap curve will be defined the following formula:
+     * * OUT = pow(IN, 1.0 / gamma)
+     * where IN and OUT is the input pixel value scaled to range [0.0, 1.0],
+     * pow is the power function and gamma is the gamma value specified by this
+     * key.</p>
+     * <p>The same curve will be applied to all color channels. The camera device
+     * may clip the input gamma value to its supported range. The actual applied
+     * value will be returned in capture result.</p>
+     * <p>The valid range of gamma value varies on different devices, but values
+     * within [1.0, 5.0] are guaranteed not to be clipped.</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#TONEMAP_MODE
+     */
+    @PublicKey
+    public static final Key<Float> TONEMAP_GAMMA =
+            new Key<Float>("android.tonemap.gamma", float.class);
+
+    /**
+     * <p>Tonemapping curve to use when {@link CaptureRequest#TONEMAP_MODE android.tonemap.mode} is
+     * PRESET_CURVE</p>
+     * <p>The tonemap curve will be defined by specified standard.</p>
+     * <p>sRGB (approximated by 16 control points):</p>
+     * <p><img alt="sRGB tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png" /></p>
+     * <p>Rec. 709 (approximated by 16 control points):</p>
+     * <p><img alt="Rec. 709 tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/rec709_tonemap.png" /></p>
+     * <p>Note that above figures show a 16 control points approximation of preset
+     * curves. Camera devices may apply a different approximation to the curve.</p>
+     * <p><b>Possible values:</b>
+     * <ul>
+     *   <li>{@link #TONEMAP_PRESET_CURVE_SRGB SRGB}</li>
+     *   <li>{@link #TONEMAP_PRESET_CURVE_REC709 REC709}</li>
+     * </ul></p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#TONEMAP_MODE
+     * @see #TONEMAP_PRESET_CURVE_SRGB
+     * @see #TONEMAP_PRESET_CURVE_REC709
+     */
+    @PublicKey
+    public static final Key<Integer> TONEMAP_PRESET_CURVE =
+            new Key<Integer>("android.tonemap.presetCurve", int.class);
+
+    /**
      * <p>This LED is nominally used to indicate to the user
      * that the camera is powered on and may be streaming images back to the
      * Application Processor. In certain rare circumstances, the OS may
@@ -2426,6 +2522,52 @@
     public static final Key<Boolean> BLACK_LEVEL_LOCK =
             new Key<Boolean>("android.blackLevel.lock", boolean.class);
 
+    /**
+     * <p>The amount of exposure time increase factor applied to the original output
+     * frame by the application processing before sending for reprocessing.</p>
+     * <p>This is optional, and will be supported if the camera device supports YUV_REPROCESSING
+     * capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains YUV_REPROCESSING).</p>
+     * <p>For some YUV reprocessing use cases, the application may choose to filter the original
+     * output frames to effectively reduce the noise to the same level as a frame that was
+     * captured with longer exposure time. To be more specific, assuming the original captured
+     * images were captured with a sensitivity of S and an exposure time of T, the model in
+     * the camera device is that the amount of noise in the image would be approximately what
+     * would be expected if the original capture parameters had been a sensitivity of
+     * S/effectiveExposureFactor and an exposure time of T*effectiveExposureFactor, rather
+     * than S and T respectively. If the captured images were processed by the application
+     * before being sent for reprocessing, then the application may have used image processing
+     * algorithms and/or multi-frame image fusion to reduce the noise in the
+     * application-processed images (input images). By using the effectiveExposureFactor
+     * control, the application can communicate to the camera device the actual noise level
+     * improvement in the application-processed image. With this information, the camera
+     * device can select appropriate noise reduction and edge enhancement parameters to avoid
+     * excessive noise reduction ({@link CaptureRequest#NOISE_REDUCTION_MODE android.noiseReduction.mode}) and insufficient edge
+     * enhancement ({@link CaptureRequest#EDGE_MODE android.edge.mode}) being applied to the reprocessed frames.</p>
+     * <p>For example, for multi-frame image fusion use case, the application may fuse
+     * multiple output frames together to a final frame for reprocessing. When N image are
+     * fused into 1 image for reprocessing, the exposure time increase factor could be up to
+     * square root of N (based on a simple photon shot noise model). The camera device will
+     * adjust the reprocessing noise reduction and edge enhancement parameters accordingly to
+     * produce the best quality images.</p>
+     * <p>This is relative factor, 1.0 indicates the application hasn't processed the input
+     * buffer in a way that affects its effective exposure time.</p>
+     * <p>This control is only effective for YUV reprocessing capture request. For noise
+     * reduction reprocessing, it is only effective when <code>{@link CaptureRequest#NOISE_REDUCTION_MODE android.noiseReduction.mode} != OFF</code>.
+     * Similarly, for edge enhancement reprocessing, it is only effective when
+     * <code>{@link CaptureRequest#EDGE_MODE android.edge.mode} != OFF</code>.</p>
+     * <p><b>Units</b>: Relative exposure time increase factor.</p>
+     * <p><b>Range of valid values:</b><br>
+     * &gt;= 1.0</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#EDGE_MODE
+     * @see CaptureRequest#NOISE_REDUCTION_MODE
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+     */
+    @PublicKey
+    public static final Key<Float> REPROCESS_EFFECTIVE_EXPOSURE_FACTOR =
+            new Key<Float>("android.reprocess.effectiveExposureFactor", float.class);
+
     /*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
      * End generated code
      *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index f17100d..9d48a4c 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -403,6 +403,8 @@
      * in this matrix result metadata. The transform should keep the magnitude
      * of the output color values within <code>[0, 1.0]</code> (assuming input color
      * values is within the normalized range <code>[0, 1.0]</code>), or clipping may occur.</p>
+     * <p>The valid range of each matrix element varies on different devices, but
+     * values within [-1.5, 3.0] are guaranteed not to be clipped.</p>
      * <p><b>Units</b>: Unitless scale factors</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * <p><b>Full capability</b> -
@@ -426,6 +428,10 @@
      * TRANSFORM_MATRIX.</p>
      * <p>The gains in the result metadata are the gains actually
      * applied by the camera device to the current frame.</p>
+     * <p>The valid range of gains varies on different devices, but gains
+     * between [1.0, 3.0] are guaranteed not to be clipped. Even if a given
+     * device allows gains below 1.0, this is usually not recommended because
+     * this can create color artifacts.</p>
      * <p><b>Units</b>: Unitless gain factors</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * <p><b>Full capability</b> -
@@ -575,7 +581,14 @@
      * ({@link CaptureRequest#SENSOR_EXPOSURE_TIME android.sensor.exposureTime}) and sensitivity ({@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity})
      * parameters. The flash may be fired if the {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode}
      * is ON_AUTO_FLASH/ON_AUTO_FLASH_REDEYE and the scene is too dark. If the
-     * {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is ON_ALWAYS_FLASH, the scene may become overexposed.</p>
+     * {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is ON_ALWAYS_FLASH, the scene may become overexposed.
+     * Similarly, AE precapture trigger CANCEL has no effect when AE is already locked.</p>
+     * <p>When an AE precapture sequence is triggered, AE unlock will not be able to unlock
+     * the AE if AE is locked by the camera device internally during precapture metering
+     * sequence In other words, submitting requests with AE unlock has no effect for an
+     * ongoing precapture metering sequence. Otherwise, the precapture metering sequence
+     * will never succeed in a sequence of preview requests where AE lock is always set
+     * to <code>false</code>.</p>
      * <p>Since the camera device has a pipeline of in-flight requests, the settings that
      * get locked do not necessarily correspond to the settings that were present in the
      * latest capture result received from the camera device, since additional captures
@@ -720,6 +733,11 @@
      * included at all in the request settings. When included and
      * set to START, the camera device will trigger the auto-exposure (AE)
      * precapture metering sequence.</p>
+     * <p>When set to CANCEL, the camera device will cancel any active
+     * precapture metering trigger, and return to its initial AE state.
+     * If a precapture metering sequence is already completed, and the camera
+     * device has implicitly locked the AE for subsequent still capture, the
+     * CANCEL trigger will unlock the AE and return to its initial AE state.</p>
      * <p>The precapture sequence should be triggered before starting a
      * high-quality still capture for final metering decisions to
      * be made, and for firing pre-capture flash pulses to estimate
@@ -735,7 +753,11 @@
      * submitted. To ensure that the AE routine restarts normal scan, the application should
      * submit a request with <code>{@link CaptureRequest#CONTROL_AE_LOCK android.control.aeLock} == true</code>, followed by a request
      * with <code>{@link CaptureRequest#CONTROL_AE_LOCK android.control.aeLock} == false</code>, if the application decides not to submit a
-     * still capture request after the precapture sequence completes.</p>
+     * still capture request after the precapture sequence completes. Alternatively, for
+     * API level 23 or newer devices, the CANCEL can be used to unlock the camera device
+     * internally locked AE if the application doesn't submit a still capture request after
+     * the AE precapture trigger. Note that, the CANCEL was added in API level 23, and must not
+     * be used in devices that have earlier API levels.</p>
      * <p>The exact effect of auto-exposure (AE) precapture trigger
      * depends on the current AE mode and state; see
      * {@link CaptureResult#CONTROL_AE_STATE android.control.aeState} for AE precapture state transition
@@ -748,6 +770,7 @@
      * <ul>
      *   <li>{@link #CONTROL_AE_PRECAPTURE_TRIGGER_IDLE IDLE}</li>
      *   <li>{@link #CONTROL_AE_PRECAPTURE_TRIGGER_START START}</li>
+     *   <li>{@link #CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL CANCEL}</li>
      * </ul></p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * <p><b>Limited capability</b> -
@@ -760,6 +783,7 @@
      * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
      * @see #CONTROL_AE_PRECAPTURE_TRIGGER_IDLE
      * @see #CONTROL_AE_PRECAPTURE_TRIGGER_START
+     * @see #CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL
      */
     @PublicKey
     public static final Key<Integer> CONTROL_AE_PRECAPTURE_TRIGGER =
@@ -892,11 +916,29 @@
      * <td align="center">Ready for high-quality capture</td>
      * </tr>
      * <tr>
-     * <td align="center">Any state</td>
+     * <td align="center">LOCKED</td>
+     * <td align="center">aeLock is ON and aePrecaptureTrigger is START</td>
+     * <td align="center">LOCKED</td>
+     * <td align="center">Precapture trigger is ignored when AE is already locked</td>
+     * </tr>
+     * <tr>
+     * <td align="center">LOCKED</td>
+     * <td align="center">aeLock is ON and aePrecaptureTrigger is CANCEL</td>
+     * <td align="center">LOCKED</td>
+     * <td align="center">Precapture trigger is ignored when AE is already locked</td>
+     * </tr>
+     * <tr>
+     * <td align="center">Any state (excluding LOCKED)</td>
      * <td align="center">{@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger} is START</td>
      * <td align="center">PRECAPTURE</td>
      * <td align="center">Start AE precapture metering sequence</td>
      * </tr>
+     * <tr>
+     * <td align="center">Any state (excluding LOCKED)</td>
+     * <td align="center">{@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger} is CANCEL</td>
+     * <td align="center">INACTIVE</td>
+     * <td align="center">Currently active precapture metering sequence is canceled</td>
+     * </tr>
      * </tbody>
      * </table>
      * <p>For the above table, the camera device may skip reporting any state changes that happen
@@ -922,18 +964,30 @@
      * <td align="center">Values are already good, transient states are skipped by camera device.</td>
      * </tr>
      * <tr>
-     * <td align="center">Any state</td>
+     * <td align="center">Any state (excluding LOCKED)</td>
      * <td align="center">{@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger} is START, sequence done</td>
      * <td align="center">FLASH_REQUIRED</td>
      * <td align="center">Converged but too dark w/o flash after a precapture sequence, transient states are skipped by camera device.</td>
      * </tr>
      * <tr>
-     * <td align="center">Any state</td>
+     * <td align="center">Any state (excluding LOCKED)</td>
      * <td align="center">{@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger} is START, sequence done</td>
      * <td align="center">CONVERGED</td>
      * <td align="center">Converged after a precapture sequence, transient states are skipped by camera device.</td>
      * </tr>
      * <tr>
+     * <td align="center">Any state (excluding LOCKED)</td>
+     * <td align="center">{@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger} is CANCEL, converged</td>
+     * <td align="center">FLASH_REQUIRED</td>
+     * <td align="center">Converged but too dark w/o flash after a precapture sequence is canceled, transient states are skipped by camera device.</td>
+     * </tr>
+     * <tr>
+     * <td align="center">Any state (excluding LOCKED)</td>
+     * <td align="center">{@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger} is CANCEL, converged</td>
+     * <td align="center">CONVERGED</td>
+     * <td align="center">Converged after a precapture sequenceis canceled, transient states are skipped by camera device.</td>
+     * </tr>
+     * <tr>
      * <td align="center">CONVERGED</td>
      * <td align="center">Camera device finished AE scan</td>
      * <td align="center">FLASH_REQUIRED</td>
@@ -1637,7 +1691,7 @@
      * <p>This control (except for MANUAL) is only effective if
      * <code>{@link CaptureRequest#CONTROL_MODE android.control.mode} != OFF</code> and any 3A routine is active.</p>
      * <p>ZERO_SHUTTER_LAG will be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}
-     * contains ZSL. MANUAL will be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}
+     * contains OPAQUE_REPROCESSING. MANUAL will be supported if {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities}
      * contains MANUAL_SENSOR. Other intent values are always supported.</p>
      * <p><b>Possible values:</b>
      * <ul>
@@ -1865,10 +1919,6 @@
      * update, as if this frame is never captured. This mode can be used in the scenario
      * where the application doesn't want a 3A manual control capture to affect
      * the subsequent auto 3A capture results.</p>
-     * <p>LEGACY mode devices will only support AUTO and USE_SCENE_MODE modes.
-     * LIMITED mode devices will only support OFF and OFF_KEEP_STATE if they
-     * support the MANUAL_SENSOR and MANUAL_POST_PROCSESING capabilities.
-     * FULL mode devices will always support OFF and OFF_KEEP_STATE.</p>
      * <p><b>Possible values:</b>
      * <ul>
      *   <li>{@link #CONTROL_MODE_OFF OFF}</li>
@@ -1876,9 +1926,12 @@
      *   <li>{@link #CONTROL_MODE_USE_SCENE_MODE USE_SCENE_MODE}</li>
      *   <li>{@link #CONTROL_MODE_OFF_KEEP_STATE OFF_KEEP_STATE}</li>
      * </ul></p>
+     * <p><b>Available values for this device:</b><br>
+     * {@link CameraCharacteristics#CONTROL_AVAILABLE_MODES android.control.availableModes}</p>
      * <p>This key is available on all devices.</p>
      *
      * @see CaptureRequest#CONTROL_AF_MODE
+     * @see CameraCharacteristics#CONTROL_AVAILABLE_MODES
      * @see #CONTROL_MODE_OFF
      * @see #CONTROL_MODE_AUTO
      * @see #CONTROL_MODE_USE_SCENE_MODE
@@ -1998,6 +2051,10 @@
      * camera device will use the highest-quality enhancement algorithms,
      * even if it slows down capture rate. FAST means the camera device will
      * not slow down capture rate when applying edge enhancement.</p>
+     * <p>For YUV_REPROCESSING, these FAST/HIGH_QUALITY modes both mean that the camera
+     * device will apply FAST/HIGH_QUALITY YUV-domain edge enhancement, respectively.
+     * The camera device may adjust its internal noise reduction parameters for best
+     * image quality based on the {@link CaptureRequest#REPROCESS_EFFECTIVE_EXPOSURE_FACTOR android.reprocess.effectiveExposureFactor}, if it is set.</p>
      * <p><b>Possible values:</b>
      * <ul>
      *   <li>{@link #EDGE_MODE_OFF OFF}</li>
@@ -2013,6 +2070,7 @@
      *
      * @see CameraCharacteristics#EDGE_AVAILABLE_EDGE_MODES
      * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+     * @see CaptureRequest#REPROCESS_EFFECTIVE_EXPOSURE_FACTOR
      * @see #EDGE_MODE_OFF
      * @see #EDGE_MODE_FAST
      * @see #EDGE_MODE_HIGH_QUALITY
@@ -2473,20 +2531,153 @@
             new Key<Integer>("android.lens.state", int.class);
 
     /**
+     * <p>The orientation of the camera relative to the sensor
+     * coordinate system.</p>
+     * <p>The four coefficients that describe the quarternion
+     * rotation from the Android sensor coordinate system to a
+     * camera-aligned coordinate system where the X-axis is
+     * aligned with the long side of the image sensor, the Y-axis
+     * is aligned with the short side of the image sensor, and
+     * the Z-axis is aligned with the optical axis of the sensor.</p>
+     * <p>To convert from the quarternion coefficients <code>(x,y,z,w)</code>
+     * to the axis of rotation <code>(a_x, a_y, a_z)</code> and rotation
+     * amount <code>theta</code>, the following formulas can be used:</p>
+     * <pre><code> theta = 2 * acos(w)
+     * a_x = x / sin(theta/2)
+     * a_y = y / sin(theta/2)
+     * a_z = z / sin(theta/2)
+     * </code></pre>
+     * <p>To create a 3x3 rotation matrix that applies the rotation
+     * defined by this quarternion, the following matrix can be
+     * used:</p>
+     * <pre><code>R = [ 1 - 2y^2 - 2z^2,       2xy - 2zw,       2xz + 2yw,
+     *            2xy + 2zw, 1 - 2x^2 - 2z^2,       2yz - 2xw,
+     *            2xz - 2yw,       2yz + 2xw, 1 - 2x^2 - 2y^2 ]
+     * </code></pre>
+     * <p>This matrix can then be used to apply the rotation to a
+     *  column vector point with</p>
+     * <p><code>p' = Rp</code></p>
+     * <p>where <code>p</code> is in the device sensor coordinate system, and
+     *  <code>p'</code> is in the camera-oriented coordinate system.</p>
+     * <p><b>Units</b>:
+     * Quarternion coefficients</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     */
+    @PublicKey
+    public static final Key<float[]> LENS_POSE_ROTATION =
+            new Key<float[]>("android.lens.poseRotation", float[].class);
+
+    /**
+     * <p>Position of the camera optical center.</p>
+     * <p>As measured in the device sensor coordinate system, the
+     * position of the camera device's optical center, as a
+     * three-dimensional vector <code>(x,y,z)</code>.</p>
+     * <p>To transform a world position to a camera-device centered
+     * coordinate system, the position must be translated by this
+     * vector and then rotated by {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}.</p>
+     * <p><b>Units</b>: Meters</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CameraCharacteristics#LENS_POSE_ROTATION
+     */
+    @PublicKey
+    public static final Key<float[]> LENS_POSE_TRANSLATION =
+            new Key<float[]>("android.lens.poseTranslation", float[].class);
+
+    /**
+     * <p>The parameters for this camera device's intrinsic
+     * calibration.</p>
+     * <p>The five calibration parameters that describe the
+     * transform from camera-centric 3D coordinates to sensor
+     * pixel coordinates:</p>
+     * <pre><code>[f_x, f_y, c_x, c_y, s]
+     * </code></pre>
+     * <p>Where <code>f_x</code> and <code>f_y</code> are the horizontal and vertical
+     * focal lengths, <code>[c_x, c_y]</code> is the position of the optical
+     * axis, and <code>s</code> is a skew parameter for the sensor plane not
+     * being aligned with the lens plane.</p>
+     * <p>These are typically used within a transformation matrix K:</p>
+     * <pre><code>K = [ f_x,   s, c_x,
+     *        0, f_y, c_y,
+     *        0    0,   1 ]
+     * </code></pre>
+     * <p>which can then be combined with the camera pose rotation
+     * <code>R</code> and translation <code>t</code> ({@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} and
+     * {@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}, respective) to calculate the
+     * complete transform from world coordinates to pixel
+     * coordinates:</p>
+     * <pre><code>P = [ K 0   * [ R t
+     *      0 1 ]     0 1 ]
+     * </code></pre>
+     * <p>and with <code>p_w</code> being a point in the world coordinate system
+     * and <code>p_s</code> being a point in the camera active pixel array
+     * coordinate system, and with the mapping including the
+     * homogeneous division by z:</p>
+     * <pre><code> p_h = (x_h, y_h, z_h) = P p_w
+     * p_s = p_h / z_h
+     * </code></pre>
+     * <p>so <code>[x_s, y_s]</code> is the pixel coordinates of the world
+     * point, <code>z_s = 1</code>, and <code>w_s</code> is a measurement of disparity
+     * (depth) in pixel coordinates.</p>
+     * <p><b>Units</b>:
+     * Pixels in the android.sensor.activeArraySize coordinate
+     * system.</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CameraCharacteristics#LENS_POSE_ROTATION
+     * @see CameraCharacteristics#LENS_POSE_TRANSLATION
+     */
+    @PublicKey
+    public static final Key<float[]> LENS_INTRINSIC_CALIBRATION =
+            new Key<float[]>("android.lens.intrinsicCalibration", float[].class);
+
+    /**
+     * <p>The correction coefficients to correct for this camera device's
+     * radial lens distortion.</p>
+     * <p>Three cofficients <code>[kappa_1, kappa_2, kappa_3]</code> that
+     * can be used to correct the lens's radial geometric
+     * distortion with the mapping equations:</p>
+     * <pre><code> x_c = x_i * ( 1 + kappa_1 * r^2 + kappa_2 * r^4 + kappa_3 * r^6 )
+     * y_c = y_i * ( 1 + kappa_1 * r^2 + kappa_2 * r^4 + kappa_3 * r^6 )
+     * </code></pre>
+     * <p>where <code>[x_i, y_i]</code> are normalized coordinates with <code>(0,0)</code>
+     * at the lens optical center, and <code>[-1, 1]</code> are the edges of
+     * the active pixel array; and where <code>[x_c, y_c]</code> are the
+     * corrected normalized coordinates with radial distortion
+     * removed; and <code>r^2 = x_i^2 + y_i^2</code>.</p>
+     * <p><b>Units</b>:
+     * Coefficients for a 6th-degree even radial polynomial.</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     */
+    @PublicKey
+    public static final Key<float[]> LENS_RADIAL_DISTORTION =
+            new Key<float[]>("android.lens.radialDistortion", float[].class);
+
+    /**
      * <p>Mode of operation for the noise reduction algorithm.</p>
      * <p>The noise reduction algorithm attempts to improve image quality by removing
-     * excessive noise added by the capture process, especially in dark conditions.
-     * OFF means no noise reduction will be applied by the camera device.</p>
+     * excessive noise added by the capture process, especially in dark conditions.</p>
+     * <p>OFF means no noise reduction will be applied by the camera device, for both raw and
+     * YUV domain.</p>
+     * <p>MINIMAL means that only sensor raw domain basic noise reduction is enabled ,to remove
+     * demosaicing or other processing artifacts. For YUV_REPROCESSING, MINIMAL is same as OFF.
+     * This mode is optional, may not be support by all devices. The application should check
+     * {@link CameraCharacteristics#NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES android.noiseReduction.availableNoiseReductionModes} before using it.</p>
      * <p>FAST/HIGH_QUALITY both mean camera device determined noise filtering
      * will be applied. HIGH_QUALITY mode indicates that the camera device
      * will use the highest-quality noise filtering algorithms,
      * even if it slows down capture rate. FAST means the camera device will not
      * slow down capture rate when applying noise filtering.</p>
+     * <p>For YUV_REPROCESSING, these FAST/HIGH_QUALITY modes both mean that the camera device
+     * will apply FAST/HIGH_QUALITY YUV domain noise reduction, respectively. The camera device
+     * may adjust the noise reduction parameters for best image quality based on the
+     * {@link CaptureRequest#REPROCESS_EFFECTIVE_EXPOSURE_FACTOR android.reprocess.effectiveExposureFactor} if it is set.</p>
      * <p><b>Possible values:</b>
      * <ul>
      *   <li>{@link #NOISE_REDUCTION_MODE_OFF OFF}</li>
      *   <li>{@link #NOISE_REDUCTION_MODE_FAST FAST}</li>
      *   <li>{@link #NOISE_REDUCTION_MODE_HIGH_QUALITY HIGH_QUALITY}</li>
+     *   <li>{@link #NOISE_REDUCTION_MODE_MINIMAL MINIMAL}</li>
      * </ul></p>
      * <p><b>Available values for this device:</b><br>
      * {@link CameraCharacteristics#NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES android.noiseReduction.availableNoiseReductionModes}</p>
@@ -2497,9 +2688,11 @@
      *
      * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
      * @see CameraCharacteristics#NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES
+     * @see CaptureRequest#REPROCESS_EFFECTIVE_EXPOSURE_FACTOR
      * @see #NOISE_REDUCTION_MODE_OFF
      * @see #NOISE_REDUCTION_MODE_FAST
      * @see #NOISE_REDUCTION_MODE_HIGH_QUALITY
+     * @see #NOISE_REDUCTION_MODE_MINIMAL
      */
     @PublicKey
     public static final Key<Integer> NOISE_REDUCTION_MODE =
@@ -2984,6 +3177,8 @@
      *   <li>{@link #SHADING_MODE_FAST FAST}</li>
      *   <li>{@link #SHADING_MODE_HIGH_QUALITY HIGH_QUALITY}</li>
      * </ul></p>
+     * <p><b>Available values for this device:</b><br>
+     * {@link CameraCharacteristics#SHADING_AVAILABLE_MODES android.shading.availableModes}</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * <p><b>Full capability</b> -
      * Present on all camera devices that report being {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_FULL HARDWARE_LEVEL_FULL} devices in the
@@ -2992,6 +3187,7 @@
      * @see CaptureRequest#CONTROL_AE_MODE
      * @see CaptureRequest#CONTROL_AWB_MODE
      * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+     * @see CameraCharacteristics#SHADING_AVAILABLE_MODES
      * @see CaptureResult#STATISTICS_LENS_SHADING_CORRECTION_MAP
      * @see CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE
      * @see #SHADING_MODE_OFF
@@ -3155,7 +3351,7 @@
     /**
      * <p>The shading map is a low-resolution floating-point map
      * that lists the coefficients used to correct for vignetting, for each
-     * Bayer color channel.</p>
+     * Bayer color channel of RAW image data.</p>
      * <p>The least shaded section of the image should have a gain factor
      * of 1; all other sections should have gains above 1.</p>
      * <p>When {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode} = TRANSFORM_MATRIX, the map
@@ -3191,8 +3387,20 @@
      * <img alt="Green (odd rows) lens shading map" src="../../../../images/camera2/metadata/android.statistics.lensShadingMap/green_o_shading.png" />
      * <img alt="Blue lens shading map" src="../../../../images/camera2/metadata/android.statistics.lensShadingMap/blue_shading.png" /></p>
      * <p>As a visualization only, inverting the full-color map to recover an
-     * image of a gray wall (using bicubic interpolation for visual quality) as captured by the sensor gives:</p>
+     * image of a gray wall (using bicubic interpolation for visual quality)
+     * as captured by the sensor gives:</p>
      * <p><img alt="Image of a uniform white wall (inverse shading map)" src="../../../../images/camera2/metadata/android.statistics.lensShadingMap/inv_shading.png" /></p>
+     * <p>Note that the RAW image data might be subject to lens shading
+     * correction not reported on this map. Query
+     * {@link CameraCharacteristics#SENSOR_INFO_LENS_SHADING_APPLIED android.sensor.info.lensShadingApplied} to see if RAW image data has subject
+     * to lens shading correction. If {@link CameraCharacteristics#SENSOR_INFO_LENS_SHADING_APPLIED android.sensor.info.lensShadingApplied}
+     * is TRUE, the RAW image data is subject to partial or full lens shading
+     * correction. In the case full lens shading correction is applied to RAW
+     * images, the gain factor map reported in this key will contain all 1.0 gains.
+     * In other words, the map reported in this key is the remaining lens shading
+     * that needs to be applied on the RAW image to get images without lens shading
+     * artifacts. See {@link CameraCharacteristics#REQUEST_MAX_NUM_OUTPUT_RAW android.request.maxNumOutputRaw} for a list of RAW image
+     * formats.</p>
      * <p><b>Range of valid values:</b><br>
      * Each gain factor is &gt;= 1</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
@@ -3202,6 +3410,8 @@
      *
      * @see CaptureRequest#COLOR_CORRECTION_MODE
      * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+     * @see CameraCharacteristics#REQUEST_MAX_NUM_OUTPUT_RAW
+     * @see CameraCharacteristics#SENSOR_INFO_LENS_SHADING_APPLIED
      * @hide
      */
     public static final Key<float[]> STATISTICS_LENS_SHADING_MAP =
@@ -3339,12 +3549,15 @@
      *   <li>{@link #STATISTICS_LENS_SHADING_MAP_MODE_OFF OFF}</li>
      *   <li>{@link #STATISTICS_LENS_SHADING_MAP_MODE_ON ON}</li>
      * </ul></p>
+     * <p><b>Available values for this device:</b><br>
+     * {@link CameraCharacteristics#STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES android.statistics.info.availableLensShadingMapModes}</p>
      * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      * <p><b>Full capability</b> -
      * Present on all camera devices that report being {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_FULL HARDWARE_LEVEL_FULL} devices in the
      * {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
      *
      * @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
+     * @see CameraCharacteristics#STATISTICS_INFO_AVAILABLE_LENS_SHADING_MAP_MODES
      * @see #STATISTICS_LENS_SHADING_MAP_MODE_OFF
      * @see #STATISTICS_LENS_SHADING_MAP_MODE_ON
      */
@@ -3531,6 +3744,8 @@
      *   <li>{@link #TONEMAP_MODE_CONTRAST_CURVE CONTRAST_CURVE}</li>
      *   <li>{@link #TONEMAP_MODE_FAST FAST}</li>
      *   <li>{@link #TONEMAP_MODE_HIGH_QUALITY HIGH_QUALITY}</li>
+     *   <li>{@link #TONEMAP_MODE_GAMMA_VALUE GAMMA_VALUE}</li>
+     *   <li>{@link #TONEMAP_MODE_PRESET_CURVE PRESET_CURVE}</li>
      * </ul></p>
      * <p><b>Available values for this device:</b><br>
      * {@link CameraCharacteristics#TONEMAP_AVAILABLE_TONE_MAP_MODES android.tonemap.availableToneMapModes}</p>
@@ -3546,12 +3761,60 @@
      * @see #TONEMAP_MODE_CONTRAST_CURVE
      * @see #TONEMAP_MODE_FAST
      * @see #TONEMAP_MODE_HIGH_QUALITY
+     * @see #TONEMAP_MODE_GAMMA_VALUE
+     * @see #TONEMAP_MODE_PRESET_CURVE
      */
     @PublicKey
     public static final Key<Integer> TONEMAP_MODE =
             new Key<Integer>("android.tonemap.mode", int.class);
 
     /**
+     * <p>Tonemapping curve to use when {@link CaptureRequest#TONEMAP_MODE android.tonemap.mode} is
+     * GAMMA_VALUE</p>
+     * <p>The tonemap curve will be defined the following formula:
+     * * OUT = pow(IN, 1.0 / gamma)
+     * where IN and OUT is the input pixel value scaled to range [0.0, 1.0],
+     * pow is the power function and gamma is the gamma value specified by this
+     * key.</p>
+     * <p>The same curve will be applied to all color channels. The camera device
+     * may clip the input gamma value to its supported range. The actual applied
+     * value will be returned in capture result.</p>
+     * <p>The valid range of gamma value varies on different devices, but values
+     * within [1.0, 5.0] are guaranteed not to be clipped.</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#TONEMAP_MODE
+     */
+    @PublicKey
+    public static final Key<Float> TONEMAP_GAMMA =
+            new Key<Float>("android.tonemap.gamma", float.class);
+
+    /**
+     * <p>Tonemapping curve to use when {@link CaptureRequest#TONEMAP_MODE android.tonemap.mode} is
+     * PRESET_CURVE</p>
+     * <p>The tonemap curve will be defined by specified standard.</p>
+     * <p>sRGB (approximated by 16 control points):</p>
+     * <p><img alt="sRGB tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png" /></p>
+     * <p>Rec. 709 (approximated by 16 control points):</p>
+     * <p><img alt="Rec. 709 tonemapping curve" src="../../../../images/camera2/metadata/android.tonemap.curveRed/rec709_tonemap.png" /></p>
+     * <p>Note that above figures show a 16 control points approximation of preset
+     * curves. Camera devices may apply a different approximation to the curve.</p>
+     * <p><b>Possible values:</b>
+     * <ul>
+     *   <li>{@link #TONEMAP_PRESET_CURVE_SRGB SRGB}</li>
+     *   <li>{@link #TONEMAP_PRESET_CURVE_REC709 REC709}</li>
+     * </ul></p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#TONEMAP_MODE
+     * @see #TONEMAP_PRESET_CURVE_SRGB
+     * @see #TONEMAP_PRESET_CURVE_REC709
+     */
+    @PublicKey
+    public static final Key<Integer> TONEMAP_PRESET_CURVE =
+            new Key<Integer>("android.tonemap.presetCurve", int.class);
+
+    /**
      * <p>This LED is nominally used to indicate to the user
      * that the camera is powered on and may be streaming images back to the
      * Application Processor. In certain rare circumstances, the OS may
@@ -3657,6 +3920,52 @@
     public static final Key<Long> SYNC_FRAME_NUMBER =
             new Key<Long>("android.sync.frameNumber", long.class);
 
+    /**
+     * <p>The amount of exposure time increase factor applied to the original output
+     * frame by the application processing before sending for reprocessing.</p>
+     * <p>This is optional, and will be supported if the camera device supports YUV_REPROCESSING
+     * capability ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES android.request.availableCapabilities} contains YUV_REPROCESSING).</p>
+     * <p>For some YUV reprocessing use cases, the application may choose to filter the original
+     * output frames to effectively reduce the noise to the same level as a frame that was
+     * captured with longer exposure time. To be more specific, assuming the original captured
+     * images were captured with a sensitivity of S and an exposure time of T, the model in
+     * the camera device is that the amount of noise in the image would be approximately what
+     * would be expected if the original capture parameters had been a sensitivity of
+     * S/effectiveExposureFactor and an exposure time of T*effectiveExposureFactor, rather
+     * than S and T respectively. If the captured images were processed by the application
+     * before being sent for reprocessing, then the application may have used image processing
+     * algorithms and/or multi-frame image fusion to reduce the noise in the
+     * application-processed images (input images). By using the effectiveExposureFactor
+     * control, the application can communicate to the camera device the actual noise level
+     * improvement in the application-processed image. With this information, the camera
+     * device can select appropriate noise reduction and edge enhancement parameters to avoid
+     * excessive noise reduction ({@link CaptureRequest#NOISE_REDUCTION_MODE android.noiseReduction.mode}) and insufficient edge
+     * enhancement ({@link CaptureRequest#EDGE_MODE android.edge.mode}) being applied to the reprocessed frames.</p>
+     * <p>For example, for multi-frame image fusion use case, the application may fuse
+     * multiple output frames together to a final frame for reprocessing. When N image are
+     * fused into 1 image for reprocessing, the exposure time increase factor could be up to
+     * square root of N (based on a simple photon shot noise model). The camera device will
+     * adjust the reprocessing noise reduction and edge enhancement parameters accordingly to
+     * produce the best quality images.</p>
+     * <p>This is relative factor, 1.0 indicates the application hasn't processed the input
+     * buffer in a way that affects its effective exposure time.</p>
+     * <p>This control is only effective for YUV reprocessing capture request. For noise
+     * reduction reprocessing, it is only effective when <code>{@link CaptureRequest#NOISE_REDUCTION_MODE android.noiseReduction.mode} != OFF</code>.
+     * Similarly, for edge enhancement reprocessing, it is only effective when
+     * <code>{@link CaptureRequest#EDGE_MODE android.edge.mode} != OFF</code>.</p>
+     * <p><b>Units</b>: Relative exposure time increase factor.</p>
+     * <p><b>Range of valid values:</b><br>
+     * &gt;= 1.0</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
+     *
+     * @see CaptureRequest#EDGE_MODE
+     * @see CaptureRequest#NOISE_REDUCTION_MODE
+     * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+     */
+    @PublicKey
+    public static final Key<Float> REPROCESS_EFFECTIVE_EXPOSURE_FACTOR =
+            new Key<Float>("android.reprocess.effectiveExposureFactor", float.class);
+
     /*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
      * End generated code
      *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/
diff --git a/core/java/android/hardware/camera2/ICameraDeviceUser.aidl b/core/java/android/hardware/camera2/ICameraDeviceUser.aidl
index 50a58ed..01f2396 100644
--- a/core/java/android/hardware/camera2/ICameraDeviceUser.aidl
+++ b/core/java/android/hardware/camera2/ICameraDeviceUser.aidl
@@ -16,7 +16,7 @@
 
 package android.hardware.camera2;
 
-import android.view.Surface;
+import android.hardware.camera2.params.OutputConfiguration;
 import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.CaptureRequest;
 
@@ -66,7 +66,7 @@
     int deleteStream(int streamId);
 
     // non-negative value is the stream ID. negative value is status_t
-    int createStream(int width, int height, int format, in Surface surface);
+    int createStream(in OutputConfiguration outputConfiguration);
 
     int createDefaultRequest(int templateId, out CameraMetadataNative request);
 
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index ec450bd1..38f8e39 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -28,6 +28,7 @@
 import android.hardware.camera2.ICameraDeviceCallbacks;
 import android.hardware.camera2.ICameraDeviceUser;
 import android.hardware.camera2.TotalCaptureResult;
+import android.hardware.camera2.params.OutputConfiguration;
 import android.hardware.camera2.utils.CameraBinderDecorator;
 import android.hardware.camera2.utils.CameraRuntimeException;
 import android.hardware.camera2.utils.LongParcelable;
@@ -78,7 +79,8 @@
     private int mRepeatingRequestId = REQUEST_ID_NONE;
     private final ArrayList<Integer> mRepeatingRequestIdDeletedList = new ArrayList<Integer>();
     // Map stream IDs to Surfaces
-    private final SparseArray<Surface> mConfiguredOutputs = new SparseArray<Surface>();
+    private final SparseArray<OutputConfiguration> mConfiguredOutputs =
+            new SparseArray<OutputConfiguration>();
 
     private final String mCameraId;
     private final CameraCharacteristics mCharacteristics;
@@ -314,7 +316,11 @@
 
     public void configureOutputs(List<Surface> outputs) throws CameraAccessException {
         // Leave this here for backwards compatibility with older code using this directly
-        configureOutputsChecked(outputs);
+        ArrayList<OutputConfiguration> outputConfigs = new ArrayList<>(outputs.size());
+        for (Surface s : outputs) {
+            outputConfigs.add(new OutputConfiguration(s));
+        }
+        configureOutputsChecked(outputConfigs);
     }
 
     /**
@@ -333,28 +339,30 @@
      *
      * @throws CameraAccessException if there were any unexpected problems during configuration
      */
-    public boolean configureOutputsChecked(List<Surface> outputs) throws CameraAccessException {
+    public boolean configureOutputsChecked(List<OutputConfiguration> outputs)
+            throws CameraAccessException {
         // Treat a null input the same an empty list
         if (outputs == null) {
-            outputs = new ArrayList<Surface>();
+            outputs = new ArrayList<OutputConfiguration>();
         }
         boolean success = false;
 
         synchronized(mInterfaceLock) {
             checkIfCameraClosedOrInError();
-
-            HashSet<Surface> addSet = new HashSet<Surface>(outputs);    // Streams to create
-            List<Integer> deleteList = new ArrayList<Integer>();        // Streams to delete
+            // Streams to create
+            HashSet<OutputConfiguration> addSet = new HashSet<OutputConfiguration>(outputs);
+         // Streams to delete
+            List<Integer> deleteList = new ArrayList<Integer>();
 
             // Determine which streams need to be created, which to be deleted
             for (int i = 0; i < mConfiguredOutputs.size(); ++i) {
                 int streamId = mConfiguredOutputs.keyAt(i);
-                Surface s = mConfiguredOutputs.valueAt(i);
+                OutputConfiguration outConfig = mConfiguredOutputs.valueAt(i);
 
-                if (!outputs.contains(s)) {
+                if (!outputs.contains(outConfig)) {
                     deleteList.add(streamId);
                 } else {
-                    addSet.remove(s);  // Don't create a stream previously created
+                    addSet.remove(outConfig);  // Don't create a stream previously created
                 }
             }
 
@@ -372,11 +380,11 @@
                 }
 
                 // Add all new streams
-                for (Surface s : addSet) {
-                    // TODO: remove width,height,format since we are ignoring
-                    // it.
-                    int streamId = mRemoteDevice.createStream(0, 0, 0, s);
-                    mConfiguredOutputs.put(streamId, s);
+                for (OutputConfiguration outConfig : outputs) {
+                    if (addSet.contains(outConfig)) {
+                        int streamId = mRemoteDevice.createStream(outConfig);
+                        mConfiguredOutputs.put(streamId, outConfig);
+                    }
                 }
 
                 try {
@@ -417,6 +425,18 @@
     public void createCaptureSession(List<Surface> outputs,
             CameraCaptureSession.StateCallback callback, Handler handler)
             throws CameraAccessException {
+        List<OutputConfiguration> outConfigurations = new ArrayList<>(outputs.size());
+        for (Surface surface : outputs) {
+            outConfigurations.add(new OutputConfiguration(surface));
+        }
+        createCaptureSessionByOutputConfiguration(outConfigurations, callback, handler);
+    }
+
+    @Override
+    public void createCaptureSessionByOutputConfiguration(
+            List<OutputConfiguration> outputConfigurations,
+            CameraCaptureSession.StateCallback callback, Handler handler)
+            throws CameraAccessException {
         synchronized(mInterfaceLock) {
             if (DEBUG) {
                 Log.d(TAG, "createCaptureSession");
@@ -434,7 +454,8 @@
             boolean configureSuccess = true;
             CameraAccessException pendingException = null;
             try {
-                configureSuccess = configureOutputsChecked(outputs); // and then block until IDLE
+                // configure outputs and then block until IDLE
+                configureSuccess = configureOutputsChecked(outputConfigurations);
             } catch (CameraAccessException e) {
                 configureSuccess = false;
                 pendingException = e;
@@ -443,10 +464,14 @@
                 }
             }
 
+            List<Surface> outSurfaces = new ArrayList<>(outputConfigurations.size());
+            for (OutputConfiguration config : outputConfigurations) {
+                outSurfaces.add(config.getSurface());
+            }
             // Fire onConfigured if configureOutputs succeeded, fire onConfigureFailed otherwise.
             CameraCaptureSessionImpl newSession =
                     new CameraCaptureSessionImpl(mNextSessionId++,
-                            outputs, callback, handler, this, mDeviceHandler,
+                            outSurfaces, callback, handler, this, mDeviceHandler,
                             configureSuccess);
 
             // TODO: wait until current session closes, then create the new session
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index f47ce79..e926a98 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -830,11 +830,19 @@
                 CameraCharacteristics.SCALER_AVAILABLE_MIN_FRAME_DURATIONS);
         StreamConfigurationDuration[] stallDurations = getBase(
                 CameraCharacteristics.SCALER_AVAILABLE_STALL_DURATIONS);
+        StreamConfiguration[] depthConfigurations = getBase(
+                CameraCharacteristics.DEPTH_AVAILABLE_DEPTH_STREAM_CONFIGURATIONS);
+        StreamConfigurationDuration[] depthMinFrameDurations = getBase(
+                CameraCharacteristics.DEPTH_AVAILABLE_DEPTH_MIN_FRAME_DURATIONS);
+        StreamConfigurationDuration[] depthStallDurations = getBase(
+                CameraCharacteristics.DEPTH_AVAILABLE_DEPTH_STALL_DURATIONS);
         HighSpeedVideoConfiguration[] highSpeedVideoConfigurations = getBase(
                 CameraCharacteristics.CONTROL_AVAILABLE_HIGH_SPEED_VIDEO_CONFIGURATIONS);
 
         return new StreamConfigurationMap(
-                configurations, minFrameDurations, stallDurations, highSpeedVideoConfigurations);
+                configurations, minFrameDurations, stallDurations,
+                depthConfigurations, depthMinFrameDurations, depthStallDurations,
+                highSpeedVideoConfigurations);
     }
 
     private <T> Integer getMaxRegions(Key<T> key) {
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
index fcf172c..70f3463 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
@@ -26,6 +26,7 @@
 import android.hardware.camera2.utils.LongParcelable;
 import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.impl.CaptureResultExtras;
+import android.hardware.camera2.params.OutputConfiguration;
 import android.hardware.camera2.utils.CameraBinderDecorator;
 import android.hardware.camera2.utils.CameraRuntimeException;
 import android.os.ConditionVariable;
@@ -504,7 +505,7 @@
     }
 
     @Override
-    public int createStream(int width, int height, int format, Surface surface) {
+    public int createStream(OutputConfiguration outputConfiguration) {
         if (DEBUG) {
             Log.d(TAG, "createStream called.");
         }
@@ -518,8 +519,12 @@
                 Log.e(TAG, "Cannot create stream, beginConfigure hasn't been called yet.");
                 return CameraBinderDecorator.INVALID_OPERATION;
             }
+            if (outputConfiguration.getRotation() != OutputConfiguration.ROTATION_0) {
+                Log.e(TAG, "Cannot create stream, stream rotation is not supported.");
+                return CameraBinderDecorator.INVALID_OPERATION;
+            }
             int id = ++mSurfaceIdCounter;
-            mSurfaces.put(id, surface);
+            mSurfaces.put(id, outputConfiguration.getSurface());
             return id;
         }
     }
diff --git a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
index 347db05..802b938 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
@@ -474,6 +474,15 @@
 
             m.set(CONTROL_AE_COMPENSATION_STEP, ParamsUtils.createRational(step));
         }
+
+        /*
+         * control.aeLockAvailable
+         */
+        {
+            boolean aeLockAvailable = p.isAutoExposureLockSupported();
+
+            m.set(CONTROL_AE_LOCK_AVAILABLE, aeLockAvailable);
+        }
     }
 
 
@@ -571,6 +580,16 @@
                 Log.v(TAG, "mapControlAwb - control.awbAvailableModes set to " +
                         ListUtils.listToString(awbAvail));
             }
+
+
+            /*
+             * control.awbLockAvailable
+             */
+            {
+                boolean awbLockAvailable = p.isAutoWhiteBalanceLockSupported();
+
+                m.set(CONTROL_AWB_LOCK_AVAILABLE, awbLockAvailable);
+            }
         }
     }
 
@@ -618,17 +637,44 @@
         /*
          * android.control.availableSceneModes
          */
+        int maxNumDetectedFaces = p.getMaxNumDetectedFaces();
         List<String> sceneModes = p.getSupportedSceneModes();
         List<Integer> supportedSceneModes =
                 ArrayUtils.convertStringListToIntList(sceneModes, sLegacySceneModes, sSceneModes);
-        if (supportedSceneModes == null) { // camera1 doesn't support scene mode settings
-            supportedSceneModes = new ArrayList<Integer>();
-            supportedSceneModes.add(CONTROL_SCENE_MODE_DISABLED); // disabled is always available
+
+        // Special case where the only scene mode listed is AUTO => no scene mode
+        if (sceneModes != null && sceneModes.size() == 1 &&
+                sceneModes.get(0) == Parameters.SCENE_MODE_AUTO) {
+            supportedSceneModes = null;
         }
-        if (p.getMaxNumDetectedFaces() > 0) { // always supports FACE_PRIORITY when face detecting
-            supportedSceneModes.add(CONTROL_SCENE_MODE_FACE_PRIORITY);
+
+        boolean sceneModeSupported = true;
+        if (supportedSceneModes == null && maxNumDetectedFaces == 0) {
+            sceneModeSupported = false;
         }
-        m.set(CONTROL_AVAILABLE_SCENE_MODES, ArrayUtils.toIntArray(supportedSceneModes));
+
+        if (sceneModeSupported) {
+            if (supportedSceneModes == null) {
+                supportedSceneModes = new ArrayList<Integer>();
+            }
+            if (maxNumDetectedFaces > 0) { // always supports FACE_PRIORITY when face detecting
+                supportedSceneModes.add(CONTROL_SCENE_MODE_FACE_PRIORITY);
+            }
+            // Remove all DISABLED occurrences
+            if (supportedSceneModes.contains(CONTROL_SCENE_MODE_DISABLED)) {
+                while(supportedSceneModes.remove(new Integer(CONTROL_SCENE_MODE_DISABLED))) {}
+            }
+            m.set(CONTROL_AVAILABLE_SCENE_MODES, ArrayUtils.toIntArray(supportedSceneModes));
+        } else {
+            m.set(CONTROL_AVAILABLE_SCENE_MODES, new int[] {CONTROL_SCENE_MODE_DISABLED});
+        }
+
+        /*
+         * android.control.availableModes
+         */
+        m.set(CONTROL_AVAILABLE_MODES, sceneModeSupported ?
+                new int[] { CONTROL_MODE_AUTO, CONTROL_MODE_USE_SCENE_MODE } :
+                new int[] { CONTROL_MODE_AUTO });
     }
 
     private static void mapLens(CameraMetadataNative m, Camera.Parameters p) {
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.aidl b/core/java/android/hardware/camera2/params/OutputConfiguration.aidl
new file mode 100644
index 0000000..0921cd8
--- /dev/null
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2015 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.hardware.camera2.params;
+
+/** @hide */
+parcelable OutputConfiguration;
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
new file mode 100644
index 0000000..0a4ed39
--- /dev/null
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2015 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.hardware.camera2.params;
+
+import android.hardware.camera2.CameraDevice;
+import android.util.Log;
+import android.view.Surface;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import static com.android.internal.util.Preconditions.*;
+
+/**
+ * A class for describing camera output, which contains a {@link Surface} and its specific
+ * configuration for creating capture session.
+ *
+ * @see CameraDevice#createCaptureSession
+ *
+ * @hide
+ */
+public final class OutputConfiguration implements Parcelable {
+
+    /**
+     * Rotation constant: 0 degree rotation (no rotation)
+     */
+    public static final int ROTATION_0 = 0;
+
+    /**
+     * Rotation constant: 90 degree counterclockwise rotation.
+     */
+    public static final int ROTATION_90 = 1;
+
+    /**
+     * Rotation constant: 180 degree counterclockwise rotation.
+     */
+    public static final int ROTATION_180 = 2;
+
+    /**
+     * Rotation constant: 270 degree counterclockwise rotation.
+     */
+    public static final int ROTATION_270 = 3;
+
+    /**
+     * Create a new immutable SurfaceConfiguration instance.
+     *
+     * @param surface
+     *          A Surface for camera to output to.
+     *
+     * <p>This constructor creates a default configuration</p>
+     *
+     */
+    public OutputConfiguration(Surface surface) {
+        checkNotNull(surface, "Surface must not be null");
+        mSurface = surface;
+        mRotation = ROTATION_0;
+    }
+
+    /**
+     * Create a new immutable SurfaceConfiguration instance.
+     *
+     * <p>This constructor takes an argument for desired camera rotation</p>
+     *
+     * @param surface
+     *          A Surface for camera to output to.
+     * @param rotation
+     *          The desired rotation to be applied on camera output. Value must be one of
+     *          ROTATION_[0, 90, 180, 270]. Note that when the rotation is 90 or 270 degree,
+     *          application should make sure corresponding surface size has width and height
+     *          transposed corresponding to the width and height without rotation. For example,
+     *          if application needs camera to capture 1280x720 picture and rotate it by 90 degree,
+     *          application should set rotation to {@code ROTATION_90} and make sure the
+     *          corresponding Surface size is 720x1280. Note that {@link CameraDevice} might
+     *          throw {@code IllegalArgumentException} if device cannot perform such rotation.
+     *
+     */
+    public OutputConfiguration(Surface surface, int rotation) {
+        checkNotNull(surface, "Surface must not be null");
+        checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
+        mSurface = surface;
+        mRotation = rotation;
+    }
+
+    /**
+     * Create an OutputConfiguration from Parcel.
+     */
+    private OutputConfiguration(Parcel source) {
+        int rotation = source.readInt();
+        Surface surface = Surface.CREATOR.createFromParcel(source);
+        checkNotNull(surface, "Surface must not be null");
+        checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
+        mSurface = surface;
+        mRotation = rotation;
+    }
+
+    /**
+     * Get the {@link Surface} associated with this {@link OutputConfiguration}.
+     *
+     * @return the {@link Surface} associated with this {@link OutputConfiguration}.
+     */
+    public Surface getSurface() {
+        return mSurface;
+    }
+
+    /**
+     * Get the rotation associated with this {@link OutputConfiguration}.
+     *
+     * @return the rotation associated with this {@link OutputConfiguration}.
+     *         Value will be one of ROTATION_[0, 90, 180, 270]
+     */
+    public int getRotation() {
+        return mRotation;
+    }
+
+    public static final Parcelable.Creator<OutputConfiguration> CREATOR =
+            new Parcelable.Creator<OutputConfiguration>() {
+        @Override
+        public OutputConfiguration createFromParcel(Parcel source) {
+            try {
+                OutputConfiguration outputConfiguration = new OutputConfiguration(source);
+                return outputConfiguration;
+            } catch (Exception e) {
+                Log.e(TAG, "Exception creating OutputConfiguration from parcel", e);
+                return null;
+            }
+        }
+
+        @Override
+        public OutputConfiguration[] newArray(int size) {
+            return new OutputConfiguration[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        if (dest == null) {
+            throw new IllegalArgumentException("dest must not be null");
+        }
+        dest.writeInt(mRotation);
+        mSurface.writeToParcel(dest, flags);
+    }
+
+    private static final String TAG = "OutputConfiguration";
+    private final Surface mSurface;
+    private final int mRotation;
+}
diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
index 479c842..a875b47 100644
--- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
@@ -90,11 +90,28 @@
             StreamConfiguration[] configurations,
             StreamConfigurationDuration[] minFrameDurations,
             StreamConfigurationDuration[] stallDurations,
+            StreamConfiguration[] depthConfigurations,
+            StreamConfigurationDuration[] depthMinFrameDurations,
+            StreamConfigurationDuration[] depthStallDurations,
             HighSpeedVideoConfiguration[] highSpeedVideoConfigurations) {
 
         mConfigurations = checkArrayElementsNotNull(configurations, "configurations");
         mMinFrameDurations = checkArrayElementsNotNull(minFrameDurations, "minFrameDurations");
         mStallDurations = checkArrayElementsNotNull(stallDurations, "stallDurations");
+
+        if (depthConfigurations == null) {
+            mDepthConfigurations = new StreamConfiguration[0];
+            mDepthMinFrameDurations = new StreamConfigurationDuration[0];
+            mDepthStallDurations = new StreamConfigurationDuration[0];
+        } else {
+            mDepthConfigurations = checkArrayElementsNotNull(depthConfigurations,
+                    "depthConfigurations");
+            mDepthMinFrameDurations = checkArrayElementsNotNull(depthMinFrameDurations,
+                    "depthMinFrameDurations");
+            mDepthStallDurations = checkArrayElementsNotNull(depthStallDurations,
+                    "depthStallDurations");
+        }
+
         if (highSpeedVideoConfigurations == null) {
             mHighSpeedVideoConfigurations = new HighSpeedVideoConfiguration[0];
         } else {
@@ -111,9 +128,24 @@
             if (count == null) {
                 count = 0;
             }
-            count = count + 1;
 
-            map.put(config.getFormat(), count);
+            map.put(config.getFormat(), count + 1);
+        }
+
+        // For each depth format, track how many sizes there are available to configure
+        for (StreamConfiguration config : mDepthConfigurations) {
+            if (!config.isOutput()) {
+                // Ignoring input depth configs
+                continue;
+            }
+
+            Integer count = mDepthOutputFormats.get(config.getFormat());
+
+            if (count == null) {
+                count = 0;
+            }
+
+            mDepthOutputFormats.put(config.getFormat(), count + 1);
         }
 
         if (!mOutputFormats.containsKey(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED)) {
@@ -215,8 +247,13 @@
     public boolean isOutputSupportedFor(int format) {
         checkArgumentFormat(format);
 
-        format = imageFormatToInternal(format);
-        return getFormatsMap(/*output*/true).containsKey(format);
+        int internalFormat = imageFormatToInternal(format);
+        int dataspace = imageFormatToDataspace(format);
+        if (dataspace == HAL_DATASPACE_DEPTH) {
+            return mDepthOutputFormats.containsKey(internalFormat);
+        } else {
+            return getFormatsMap(/*output*/true).containsKey(internalFormat);
+        }
     }
 
     /**
@@ -387,7 +424,8 @@
             return null;
         }
 
-        return getInternalFormatSizes(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, /*output*/true);
+        return getInternalFormatSizes(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED,
+                HAL_DATASPACE_UNKNOWN,/*output*/true);
     }
 
     /**
@@ -600,7 +638,10 @@
         checkNotNull(size, "size must not be null");
         checkArgumentFormatSupported(format, /*output*/true);
 
-        return getInternalFormatDuration(imageFormatToInternal(format), size, DURATION_MIN_FRAME);
+        return getInternalFormatDuration(imageFormatToInternal(format),
+                imageFormatToDataspace(format),
+                size,
+                DURATION_MIN_FRAME);
     }
 
     /**
@@ -653,6 +694,7 @@
         }
 
         return getInternalFormatDuration(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED,
+                HAL_DATASPACE_UNKNOWN,
                 size, DURATION_MIN_FRAME);
     }
 
@@ -742,7 +784,9 @@
         checkArgumentFormatSupported(format, /*output*/true);
 
         return getInternalFormatDuration(imageFormatToInternal(format),
-                size, DURATION_STALL);
+                imageFormatToDataspace(format),
+                size,
+                DURATION_STALL);
     }
 
     /**
@@ -779,7 +823,7 @@
         }
 
         return getInternalFormatDuration(HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED,
-                size, DURATION_STALL);
+                HAL_DATASPACE_UNKNOWN, size, DURATION_STALL);
     }
 
     /**
@@ -858,6 +902,7 @@
             case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
             case HAL_PIXEL_FORMAT_BLOB:
             case HAL_PIXEL_FORMAT_RAW_OPAQUE:
+            case HAL_PIXEL_FORMAT_Y16:
                 return format;
             case ImageFormat.JPEG:
                 throw new IllegalArgumentException(
@@ -897,8 +942,8 @@
     }
 
     /**
-     * Convert a public-visible {@code ImageFormat} into an internal format
-     * compatible with {@code graphics.h}.
+     * Convert an internal format compatible with {@code graphics.h} into public-visible
+     * {@code ImageFormat}. This assumes the dataspace of the format is not HAL_DATASPACE_DEPTH.
      *
      * <p>In particular these formats are converted:
      * <ul>
@@ -912,7 +957,8 @@
      *
      * <p>All other formats are returned as-is, no further invalid check is performed.</p>
      *
-     * <p>This function is the dual of {@link #imageFormatToInternal}.</p>
+     * <p>This function is the dual of {@link #imageFormatToInternal} for dataspaces other than
+     * HAL_DATASPACE_DEPTH.</p>
      *
      * @param format image format from {@link ImageFormat} or {@link PixelFormat}
      * @return the converted image formats
@@ -941,6 +987,55 @@
     }
 
     /**
+     * Convert an internal format compatible with {@code graphics.h} into public-visible
+     * {@code ImageFormat}. This assumes the dataspace of the format is HAL_DATASPACE_DEPTH.
+     *
+     * <p>In particular these formats are converted:
+     * <ul>
+     * <li>HAL_PIXEL_FORMAT_BLOB => ImageFormat.DEPTH_POINT_CLOUD
+     * <li>HAL_PIXEL_FORMAT_Y16 => ImageFormat.DEPTH16
+     * </ul>
+     * </p>
+     *
+     * <p>Passing in an implementation-defined format which has no public equivalent will fail;
+     * as will passing in a public format which has a different internal format equivalent.
+     * See {@link #checkArgumentFormat} for more details about a legal public format.</p>
+     *
+     * <p>All other formats are returned as-is, no further invalid check is performed.</p>
+     *
+     * <p>This function is the dual of {@link #imageFormatToInternal} for formats associated with
+     * HAL_DATASPACE_DEPTH.</p>
+     *
+     * @param format image format from {@link ImageFormat} or {@link PixelFormat}
+     * @return the converted image formats
+     *
+     * @throws IllegalArgumentException
+     *          if {@code format} is {@code HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED} or
+     *          {@link ImageFormat#JPEG}
+     *
+     * @see ImageFormat
+     * @see PixelFormat
+     * @see #checkArgumentFormat
+     */
+    static int depthFormatToPublic(int format) {
+        switch (format) {
+            case HAL_PIXEL_FORMAT_BLOB:
+                return ImageFormat.DEPTH_POINT_CLOUD;
+            case HAL_PIXEL_FORMAT_Y16:
+                return ImageFormat.DEPTH16;
+            case ImageFormat.JPEG:
+                throw new IllegalArgumentException(
+                        "ImageFormat.JPEG is an unknown internal format");
+            case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
+                throw new IllegalArgumentException(
+                        "IMPLEMENTATION_DEFINED must not leak to public API");
+            default:
+                throw new IllegalArgumentException(
+                        "Unknown DATASPACE_DEPTH format " + format);
+        }
+    }
+
+    /**
      * Convert image formats from internal to public formats (in-place).
      *
      * @param formats an array of image formats
@@ -967,6 +1062,8 @@
      * <p>In particular these formats are converted:
      * <ul>
      * <li>ImageFormat.JPEG => HAL_PIXEL_FORMAT_BLOB
+     * <li>ImageFormat.DEPTH_POINT_CLOUD => HAL_PIXEL_FORMAT_BLOB
+     * <li>ImageFormat.DEPTH16 => HAL_PIXEL_FORMAT_Y16
      * </ul>
      * </p>
      *
@@ -990,7 +1087,10 @@
     static int imageFormatToInternal(int format) {
         switch (format) {
             case ImageFormat.JPEG:
+            case ImageFormat.DEPTH_POINT_CLOUD:
                 return HAL_PIXEL_FORMAT_BLOB;
+            case ImageFormat.DEPTH16:
+                return HAL_PIXEL_FORMAT_Y16;
             case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
                 throw new IllegalArgumentException(
                         "IMPLEMENTATION_DEFINED is not allowed via public API");
@@ -1000,6 +1100,48 @@
     }
 
     /**
+     * Convert a public format compatible with {@code ImageFormat} to an internal dataspace
+     * from {@code graphics.h}.
+     *
+     * <p>In particular these formats are converted:
+     * <ul>
+     * <li>ImageFormat.JPEG => HAL_DATASPACE_JFIF
+     * <li>ImageFormat.DEPTH_POINT_CLOUD => HAL_DATASPACE_DEPTH
+     * <li>ImageFormat.DEPTH16 => HAL_DATASPACE_DEPTH
+     * <li>others => HAL_DATASPACE_UNKNOWN
+     * </ul>
+     * </p>
+     *
+     * <p>Passing in an implementation-defined format here will fail (it's not a public format);
+     * as will passing in an internal format which has a different public format equivalent.
+     * See {@link #checkArgumentFormat} for more details about a legal public format.</p>
+     *
+     * <p>All other formats are returned as-is, no invalid check is performed.</p>
+     *
+     * <p>This function is the dual of {@link #imageFormatToPublic}.</p>
+     *
+     * @param format public image format from {@link ImageFormat} or {@link PixelFormat}
+     * @return the converted image formats
+     *
+     * @see ImageFormat
+     * @see PixelFormat
+     *
+     * @throws IllegalArgumentException
+     *              if {@code format} was {@code HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED}
+     */
+    static int imageFormatToDataspace(int format) {
+        switch (format) {
+            case ImageFormat.JPEG:
+                return HAL_DATASPACE_JFIF;
+            case ImageFormat.DEPTH_POINT_CLOUD:
+            case ImageFormat.DEPTH16:
+                return HAL_DATASPACE_DEPTH;
+            default:
+                return HAL_DATASPACE_UNKNOWN;
+        }
+    }
+
+    /**
      * Convert image formats from public to internal formats (in-place).
      *
      * @param formats an array of image formats
@@ -1028,13 +1170,16 @@
             return null;
         }
 
-        format = imageFormatToInternal(format);
+        int internalFormat = imageFormatToInternal(format);
+        int dataspace = imageFormatToDataspace(format);
 
-        return getInternalFormatSizes(format, output);
+        return getInternalFormatSizes(internalFormat, dataspace, output);
     }
 
-    private Size[] getInternalFormatSizes(int format, boolean output) {
-        HashMap<Integer, Integer> formatsMap = getFormatsMap(output);
+    private Size[] getInternalFormatSizes(int format, int dataspace, boolean output) {
+
+        HashMap<Integer, Integer> formatsMap =
+                (dataspace == HAL_DATASPACE_DEPTH) ? mDepthOutputFormats : getFormatsMap(output);
 
         Integer sizesCount = formatsMap.get(format);
         if (sizesCount == null) {
@@ -1045,7 +1190,11 @@
         Size[] sizes = new Size[len];
         int sizeIndex = 0;
 
-        for (StreamConfiguration config : mConfigurations) {
+        StreamConfiguration[] configurations =
+                (dataspace == HAL_DATASPACE_DEPTH) ? mDepthConfigurations : mConfigurations;
+
+
+        for (StreamConfiguration config : configurations) {
             if (config.getFormat() == format && config.isOutput() == output) {
                 sizes[sizeIndex++] = config.getSize();
             }
@@ -1068,15 +1217,19 @@
         for (int format : getFormatsMap(output).keySet()) {
             if (format != HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED &&
                 format != HAL_PIXEL_FORMAT_RAW_OPAQUE) {
-                formats[i++] = format;
+                formats[i++] = imageFormatToPublic(format);
             }
         }
-
+        if (output) {
+            for (int format : mDepthOutputFormats.keySet()) {
+                formats[i++] = depthFormatToPublic(format);
+            }
+        }
         if (formats.length != i) {
             throw new AssertionError("Too few formats " + i + ", expected " + formats.length);
         }
 
-        return imageFormatToPublic(formats);
+        return formats;
     }
 
     /** Get the format -> size count map for either output or input formats */
@@ -1084,14 +1237,14 @@
         return output ? mOutputFormats : mInputFormats;
     }
 
-    private long getInternalFormatDuration(int format, Size size, int duration) {
+    private long getInternalFormatDuration(int format, int dataspace, Size size, int duration) {
         // assume format is already checked, since its internal
 
-        if (!arrayContains(getInternalFormatSizes(format, /*output*/true), size)) {
+        if (!arrayContains(getInternalFormatSizes(format, dataspace, /*output*/true), size)) {
             throw new IllegalArgumentException("size was not supported");
         }
 
-        StreamConfigurationDuration[] durations = getDurations(duration);
+        StreamConfigurationDuration[] durations = getDurations(duration, dataspace);
 
         for (StreamConfigurationDuration configurationDuration : durations) {
             if (configurationDuration.getFormat() == format &&
@@ -1110,12 +1263,14 @@
      * @see #DURATION_MIN_FRAME
      * @see #DURATION_STALL
      * */
-    private StreamConfigurationDuration[] getDurations(int duration) {
+    private StreamConfigurationDuration[] getDurations(int duration, int dataspace) {
         switch (duration) {
             case DURATION_MIN_FRAME:
-                return mMinFrameDurations;
+                return (dataspace == HAL_DATASPACE_DEPTH) ?
+                        mDepthMinFrameDurations : mMinFrameDurations;
             case DURATION_STALL:
-                return mStallDurations;
+                return (dataspace == HAL_DATASPACE_DEPTH) ?
+                        mDepthStallDurations : mStallDurations;
             default:
                 throw new IllegalArgumentException("duration was invalid");
         }
@@ -1132,6 +1287,9 @@
         if (formatsMap.containsKey(HAL_PIXEL_FORMAT_RAW_OPAQUE)) {
             size -= 1;
         }
+        if (output) {
+            size += mDepthOutputFormats.size();
+        }
 
         return size;
     }
@@ -1154,10 +1312,14 @@
     private static final int HAL_PIXEL_FORMAT_BLOB = 0x21;
     private static final int HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 0x22;
     private static final int HAL_PIXEL_FORMAT_RAW_OPAQUE = 0x24;
+    private static final int HAL_PIXEL_FORMAT_Y16 = 0x20363159;
+
+    private static final int HAL_DATASPACE_UNKNOWN = 0x0;
+    private static final int HAL_DATASPACE_JFIF = 0x101;
+    private static final int HAL_DATASPACE_DEPTH = 0x1000;
 
     /**
-     * @see #getDurations(int)
-     * @see #getDurationDefault(int)
+     * @see #getDurations(int, int)
      */
     private static final int DURATION_MIN_FRAME = 0;
     private static final int DURATION_STALL = 1;
@@ -1165,6 +1327,11 @@
     private final StreamConfiguration[] mConfigurations;
     private final StreamConfigurationDuration[] mMinFrameDurations;
     private final StreamConfigurationDuration[] mStallDurations;
+
+    private final StreamConfiguration[] mDepthConfigurations;
+    private final StreamConfigurationDuration[] mDepthMinFrameDurations;
+    private final StreamConfigurationDuration[] mDepthStallDurations;
+
     private final HighSpeedVideoConfiguration[] mHighSpeedVideoConfigurations;
 
     /** ImageFormat -> num output sizes mapping */
@@ -1173,6 +1340,9 @@
     /** ImageFormat -> num input sizes mapping */
     private final HashMap</*ImageFormat*/Integer, /*Count*/Integer> mInputFormats =
             new HashMap<Integer, Integer>();
+    /** ImageFormat -> num depth output sizes mapping */
+    private final HashMap</*ImageFormat*/Integer, /*Count*/Integer> mDepthOutputFormats =
+            new HashMap<Integer, Integer>();
     /** High speed video Size -> FPS range count mapping*/
     private final HashMap</*HighSpeedVideoSize*/Size, /*Count*/Integer> mHighSpeedVideoSizeMap =
             new HashMap<Size, Integer>();
diff --git a/core/java/android/net/BaseDhcpStateMachine.java b/core/java/android/net/BaseDhcpStateMachine.java
new file mode 100644
index 0000000..a25847d
--- /dev/null
+++ b/core/java/android/net/BaseDhcpStateMachine.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2015 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.net;
+
+import com.android.internal.util.StateMachine;
+
+/**
+ * Interface that must be implemented by DHCP state machines.
+ *
+ * This is an abstract class instead of a Java interface so that callers can just declare an object
+ * of this type and be able to call all the methods defined by either StateMachine or this class.
+ *
+ * @hide
+ */
+public abstract class BaseDhcpStateMachine extends StateMachine {
+    protected BaseDhcpStateMachine(String tag) {
+        super(tag);
+    }
+    public abstract void registerForPreDhcpNotification();
+    public abstract void doQuit();
+}
diff --git a/core/java/android/net/BaseNetworkStateTracker.java b/core/java/android/net/BaseNetworkStateTracker.java
deleted file mode 100644
index e4e5b1e..0000000
--- a/core/java/android/net/BaseNetworkStateTracker.java
+++ /dev/null
@@ -1,205 +0,0 @@
-/*
- * 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 android.net;
-
-import android.content.Context;
-import android.os.Handler;
-import android.os.Messenger;
-
-import java.util.concurrent.atomic.AtomicBoolean;
-
-import com.android.internal.util.Preconditions;
-
-/**
- * Interface to control and observe state of a specific network, hiding
- * network-specific details from {@link ConnectivityManager}. Surfaces events
- * through the registered {@link Handler} to enable {@link ConnectivityManager}
- * to respond to state changes over time.
- *
- * @hide
- */
-public abstract class BaseNetworkStateTracker implements NetworkStateTracker {
-    // TODO: better document threading expectations
-    // TODO: migrate to make NetworkStateTracker abstract class
-
-    public static final String PROP_TCP_BUFFER_UNKNOWN = "net.tcp.buffersize.unknown";
-    public static final String PROP_TCP_BUFFER_WIFI = "net.tcp.buffersize.wifi";
-
-    protected Context mContext;
-    private Handler mTarget;
-
-    protected NetworkInfo mNetworkInfo;
-    protected LinkProperties mLinkProperties;
-    protected NetworkCapabilities mNetworkCapabilities;
-    protected Network mNetwork = new Network(ConnectivityManager.NETID_UNSET);
-
-    private AtomicBoolean mTeardownRequested = new AtomicBoolean(false);
-    private AtomicBoolean mPrivateDnsRouteSet = new AtomicBoolean(false);
-    private AtomicBoolean mDefaultRouteSet = new AtomicBoolean(false);
-
-    public BaseNetworkStateTracker(int networkType) {
-        mNetworkInfo = new NetworkInfo(
-                networkType, -1, ConnectivityManager.getNetworkTypeName(networkType), null);
-        mLinkProperties = new LinkProperties();
-        mNetworkCapabilities = new NetworkCapabilities();
-    }
-
-    protected BaseNetworkStateTracker() {
-        // By default, let the sub classes construct everything
-    }
-
-    @Deprecated
-    protected Handler getTargetHandler() {
-        return mTarget;
-    }
-
-    protected final void dispatchStateChanged() {
-        // TODO: include snapshot of other fields when sending
-        mTarget.obtainMessage(EVENT_STATE_CHANGED, getNetworkInfo()).sendToTarget();
-    }
-
-    protected final void dispatchConfigurationChanged() {
-        // TODO: include snapshot of other fields when sending
-        mTarget.obtainMessage(EVENT_CONFIGURATION_CHANGED, getNetworkInfo()).sendToTarget();
-    }
-
-    @Override
-    public void startMonitoring(Context context, Handler target) {
-        mContext = Preconditions.checkNotNull(context);
-        mTarget = Preconditions.checkNotNull(target);
-        startMonitoringInternal();
-    }
-
-    protected void startMonitoringInternal() {
-
-    }
-
-    @Override
-    public NetworkInfo getNetworkInfo() {
-        return new NetworkInfo(mNetworkInfo);
-    }
-
-    @Override
-    public LinkProperties getLinkProperties() {
-        return new LinkProperties(mLinkProperties);
-    }
-
-    @Override
-    public NetworkCapabilities getNetworkCapabilities() {
-        return new NetworkCapabilities(mNetworkCapabilities);
-    }
-
-    @Override
-    public LinkQualityInfo getLinkQualityInfo() {
-        return null;
-    }
-
-    @Override
-    public void captivePortalCheckCompleted(boolean isCaptivePortal) {
-        // not implemented
-    }
-
-    @Override
-    public boolean setRadio(boolean turnOn) {
-        // Base tracker doesn't handle radios
-        return true;
-    }
-
-    @Override
-    public boolean isAvailable() {
-        return mNetworkInfo.isAvailable();
-    }
-
-    @Override
-    public void setUserDataEnable(boolean enabled) {
-        // Base tracker doesn't handle enabled flags
-    }
-
-    @Override
-    public void setPolicyDataEnable(boolean enabled) {
-        // Base tracker doesn't handle enabled flags
-    }
-
-    @Override
-    public boolean isPrivateDnsRouteSet() {
-        return mPrivateDnsRouteSet.get();
-    }
-
-    @Override
-    public void privateDnsRouteSet(boolean enabled) {
-        mPrivateDnsRouteSet.set(enabled);
-    }
-
-    @Override
-    public boolean isDefaultRouteSet() {
-        return mDefaultRouteSet.get();
-    }
-
-    @Override
-    public void defaultRouteSet(boolean enabled) {
-        mDefaultRouteSet.set(enabled);
-    }
-
-    @Override
-    public boolean isTeardownRequested() {
-        return mTeardownRequested.get();
-    }
-
-    @Override
-    public void setTeardownRequested(boolean isRequested) {
-        mTeardownRequested.set(isRequested);
-    }
-
-    @Override
-    public void setDependencyMet(boolean met) {
-        // Base tracker doesn't handle dependencies
-    }
-
-    @Override
-    public void supplyMessenger(Messenger messenger) {
-        // not supported on this network
-    }
-
-    @Override
-    public String getNetworkInterfaceName() {
-        if (mLinkProperties != null) {
-            return mLinkProperties.getInterfaceName();
-        } else {
-            return null;
-        }
-    }
-
-    @Override
-    public void startSampling(SamplingDataTracker.SamplingSnapshot s) {
-        // nothing to do
-    }
-
-    @Override
-    public void stopSampling(SamplingDataTracker.SamplingSnapshot s) {
-        // nothing to do
-    }
-
-    @Override
-    public void setNetId(int netId) {
-        mNetwork = new Network(netId);
-    }
-
-    @Override
-    public Network getNetwork() {
-        return mNetwork;
-    }
-}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index eb2df0b..7b758bb 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -110,13 +110,41 @@
             "android.net.conn.CONNECTIVITY_CHANGE_IMMEDIATE";
 
     /**
+     * The device has connected to a network that has presented a captive
+     * portal, which is blocking Internet connectivity. The user was presented
+     * with a notification that network sign in is required,
+     * and the user invoked the notification's action indicating they
+     * desire to sign in to the network. Apps handling this action should
+     * facilitate signing in to the network. This action includes a
+     * {@link Network} typed extra called {@link #EXTRA_NETWORK} that represents
+     * the network presenting the captive portal; all communication with the
+     * captive portal must be done using this {@code Network} object.
+     * <p/>
+     * When the app handling this action believes the user has signed in to
+     * the network and the captive portal has been dismissed, the app should call
+     * {@link #reportCaptivePortalDismissed} so the system can reevaluate the network.
+     * If reevaluation finds the network no longer subject to a captive portal,
+     * the network may become the default active data network.
+     * <p/>
+     * When the app handling this action believes the user explicitly wants
+     * to ignore the captive portal and the network, the app should call
+     * {@link #ignoreNetworkWithCaptivePortal}.
+     * <p/>
+     * Note that this action includes a {@code String} extra named
+     * {@link #EXTRA_CAPTIVE_PORTAL_TOKEN} that must
+     * be passed in to {@link #reportCaptivePortalDismissed} and
+     * {@link #ignoreNetworkWithCaptivePortal}.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_CAPTIVE_PORTAL_SIGN_IN = "android.net.conn.CAPTIVE_PORTAL";
+
+    /**
      * The lookup key for a {@link NetworkInfo} object. Retrieve with
      * {@link android.content.Intent#getParcelableExtra(String)}.
      *
      * @deprecated Since {@link NetworkInfo} can vary based on UID, applications
      *             should always obtain network information through
-     *             {@link #getActiveNetworkInfo()} or
-     *             {@link #getAllNetworkInfo()}.
+     *             {@link #getActiveNetworkInfo()}.
      * @see #EXTRA_NETWORK_TYPE
      */
     @Deprecated
@@ -124,8 +152,6 @@
 
     /**
      * Network type which triggered a {@link #CONNECTIVITY_ACTION} broadcast.
-     * Can be used with {@link #getNetworkInfo(int)} to get {@link NetworkInfo}
-     * state based on the calling application.
      *
      * @see android.content.Intent#getIntExtra(String, int)
      */
@@ -175,6 +201,15 @@
     public static final String EXTRA_INET_CONDITION = "inetCondition";
 
     /**
+     * The lookup key for a string that is sent out with
+     * {@link #ACTION_CAPTIVE_PORTAL_SIGN_IN}. This string must be
+     * passed in to {@link #reportCaptivePortalDismissed} and
+     * {@link #ignoreNetworkWithCaptivePortal}. Retrieve it with
+     * {@link android.content.Intent#getStringExtra(String)}.
+     */
+    public static final String EXTRA_CAPTIVE_PORTAL_TOKEN = "captivePortalToken";
+
+    /**
      * Broadcast action to indicate the change of data activity status
      * (idle or active) on a network in a recent period.
      * The network becomes active when data transmission is started, or
@@ -588,9 +623,9 @@
      * network.
      *
      * @return a {@link NetworkInfo} object for the current default network
-     *        or {@code null} if no network default network is currently active
+     *        or {@code null} if no default network is currently active
      *
-     * <p>This method requires the call to hold the permission
+     * <p>This method requires the caller to hold the permission
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      */
     public NetworkInfo getActiveNetworkInfo() {
@@ -602,6 +637,27 @@
     }
 
     /**
+     * Returns a {@link Network} object corresponding to the currently active
+     * default data network.  In the event that the current active default data
+     * network disconnects, the returned {@code Network} object will no longer
+     * be usable.  This will return {@code null} when there is no default
+     * network.
+     *
+     * @return a {@link Network} object for the current default network or
+     *        {@code null} if no default network is currently active
+     *
+     * <p>This method requires the caller to hold the permission
+     * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
+     */
+    public Network getActiveNetwork() {
+        try {
+            return mService.getActiveNetwork();
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
      * Returns details about the currently active default data network
      * for a given uid.  This is for internal use only to avoid spying
      * other apps.
@@ -634,6 +690,10 @@
      *
      * <p>This method requires the caller to hold the permission
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
+     *
+     * @deprecated This method does not support multiple connected networks
+     *             of the same type. Use {@link #getAllNetworks} and
+     *             {@link #getNetworkInfo(android.net.Network)} instead.
      */
     public NetworkInfo getNetworkInfo(int networkType) {
         try {
@@ -673,6 +733,10 @@
      *
      * <p>This method requires the caller to hold the permission
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
+     *
+     * @deprecated This method does not support multiple connected networks
+     *             of the same type. Use {@link #getAllNetworks} and
+     *             {@link #getNetworkInfo(android.net.Network)} instead.
      */
     public NetworkInfo[] getAllNetworkInfo() {
         try {
@@ -690,6 +754,9 @@
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      *
      * @hide
+     * @deprecated This method does not support multiple connected networks
+     *             of the same type. Use {@link #getAllNetworks} and
+     *             {@link #getNetworkInfo(android.net.Network)} instead.
      */
     public Network getNetworkForType(int networkType) {
         try {
@@ -737,9 +804,9 @@
      * network.
      *
      * @return a {@link NetworkInfo} object for the current default network
-     *        or {@code null} if no network default network is currently active
+     *        or {@code null} if no default network is currently active
      *
-     * <p>This method requires the call to hold the permission
+     * <p>This method requires the caller to hold the permission
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      *
      * {@hide}
@@ -759,7 +826,7 @@
      *        for the current default network, or {@code null} if there
      *        is no current default network.
      *
-     * <p>This method requires the call to hold the permission
+     * <p>This method requires the caller to hold the permission
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      * {@hide}
      */
@@ -779,9 +846,13 @@
      *        for the given networkType, or {@code null} if there is
      *        no current default network.
      *
-     * <p>This method requires the call to hold the permission
+     * <p>This method requires the caller to hold the permission
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      * {@hide}
+     * @deprecated This method does not support multiple connected networks
+     *             of the same type. Use {@link #getAllNetworks},
+     *             {@link #getNetworkInfo(android.net.Network)}, and
+     *             {@link #getLinkProperties(android.net.Network)} instead.
      */
     public LinkProperties getLinkProperties(int networkType) {
         try {
@@ -822,48 +893,6 @@
     }
 
     /**
-     * Tells each network type to set its radio power state as directed.
-     *
-     * @param turnOn a boolean, {@code true} to turn the radios on,
-     *        {@code false} to turn them off.
-     * @return a boolean, {@code true} indicating success.  All network types
-     *        will be tried, even if some fail.
-     *
-     * <p>This method requires the call to hold the permission
-     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
-     * {@hide}
-     */
-// TODO - check for any callers and remove
-//    public boolean setRadios(boolean turnOn) {
-//        try {
-//            return mService.setRadios(turnOn);
-//        } catch (RemoteException e) {
-//            return false;
-//        }
-//    }
-
-    /**
-     * Tells a given networkType to set its radio power state as directed.
-     *
-     * @param networkType the int networkType of interest.
-     * @param turnOn a boolean, {@code true} to turn the radio on,
-     *        {@code} false to turn it off.
-     * @return a boolean, {@code true} indicating success.
-     *
-     * <p>This method requires the call to hold the permission
-     * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
-     * {@hide}
-     */
-// TODO - check for any callers and remove
-//    public boolean setRadio(int networkType, boolean turnOn) {
-//        try {
-//            return mService.setRadio(networkType, turnOn);
-//        } catch (RemoteException e) {
-//            return false;
-//        }
-//    }
-
-    /**
      * Tells the underlying networking system that the caller wants to
      * begin using the named feature. The interpretation of {@code feature}
      * is completely up to each networking implementation.
@@ -1201,7 +1230,7 @@
      * @return {@code true} on success, {@code false} on failure
      *
      * @deprecated Deprecated in favor of the {@link #requestNetwork},
-     *             {@link #setProcessDefaultNetwork} and {@link Network#getSocketFactory} api.
+     *             {@link #bindProcessToNetwork} and {@link Network#getSocketFactory} api.
      */
     public boolean requestRouteToHost(int networkType, int hostAddress) {
         return requestRouteToHostAddress(networkType, NetworkUtils.intToInetAddress(hostAddress));
@@ -1219,7 +1248,7 @@
      * @return {@code true} on success, {@code false} on failure
      * @hide
      * @deprecated Deprecated in favor of the {@link #requestNetwork} and
-     *             {@link #setProcessDefaultNetwork} api.
+     *             {@link #bindProcessToNetwork} api.
      */
     public boolean requestRouteToHostAddress(int networkType, InetAddress hostAddress) {
         try {
@@ -1272,7 +1301,7 @@
      * network is active. Quota status can change rapidly, so these values
      * shouldn't be cached.
      *
-     * <p>This method requires the call to hold the permission
+     * <p>This method requires the caller to hold the permission
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      *
      * @hide
@@ -1344,7 +1373,7 @@
      * listener.
      * <p>
      * If the process default network has been set with
-     * {@link ConnectivityManager#setProcessDefaultNetwork} this function will not
+     * {@link ConnectivityManager#bindProcessToNetwork} this function will not
      * reflect the process's default, but the system default.
      *
      * @param l The listener to be told when the network is active.
@@ -1429,11 +1458,20 @@
      *               situations where a Context pointer is unavailable.
      * @hide
      */
-    public static ConnectivityManager getInstance() {
-        if (sInstance == null) {
+    static ConnectivityManager getInstanceOrNull() {
+        return sInstance;
+    }
+
+    /**
+     * @deprecated - use getSystemService. This is a kludge to support static access in certain
+     *               situations where a Context pointer is unavailable.
+     * @hide
+     */
+    private static ConnectivityManager getInstance() {
+        if (getInstanceOrNull() == null) {
             throw new IllegalStateException("No ConnectivityManager yet constructed");
         }
-        return sInstance;
+        return getInstanceOrNull();
     }
 
     /**
@@ -1442,7 +1480,7 @@
      *
      * @return an array of 0 or more Strings of tetherable interface names.
      *
-     * <p>This method requires the call to hold the permission
+     * <p>This method requires the caller to hold the permission
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      * {@hide}
      */
@@ -1459,7 +1497,7 @@
      *
      * @return an array of 0 or more String of currently tethered interface names.
      *
-     * <p>This method requires the call to hold the permission
+     * <p>This method requires the caller to hold the permission
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      * {@hide}
      */
@@ -1482,7 +1520,7 @@
      * @return an array of 0 or more String indicating the interface names
      *        which failed to tether.
      *
-     * <p>This method requires the call to hold the permission
+     * <p>This method requires the caller to hold the permission
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      * {@hide}
      */
@@ -1520,7 +1558,7 @@
      * @param iface the interface name to tether.
      * @return error a {@code TETHER_ERROR} value indicating success or failure type
      *
-     * <p>This method requires the call to hold the permission
+     * <p>This method requires the caller to hold the permission
      * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
      * {@hide}
      */
@@ -1538,7 +1576,7 @@
      * @param iface the interface name to untether.
      * @return error a {@code TETHER_ERROR} value indicating success or failure type
      *
-     * <p>This method requires the call to hold the permission
+     * <p>This method requires the caller to hold the permission
      * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
      * {@hide}
      */
@@ -1557,7 +1595,7 @@
      *
      * @return a boolean - {@code true} indicating Tethering is supported.
      *
-     * <p>This method requires the call to hold the permission
+     * <p>This method requires the caller to hold the permission
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      * {@hide}
      */
@@ -1577,7 +1615,7 @@
      * @return an array of 0 or more regular expression Strings defining
      *        what interfaces are considered tetherable usb interfaces.
      *
-     * <p>This method requires the call to hold the permission
+     * <p>This method requires the caller to hold the permission
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      * {@hide}
      */
@@ -1597,7 +1635,7 @@
      * @return an array of 0 or more regular expression Strings defining
      *        what interfaces are considered tetherable wifi interfaces.
      *
-     * <p>This method requires the call to hold the permission
+     * <p>This method requires the caller to hold the permission
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      * {@hide}
      */
@@ -1617,7 +1655,7 @@
      * @return an array of 0 or more regular expression Strings defining
      *        what interfaces are considered tetherable bluetooth interfaces.
      *
-     * <p>This method requires the call to hold the permission
+     * <p>This method requires the caller to hold the permission
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      * {@hide}
      */
@@ -1639,7 +1677,7 @@
      * @param enable a boolean - {@code true} to enable tethering
      * @return error a {@code TETHER_ERROR} value indicating success or failure type
      *
-     * <p>This method requires the call to hold the permission
+     * <p>This method requires the caller to hold the permission
      * {@link android.Manifest.permission#CHANGE_NETWORK_STATE}.
      * {@hide}
      */
@@ -1682,7 +1720,7 @@
      * @return error The error code of the last error tethering or untethering the named
      *               interface
      *
-     * <p>This method requires the call to hold the permission
+     * <p>This method requires the caller to hold the permission
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      * {@hide}
      */
@@ -1701,7 +1739,7 @@
      * @param networkType The type of network you want to report on
      * @param percentage The quality of the connection 0 is bad, 100 is good
      *
-     * <p>This method requires the call to hold the permission
+     * <p>This method requires the caller to hold the permission
      * {@link android.Manifest.permission#STATUS_BAR}.
      * {@hide}
      */
@@ -1720,10 +1758,109 @@
      *
      * @param network The {@link Network} the application was attempting to use
      *                or {@code null} to indicate the current default network.
+     * @deprecated Use {@link #reportNetworkConnectivity} which allows reporting both
+     *             working and non-working connectivity.
      */
     public void reportBadNetwork(Network network) {
         try {
-            mService.reportBadNetwork(network);
+            // One of these will be ignored because it matches system's current state.
+            // The other will trigger the necessary reevaluation.
+            mService.reportNetworkConnectivity(network, true);
+            mService.reportNetworkConnectivity(network, false);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Report to the framework whether a network has working connectivity.
+     * This provides a hint to the system that a particular network is providing
+     * working connectivity or not.  In response the framework may re-evaluate
+     * the network's connectivity and might take further action thereafter.
+     *
+     * @param network The {@link Network} the application was attempting to use
+     *                or {@code null} to indicate the current default network.
+     * @param hasConnectivity {@code true} if the application was able to successfully access the
+     *                        Internet using {@code network} or {@code false} if not.
+     */
+    public void reportNetworkConnectivity(Network network, boolean hasConnectivity) {
+        try {
+            mService.reportNetworkConnectivity(network, hasConnectivity);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /** {@hide} */
+    public static final int CAPTIVE_PORTAL_APP_RETURN_DISMISSED    = 0;
+    /** {@hide} */
+    public static final int CAPTIVE_PORTAL_APP_RETURN_UNWANTED     = 1;
+    /** {@hide} */
+    public static final int CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS = 2;
+
+    /**
+     * Called by an app handling the {@link #ACTION_CAPTIVE_PORTAL_SIGN_IN}
+     * action to indicate to the system that the captive portal has been
+     * dismissed.  In response the framework will re-evaluate the network's
+     * connectivity and might take further action thereafter.
+     *
+     * @param network The {@link Network} object passed via
+     *                {@link #EXTRA_NETWORK} with the
+     *                {@link #ACTION_CAPTIVE_PORTAL_SIGN_IN} action.
+     * @param actionToken The {@code String} passed via
+     *                    {@link #EXTRA_CAPTIVE_PORTAL_TOKEN} with the
+     *                    {@code ACTION_CAPTIVE_PORTAL_SIGN_IN} action.
+     */
+    public void reportCaptivePortalDismissed(Network network, String actionToken) {
+        try {
+            mService.captivePortalAppResponse(network, CAPTIVE_PORTAL_APP_RETURN_DISMISSED,
+                    actionToken);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Called by an app handling the {@link #ACTION_CAPTIVE_PORTAL_SIGN_IN}
+     * action to indicate that the user does not want to pursue signing in to
+     * captive portal and the system should continue to prefer other networks
+     * without captive portals for use as the default active data network.  The
+     * system will not retest the network for a captive portal so as to avoid
+     * disturbing the user with further sign in to network notifications.
+     *
+     * @param network The {@link Network} object passed via
+     *                {@link #EXTRA_NETWORK} with the
+     *                {@link #ACTION_CAPTIVE_PORTAL_SIGN_IN} action.
+     * @param actionToken The {@code String} passed via
+     *                    {@link #EXTRA_CAPTIVE_PORTAL_TOKEN} with the
+     *                    {@code ACTION_CAPTIVE_PORTAL_SIGN_IN} action.
+     */
+    public void ignoreNetworkWithCaptivePortal(Network network, String actionToken) {
+        try {
+            mService.captivePortalAppResponse(network, CAPTIVE_PORTAL_APP_RETURN_UNWANTED,
+                    actionToken);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Called by an app handling the {@link #ACTION_CAPTIVE_PORTAL_SIGN_IN}
+     * action to indicate the user wants to use this network as is, even though
+     * the captive portal is still in place.  The system will treat the network
+     * as if it did not have a captive portal when selecting the network to use
+     * as the default active data network. This may result in this network
+     * becoming the default active data network, which could disrupt network
+     * connectivity for apps because the captive portal is still in place.
+     *
+     * @param network The {@link Network} object passed via
+     *                {@link #EXTRA_NETWORK} with the
+     *                {@link #ACTION_CAPTIVE_PORTAL_SIGN_IN} action.
+     * @param actionToken The {@code String} passed via
+     *                    {@link #EXTRA_CAPTIVE_PORTAL_TOKEN} with the
+     *                    {@code ACTION_CAPTIVE_PORTAL_SIGN_IN} action.
+     * @hide
+     */
+    public void useNetworkWithCaptivePortal(Network network, String actionToken) {
+        try {
+            mService.captivePortalAppResponse(network, CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS,
+                    actionToken);
         } catch (RemoteException e) {
         }
     }
@@ -1737,7 +1874,7 @@
      * @param p The a {@link ProxyInfo} object defining the new global
      *        HTTP proxy.  A {@code null} value will clear the global HTTP proxy.
      *
-     * <p>This method requires the call to hold the permission
+     * <p>This method requires the caller to hold the permission
      * android.Manifest.permission#CONNECTIVITY_INTERNAL.
      * @hide
      */
@@ -1754,7 +1891,7 @@
      * @return {@link ProxyInfo} for the current global HTTP proxy or {@code null}
      *        if no global HTTP proxy is set.
      *
-     * <p>This method requires the call to hold the permission
+     * <p>This method requires the caller to hold the permission
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      * @hide
      */
@@ -1769,15 +1906,14 @@
     /**
      * Get the current default HTTP proxy settings.  If a global proxy is set it will be returned,
      * otherwise if this process is bound to a {@link Network} using
-     * {@link #setProcessDefaultNetwork} then that {@code Network}'s proxy is returned, otherwise
+     * {@link #bindProcessToNetwork} then that {@code Network}'s proxy is returned, otherwise
      * the default network's proxy is returned.
      *
      * @return the {@link ProxyInfo} for the current HTTP proxy, or {@code null} if no
      *        HTTP proxy is active.
-     * @hide
      */
     public ProxyInfo getDefaultProxy() {
-        final Network network = getProcessDefaultNetwork();
+        final Network network = getBoundNetworkForProcess();
         if (network != null) {
             final ProxyInfo globalProxy = getGlobalProxy();
             if (globalProxy != null) return globalProxy;
@@ -1793,25 +1929,6 @@
     }
 
     /**
-     * Sets a secondary requirement bit for the given networkType.
-     * This requirement bit is generally under the control of the carrier
-     * or its agents and is not directly controlled by the user.
-     *
-     * @param networkType The network who's dependence has changed
-     * @param met Boolean - true if network use is OK, false if not
-     *
-     * <p>This method requires the call to hold the permission
-     * {@link android.Manifest.permission#CONNECTIVITY_INTERNAL}.
-     * {@hide}
-     */
-    public void setDataDependency(int networkType, boolean met) {
-        try {
-            mService.setDataDependency(networkType, met);
-        } catch (RemoteException e) {
-        }
-    }
-
-    /**
      * Returns true if the hardware supports the given network type
      * else it returns false.  This doesn't indicate we have coverage
      * or are authorized onto a network, just whether or not the
@@ -1822,7 +1939,7 @@
      * @param networkType The network type we'd like to check
      * @return {@code true} if supported, else {@code false}
      *
-     * <p>This method requires the call to hold the permission
+     * <p>This method requires the caller to hold the permission
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      * @hide
      */
@@ -1844,7 +1961,7 @@
      * @return {@code true} if large transfers should be avoided, otherwise
      *        {@code false}.
      *
-     * <p>This method requires the call to hold the permission
+     * <p>This method requires the caller to hold the permission
      * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
      */
     public boolean isActiveNetworkMetered() {
@@ -1880,7 +1997,7 @@
      *        in question.
      * @param isCaptivePortal true/false.
      *
-     * <p>This method requires the call to hold the permission
+     * <p>This method requires the caller to hold the permission
      * {@link android.Manifest.permission#CONNECTIVITY_INTERNAL}.
      * {@hide}
      */
@@ -1892,20 +2009,6 @@
     }
 
     /**
-     * Supply the backend messenger for a network tracker
-     *
-     * @param networkType NetworkType to set
-     * @param messenger {@link Messenger}
-     * {@hide}
-     */
-    public void supplyMessenger(int networkType, Messenger messenger) {
-        try {
-            mService.supplyMessenger(networkType, messenger);
-        } catch (RemoteException e) {
-        }
-    }
-
-    /**
      * Check mobile provisioning.
      *
      * @param suggestedTimeOutMs, timeout in milliseconds
@@ -1955,6 +2058,7 @@
      * @param networkType
      *
      * {@hide}
+     * @deprecated Doesn't properly deal with multiple connected networks of the same type.
      */
     public void setProvisioningNotificationVisible(boolean visible, int networkType,
             String action) {
@@ -1969,7 +2073,7 @@
      *
      * @param enable whether to enable airplane mode or not
      *
-     * <p>This method requires the call to hold the permission
+     * <p>This method requires the caller to hold the permission
      * {@link android.Manifest.permission#CONNECTIVITY_INTERNAL}.
      * @hide
      */
@@ -1994,12 +2098,18 @@
         } catch (RemoteException e) { }
     }
 
-    /** {@hide} */
-    public void registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp,
+    /**
+     * @hide
+     * Register a NetworkAgent with ConnectivityService.
+     * @return NetID corresponding to NetworkAgent.
+     */
+    public int registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp,
             NetworkCapabilities nc, int score, NetworkMisc misc) {
         try {
-            mService.registerNetworkAgent(messenger, ni, lp, nc, score, misc);
-        } catch (RemoteException e) { }
+            return mService.registerNetworkAgent(messenger, ni, lp, nc, score, misc);
+        } catch (RemoteException e) {
+            return NETID_UNSET;
+        }
     }
 
     /**
@@ -2370,9 +2480,8 @@
      * successfully finding a network for the applications request.  Retrieve it with
      * {@link android.content.Intent#getParcelableExtra(String)}.
      * <p>
-     * Note that if you intend to invoke (@link #setProcessDefaultNetwork(Network)) or
-     * {@link Network#openConnection(java.net.URL)} then you must get a
-     * ConnectivityManager instance before doing so.
+     * Note that if you intend to invoke {@link Network#openConnection(java.net.URL)}
+     * then you must get a ConnectivityManager instance before doing so.
      */
     public static final String EXTRA_NETWORK = "android.net.extra.NETWORK";
 
@@ -2461,6 +2570,23 @@
     }
 
     /**
+     * Request connectivityservice to refresh network capabilities for the given
+     * {@link network}. This method returns true if the network is still active, false
+     * otherwise. Notice the method call assumes the caller has registered for
+     * listening NetworkCapabilities updates.
+     *
+     * @param network{@link Network} specifying which network you're interested.
+     * @hide
+     */
+    public boolean requestBwUpdate(Network network) {
+        try {
+            return mService.requestBwUpdate(network);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
      * Unregisters callbacks about and possibly releases networks originating from
      * {@link #requestNetwork} and {@link #registerNetworkCallback} calls.  If the
      * given {@code NetworkCallback} had previously been used with {@code #requestNetwork},
@@ -2491,15 +2617,42 @@
      * Sockets created by Network.getSocketFactory().createSocket() and
      * performing network-specific host name resolutions via
      * {@link Network#getAllByName Network.getAllByName} is preferred to calling
-     * {@code setProcessDefaultNetwork}.
+     * {@code bindProcessToNetwork}.
      *
      * @param network The {@link Network} to bind the current process to, or {@code null} to clear
      *                the current binding.
      * @return {@code true} on success, {@code false} if the {@link Network} is no longer valid.
      */
+    public boolean bindProcessToNetwork(Network network) {
+        // Forcing callers to call thru non-static function ensures ConnectivityManager
+        // instantiated.
+        return setProcessDefaultNetwork(network);
+    }
+
+    /**
+     * Binds the current process to {@code network}.  All Sockets created in the future
+     * (and not explicitly bound via a bound SocketFactory from
+     * {@link Network#getSocketFactory() Network.getSocketFactory()}) will be bound to
+     * {@code network}.  All host name resolutions will be limited to {@code network} as well.
+     * Note that if {@code network} ever disconnects, all Sockets created in this way will cease to
+     * work and all host name resolutions will fail.  This is by design so an application doesn't
+     * accidentally use Sockets it thinks are still bound to a particular {@link Network}.
+     * To clear binding pass {@code null} for {@code network}.  Using individually bound
+     * Sockets created by Network.getSocketFactory().createSocket() and
+     * performing network-specific host name resolutions via
+     * {@link Network#getAllByName Network.getAllByName} is preferred to calling
+     * {@code setProcessDefaultNetwork}.
+     *
+     * @param network The {@link Network} to bind the current process to, or {@code null} to clear
+     *                the current binding.
+     * @return {@code true} on success, {@code false} if the {@link Network} is no longer valid.
+     * @deprecated This function can throw {@link IllegalStateException}.  Use
+     *             {@link #bindProcessToNetwork} instead.  {@code bindProcessToNetwork}
+     *             is a direct replacement.
+     */
     public static boolean setProcessDefaultNetwork(Network network) {
         int netId = (network == null) ? NETID_UNSET : network.netId;
-        if (netId == NetworkUtils.getNetworkBoundToProcess()) {
+        if (netId == NetworkUtils.getBoundNetworkForProcess()) {
             return true;
         }
         if (NetworkUtils.bindProcessToNetwork(netId)) {
@@ -2519,19 +2672,34 @@
 
     /**
      * Returns the {@link Network} currently bound to this process via
-     * {@link #setProcessDefaultNetwork}, or {@code null} if no {@link Network} is explicitly bound.
+     * {@link #bindProcessToNetwork}, or {@code null} if no {@link Network} is explicitly bound.
      *
      * @return {@code Network} to which this process is bound, or {@code null}.
      */
+    public Network getBoundNetworkForProcess() {
+        // Forcing callers to call thru non-static function ensures ConnectivityManager
+        // instantiated.
+        return getProcessDefaultNetwork();
+    }
+
+    /**
+     * Returns the {@link Network} currently bound to this process via
+     * {@link #bindProcessToNetwork}, or {@code null} if no {@link Network} is explicitly bound.
+     *
+     * @return {@code Network} to which this process is bound, or {@code null}.
+     * @deprecated Using this function can lead to other functions throwing
+     *             {@link IllegalStateException}.  Use {@link #getBoundNetworkForProcess} instead.
+     *             {@code getBoundNetworkForProcess} is a direct replacement.
+     */
     public static Network getProcessDefaultNetwork() {
-        int netId = NetworkUtils.getNetworkBoundToProcess();
+        int netId = NetworkUtils.getBoundNetworkForProcess();
         if (netId == NETID_UNSET) return null;
         return new Network(netId);
     }
 
     /**
      * Binds host resolutions performed by this process to {@code network}.
-     * {@link #setProcessDefaultNetwork} takes precedence over this setting.
+     * {@link #bindProcessToNetwork} takes precedence over this setting.
      *
      * @param network The {@link Network} to bind host resolutions from the current process to, or
      *                {@code null} to clear the current binding.
diff --git a/core/java/android/net/DhcpStateMachine.java b/core/java/android/net/DhcpStateMachine.java
index 5151a04..73ef78e 100644
--- a/core/java/android/net/DhcpStateMachine.java
+++ b/core/java/android/net/DhcpStateMachine.java
@@ -37,7 +37,7 @@
  * StateMachine that interacts with the native DHCP client and can talk to
  * a controller that also needs to be a StateMachine
  *
- * The Dhcp state machine provides the following features:
+ * The DhcpStateMachine provides the following features:
  * - Wakeup and renewal using the native DHCP client  (which will not renew
  *   on its own when the device is in suspend state and this can lead to device
  *   holding IP address beyond expiry)
@@ -47,7 +47,7 @@
  *
  * @hide
  */
-public class DhcpStateMachine extends StateMachine {
+public class DhcpStateMachine extends BaseDhcpStateMachine {
 
     private static final String TAG = "DhcpStateMachine";
     private static final boolean DBG = false;
@@ -72,11 +72,6 @@
     //Used for sanity check on setting up renewal
     private static final int MIN_RENEWAL_TIME_SECS = 5 * 60;  // 5 minutes
 
-    private enum DhcpAction {
-        START,
-        RENEW
-    };
-
     private final String mInterfaceName;
     private boolean mRegisteredForPreDhcpNotification = false;
 
@@ -99,6 +94,9 @@
      * after pre DHCP action is complete */
     public static final int CMD_PRE_DHCP_ACTION_COMPLETE    = BASE + 7;
 
+    /* Command from ourselves to see if DHCP results are available */
+    private static final int CMD_GET_DHCP_RESULTS           = BASE + 8;
+
     /* Message.arg1 arguments to CMD_POST_DHCP notification */
     public static final int DHCP_SUCCESS = 1;
     public static final int DHCP_FAILURE = 2;
@@ -108,6 +106,7 @@
     private State mWaitBeforeStartState = new WaitBeforeStartState();
     private State mRunningState = new RunningState();
     private State mWaitBeforeRenewalState = new WaitBeforeRenewalState();
+    private State mPollingState = new PollingState();
 
     private DhcpStateMachine(Context context, StateMachine controller, String intf) {
         super(TAG);
@@ -139,6 +138,7 @@
         addState(mDefaultState);
             addState(mStoppedState, mDefaultState);
             addState(mWaitBeforeStartState, mDefaultState);
+            addState(mPollingState, mDefaultState);
             addState(mRunningState, mDefaultState);
             addState(mWaitBeforeRenewalState, mDefaultState);
 
@@ -161,6 +161,7 @@
      * This is used by Wifi at this time for the purpose of doing BT-Wifi coex
      * handling during Dhcp
      */
+    @Override
     public void registerForPreDhcpNotification() {
         mRegisteredForPreDhcpNotification = true;
     }
@@ -170,6 +171,7 @@
      *
      * @hide
      */
+    @Override
     public void doQuit() {
         quit();
     }
@@ -204,6 +206,10 @@
         @Override
         public void enter() {
             if (DBG) Log.d(TAG, getName() + "\n");
+            if (!NetworkUtils.stopDhcp(mInterfaceName)) {
+                Log.e(TAG, "Failed to stop Dhcp on " + mInterfaceName);
+            }
+            mDhcpResults = null;
         }
 
         @Override
@@ -217,7 +223,7 @@
                         mController.sendMessage(CMD_PRE_DHCP_ACTION);
                         transitionTo(mWaitBeforeStartState);
                     } else {
-                        if (runDhcp(DhcpAction.START)) {
+                        if (runDhcpStart()) {
                             transitionTo(mRunningState);
                         }
                     }
@@ -245,10 +251,10 @@
             if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
             switch (message.what) {
                 case CMD_PRE_DHCP_ACTION_COMPLETE:
-                    if (runDhcp(DhcpAction.START)) {
+                    if (runDhcpStart()) {
                         transitionTo(mRunningState);
                     } else {
-                        transitionTo(mStoppedState);
+                        transitionTo(mPollingState);
                     }
                     break;
                 case CMD_STOP_DHCP:
@@ -265,6 +271,55 @@
         }
     }
 
+    class PollingState extends State {
+        private static final long MAX_DELAY_SECONDS = 32;
+        private long delaySeconds;
+
+        private void scheduleNextResultsCheck() {
+            sendMessageDelayed(obtainMessage(CMD_GET_DHCP_RESULTS), delaySeconds * 1000);
+            delaySeconds *= 2;
+            if (delaySeconds > MAX_DELAY_SECONDS) {
+                delaySeconds = MAX_DELAY_SECONDS;
+            }
+        }
+
+        @Override
+        public void enter() {
+            if (DBG) Log.d(TAG, "Entering " + getName() + "\n");
+            delaySeconds = 1;
+            scheduleNextResultsCheck();
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            boolean retValue = HANDLED;
+            if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
+            switch (message.what) {
+                case CMD_GET_DHCP_RESULTS:
+                    if (DBG) Log.d(TAG, "GET_DHCP_RESULTS on " + mInterfaceName);
+                    if (dhcpSucceeded()) {
+                        transitionTo(mRunningState);
+                    } else {
+                        scheduleNextResultsCheck();
+                    }
+                    break;
+                case CMD_STOP_DHCP:
+                    transitionTo(mStoppedState);
+                    break;
+                default:
+                    retValue = NOT_HANDLED;
+                    break;
+            }
+            return retValue;
+        }
+
+        @Override
+        public void exit() {
+            if (DBG) Log.d(TAG, "Exiting " + getName() + "\n");
+            removeMessages(CMD_GET_DHCP_RESULTS);
+        }
+    }
+
     class RunningState extends State {
         @Override
         public void enter() {
@@ -278,9 +333,6 @@
             switch (message.what) {
                 case CMD_STOP_DHCP:
                     mAlarmManager.cancel(mDhcpRenewalIntent);
-                    if (!NetworkUtils.stopDhcp(mInterfaceName)) {
-                        Log.e(TAG, "Failed to stop Dhcp on " + mInterfaceName);
-                    }
                     transitionTo(mStoppedState);
                     break;
                 case CMD_RENEW_DHCP:
@@ -290,7 +342,7 @@
                         transitionTo(mWaitBeforeRenewalState);
                         //mDhcpRenewWakeLock is released in WaitBeforeRenewalState
                     } else {
-                        if (!runDhcp(DhcpAction.RENEW)) {
+                        if (!runDhcpRenew()) {
                             transitionTo(mStoppedState);
                         }
                         mDhcpRenewWakeLock.release();
@@ -319,13 +371,10 @@
             switch (message.what) {
                 case CMD_STOP_DHCP:
                     mAlarmManager.cancel(mDhcpRenewalIntent);
-                    if (!NetworkUtils.stopDhcp(mInterfaceName)) {
-                        Log.e(TAG, "Failed to stop Dhcp on " + mInterfaceName);
-                    }
                     transitionTo(mStoppedState);
                     break;
                 case CMD_PRE_DHCP_ACTION_COMPLETE:
-                    if (runDhcp(DhcpAction.RENEW)) {
+                    if (runDhcpRenew()) {
                        transitionTo(mRunningState);
                     } else {
                        transitionTo(mStoppedState);
@@ -346,52 +395,68 @@
         }
     }
 
-    private boolean runDhcp(DhcpAction dhcpAction) {
-        boolean success = false;
+    private boolean dhcpSucceeded() {
         DhcpResults dhcpResults = new DhcpResults();
-
-        if (dhcpAction == DhcpAction.START) {
-            /* Stop any existing DHCP daemon before starting new */
-            NetworkUtils.stopDhcp(mInterfaceName);
-            if (DBG) Log.d(TAG, "DHCP request on " + mInterfaceName);
-            success = NetworkUtils.runDhcp(mInterfaceName, dhcpResults);
-        } else if (dhcpAction == DhcpAction.RENEW) {
-            if (DBG) Log.d(TAG, "DHCP renewal on " + mInterfaceName);
-            success = NetworkUtils.runDhcpRenew(mInterfaceName, dhcpResults);
-            if (success) dhcpResults.updateFromDhcpRequest(mDhcpResults);
+        if (!NetworkUtils.getDhcpResults(mInterfaceName, dhcpResults)) {
+            return false;
         }
-        if (success) {
-            if (DBG) Log.d(TAG, "DHCP succeeded on " + mInterfaceName);
-            long leaseDuration = dhcpResults.leaseDuration; //int to long conversion
 
-            //Sanity check for renewal
-            if (leaseDuration >= 0) {
-                //TODO: would be good to notify the user that his network configuration is
-                //bad and that the device cannot renew below MIN_RENEWAL_TIME_SECS
-                if (leaseDuration < MIN_RENEWAL_TIME_SECS) {
-                    leaseDuration = MIN_RENEWAL_TIME_SECS;
-                }
-                //Do it a bit earlier than half the lease duration time
-                //to beat the native DHCP client and avoid extra packets
-                //48% for one hour lease time = 29 minutes
-                mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
-                        SystemClock.elapsedRealtime() +
-                        leaseDuration * 480, //in milliseconds
-                        mDhcpRenewalIntent);
-            } else {
-                //infinite lease time, no renewal needed
+        if (DBG) Log.d(TAG, "DHCP results found for " + mInterfaceName);
+        long leaseDuration = dhcpResults.leaseDuration; //int to long conversion
+
+        //Sanity check for renewal
+        if (leaseDuration >= 0) {
+            //TODO: would be good to notify the user that his network configuration is
+            //bad and that the device cannot renew below MIN_RENEWAL_TIME_SECS
+            if (leaseDuration < MIN_RENEWAL_TIME_SECS) {
+                leaseDuration = MIN_RENEWAL_TIME_SECS;
             }
-
-            mDhcpResults = dhcpResults;
-            mController.obtainMessage(CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, dhcpResults)
-                .sendToTarget();
+            //Do it a bit earlier than half the lease duration time
+            //to beat the native DHCP client and avoid extra packets
+            //48% for one hour lease time = 29 minutes
+            mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+                    SystemClock.elapsedRealtime() +
+                    leaseDuration * 480, //in milliseconds
+                    mDhcpRenewalIntent);
         } else {
-            Log.e(TAG, "DHCP failed on " + mInterfaceName + ": " +
-                    NetworkUtils.getDhcpError());
-            NetworkUtils.stopDhcp(mInterfaceName);
-            mController.obtainMessage(CMD_POST_DHCP_ACTION, DHCP_FAILURE, 0)
-                .sendToTarget();
+            //infinite lease time, no renewal needed
         }
-        return success;
+
+        // Fill in any missing fields in dhcpResults from the previous results.
+        // If mDhcpResults is null (i.e. this is the first server response),
+        // this is a noop.
+        dhcpResults.updateFromDhcpRequest(mDhcpResults);
+        mDhcpResults = dhcpResults;
+        mController.obtainMessage(CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, dhcpResults)
+            .sendToTarget();
+        return true;
+    }
+
+    private boolean runDhcpStart() {
+        /* Stop any existing DHCP daemon before starting new */
+        NetworkUtils.stopDhcp(mInterfaceName);
+        mDhcpResults = null;
+
+        if (DBG) Log.d(TAG, "DHCP request on " + mInterfaceName);
+        if (!NetworkUtils.startDhcp(mInterfaceName) || !dhcpSucceeded()) {
+            Log.e(TAG, "DHCP request failed on " + mInterfaceName + ": " +
+                    NetworkUtils.getDhcpError());
+            mController.obtainMessage(CMD_POST_DHCP_ACTION, DHCP_FAILURE, 0)
+                    .sendToTarget();
+            return false;
+        }
+        return true;
+    }
+
+    private boolean runDhcpRenew() {
+        if (DBG) Log.d(TAG, "DHCP renewal on " + mInterfaceName);
+        if (!NetworkUtils.startDhcpRenew(mInterfaceName) || !dhcpSucceeded()) {
+            Log.e(TAG, "DHCP renew failed on " + mInterfaceName + ": " +
+                    NetworkUtils.getDhcpError());
+            mController.obtainMessage(CMD_POST_DHCP_ACTION, DHCP_FAILURE, 0)
+                    .sendToTarget();
+            return false;
+        }
+        return true;
     }
 }
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 46af112..6e06aa5 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -42,6 +42,7 @@
 /** {@hide} */
 interface IConnectivityManager
 {
+    Network getActiveNetwork();
     NetworkInfo getActiveNetworkInfo();
     NetworkInfo getActiveNetworkInfoForUid(int uid);
     NetworkInfo getNetworkInfo(int networkType);
@@ -94,7 +95,9 @@
 
     void reportInetCondition(int networkType, int percentage);
 
-    void reportBadNetwork(in Network network);
+    void reportNetworkConnectivity(in Network network, boolean hasConnectivity);
+
+    void captivePortalAppResponse(in Network network, int response, String actionToken);
 
     ProxyInfo getGlobalProxy();
 
@@ -102,8 +105,6 @@
 
     ProxyInfo getDefaultProxy();
 
-    void setDataDependency(int networkType, boolean met);
-
     boolean prepareVpn(String oldPackage, String newPackage);
 
     void setVpnPackageAuthorization(boolean authorized);
@@ -120,10 +121,6 @@
 
     void captivePortalCheckCompleted(in NetworkInfo info, boolean isCaptivePortal);
 
-    void supplyMessenger(int networkType, in Messenger messenger);
-
-    int findConnectionTypeForIface(in String iface);
-
     int checkMobileProvisioning(int suggestedTimeOutMs);
 
     String getMobileProvisioningUrl();
@@ -136,9 +133,11 @@
 
     void registerNetworkFactory(in Messenger messenger, in String name);
 
+    boolean requestBwUpdate(in Network network);
+
     void unregisterNetworkFactory(in Messenger messenger);
 
-    void registerNetworkAgent(in Messenger messenger, in NetworkInfo ni, in LinkProperties lp,
+    int registerNetworkAgent(in Messenger messenger, in NetworkInfo ni, in LinkProperties lp,
             in NetworkCapabilities nc, int score, in NetworkMisc misc);
 
     NetworkRequest requestNetwork(in NetworkCapabilities networkCapabilities,
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
deleted file mode 100644
index abbb7b8..0000000
--- a/core/java/android/net/MobileDataStateTracker.java
+++ /dev/null
@@ -1,910 +0,0 @@
-/*
- * Copyright (C) 2008 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.net;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.net.NetworkInfo.DetailedState;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.telephony.PhoneStateListener;
-import android.telephony.SignalStrength;
-import android.telephony.TelephonyManager;
-import android.text.TextUtils;
-import android.util.Slog;
-
-import com.android.internal.telephony.DctConstants;
-import com.android.internal.telephony.ITelephony;
-import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.util.AsyncChannel;
-
-import java.io.CharArrayWriter;
-import java.io.PrintWriter;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * Track the state of mobile data connectivity. This is done by
- * receiving broadcast intents from the Phone process whenever
- * the state of data connectivity changes.
- *
- * {@hide}
- */
-public class MobileDataStateTracker extends BaseNetworkStateTracker {
-
-    private static final String TAG = "MobileDataStateTracker";
-    private static final boolean DBG = false;
-    private static final boolean VDBG = false;
-
-    private PhoneConstants.DataState mMobileDataState;
-    private ITelephony mPhoneService;
-
-    private String mApnType;
-    private NetworkInfo mNetworkInfo;
-    private boolean mTeardownRequested = false;
-    private Handler mTarget;
-    private Context mContext;
-    private LinkProperties mLinkProperties;
-    private boolean mPrivateDnsRouteSet = false;
-    private boolean mDefaultRouteSet = false;
-
-    // NOTE: these are only kept for debugging output; actual values are
-    // maintained in DataConnectionTracker.
-    protected boolean mUserDataEnabled = true;
-    protected boolean mPolicyDataEnabled = true;
-
-    private Handler mHandler;
-    private AsyncChannel mDataConnectionTrackerAc;
-
-    private AtomicBoolean mIsCaptivePortal = new AtomicBoolean(false);
-
-    private SignalStrength mSignalStrength;
-
-    private SamplingDataTracker mSamplingDataTracker = new SamplingDataTracker();
-
-    private static final int UNKNOWN = LinkQualityInfo.UNKNOWN_INT;
-
-    /**
-     * Create a new MobileDataStateTracker
-     * @param netType the ConnectivityManager network type
-     * @param tag the name of this network
-     */
-    public MobileDataStateTracker(int netType, String tag) {
-        mNetworkInfo = new NetworkInfo(netType,
-                TelephonyManager.getDefault().getNetworkType(), tag,
-                TelephonyManager.getDefault().getNetworkTypeName());
-        mApnType = networkTypeToApnType(netType);
-    }
-
-    /**
-     * Begin monitoring data connectivity.
-     *
-     * @param context is the current Android context
-     * @param target is the Hander to which to return the events.
-     */
-    public void startMonitoring(Context context, Handler target) {
-        mTarget = target;
-        mContext = context;
-
-        mHandler = new MdstHandler(target.getLooper(), this);
-
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
-        filter.addAction(TelephonyIntents.ACTION_DATA_CONNECTION_CONNECTED_TO_PROVISIONING_APN);
-        filter.addAction(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED);
-
-        mContext.registerReceiver(new MobileDataStateReceiver(), filter);
-        mMobileDataState = PhoneConstants.DataState.DISCONNECTED;
-
-        TelephonyManager tm = (TelephonyManager)mContext.getSystemService(
-                Context.TELEPHONY_SERVICE);
-        tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
-    }
-
-    private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
-        @Override
-        public void onSignalStrengthsChanged(SignalStrength signalStrength) {
-            mSignalStrength = signalStrength;
-        }
-    };
-
-    static class MdstHandler extends Handler {
-        private MobileDataStateTracker mMdst;
-
-        MdstHandler(Looper looper, MobileDataStateTracker mdst) {
-            super(looper);
-            mMdst = mdst;
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
-                    if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
-                        if (VDBG) {
-                            mMdst.log("MdstHandler connected");
-                        }
-                        mMdst.mDataConnectionTrackerAc = (AsyncChannel) msg.obj;
-                    } else {
-                        if (VDBG) {
-                            mMdst.log("MdstHandler %s NOT connected error=" + msg.arg1);
-                        }
-                    }
-                    break;
-                case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
-                    if (VDBG) mMdst.log("Disconnected from DataStateTracker");
-                    mMdst.mDataConnectionTrackerAc = null;
-                    break;
-                default: {
-                    if (VDBG) mMdst.log("Ignorning unknown message=" + msg);
-                    break;
-                }
-            }
-        }
-    }
-
-    public boolean isPrivateDnsRouteSet() {
-        return mPrivateDnsRouteSet;
-    }
-
-    public void privateDnsRouteSet(boolean enabled) {
-        mPrivateDnsRouteSet = enabled;
-    }
-
-    public NetworkInfo getNetworkInfo() {
-        return mNetworkInfo;
-    }
-
-    public boolean isDefaultRouteSet() {
-        return mDefaultRouteSet;
-    }
-
-    public void defaultRouteSet(boolean enabled) {
-        mDefaultRouteSet = enabled;
-    }
-
-    /**
-     * This is not implemented.
-     */
-    public void releaseWakeLock() {
-    }
-
-    private void updateLinkProperitesAndCapatilities(Intent intent) {
-        mLinkProperties = intent.getParcelableExtra(
-                PhoneConstants.DATA_LINK_PROPERTIES_KEY);
-        if (mLinkProperties == null) {
-            loge("CONNECTED event did not supply link properties.");
-            mLinkProperties = new LinkProperties();
-        }
-        mLinkProperties.setMtu(mContext.getResources().getInteger(
-                com.android.internal.R.integer.config_mobile_mtu));
-        mNetworkCapabilities = intent.getParcelableExtra(
-                PhoneConstants.DATA_NETWORK_CAPABILITIES_KEY);
-        if (mNetworkCapabilities == null) {
-            loge("CONNECTED event did not supply network capabilities.");
-            mNetworkCapabilities = new NetworkCapabilities();
-        }
-    }
-
-    private class MobileDataStateReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (intent.getAction().equals(TelephonyIntents.
-                    ACTION_DATA_CONNECTION_CONNECTED_TO_PROVISIONING_APN)) {
-                String apnName = intent.getStringExtra(PhoneConstants.DATA_APN_KEY);
-                String apnType = intent.getStringExtra(PhoneConstants.DATA_APN_TYPE_KEY);
-                if (!TextUtils.equals(mApnType, apnType)) {
-                    return;
-                }
-                if (DBG) {
-                    log("Broadcast received: " + intent.getAction() + " apnType=" + apnType
-                            + " apnName=" + apnName);
-                }
-
-                // Make us in the connecting state until we make a new TYPE_MOBILE_PROVISIONING
-                mMobileDataState = PhoneConstants.DataState.CONNECTING;
-                updateLinkProperitesAndCapatilities(intent);
-                mNetworkInfo.setIsConnectedToProvisioningNetwork(true);
-
-                // Change state to SUSPENDED so setDetailedState
-                // sends EVENT_STATE_CHANGED to connectivityService
-                setDetailedState(DetailedState.SUSPENDED, "", apnName);
-            } else if (intent.getAction().equals(TelephonyIntents.
-                    ACTION_ANY_DATA_CONNECTION_STATE_CHANGED)) {
-                String apnType = intent.getStringExtra(PhoneConstants.DATA_APN_TYPE_KEY);
-                if (VDBG) {
-                    log(String.format("Broadcast received: ACTION_ANY_DATA_CONNECTION_STATE_CHANGED"
-                        + "mApnType=%s %s received apnType=%s", mApnType,
-                        TextUtils.equals(apnType, mApnType) ? "==" : "!=", apnType));
-                }
-                if (!TextUtils.equals(apnType, mApnType)) {
-                    return;
-                }
-                // Assume this isn't a provisioning network.
-                mNetworkInfo.setIsConnectedToProvisioningNetwork(false);
-                if (DBG) {
-                    log("Broadcast received: " + intent.getAction() + " apnType=" + apnType);
-                }
-
-                int oldSubtype = mNetworkInfo.getSubtype();
-                int newSubType = TelephonyManager.getDefault().getNetworkType();
-                String subTypeName = TelephonyManager.getDefault().getNetworkTypeName();
-                mNetworkInfo.setSubtype(newSubType, subTypeName);
-                if (newSubType != oldSubtype && mNetworkInfo.isConnected()) {
-                    Message msg = mTarget.obtainMessage(EVENT_NETWORK_SUBTYPE_CHANGED,
-                                                        oldSubtype, 0, mNetworkInfo);
-                    msg.sendToTarget();
-                }
-
-                PhoneConstants.DataState state = Enum.valueOf(PhoneConstants.DataState.class,
-                        intent.getStringExtra(PhoneConstants.STATE_KEY));
-                String reason = intent.getStringExtra(PhoneConstants.STATE_CHANGE_REASON_KEY);
-                String apnName = intent.getStringExtra(PhoneConstants.DATA_APN_KEY);
-                mNetworkInfo.setRoaming(intent.getBooleanExtra(
-                        PhoneConstants.DATA_NETWORK_ROAMING_KEY, false));
-                if (VDBG) {
-                    log(mApnType + " setting isAvailable to " +
-                            intent.getBooleanExtra(PhoneConstants.NETWORK_UNAVAILABLE_KEY,false));
-                }
-                mNetworkInfo.setIsAvailable(!intent.getBooleanExtra(
-                        PhoneConstants.NETWORK_UNAVAILABLE_KEY, false));
-
-                if (DBG) {
-                    log("Received state=" + state + ", old=" + mMobileDataState +
-                        ", reason=" + (reason == null ? "(unspecified)" : reason));
-                }
-                if (mMobileDataState != state) {
-                    mMobileDataState = state;
-                    switch (state) {
-                        case DISCONNECTED:
-                            if(isTeardownRequested()) {
-                                setTeardownRequested(false);
-                            }
-
-                            setDetailedState(DetailedState.DISCONNECTED, reason, apnName);
-                            // can't do this here - ConnectivityService needs it to clear stuff
-                            // it's ok though - just leave it to be refreshed next time
-                            // we connect.
-                            //if (DBG) log("clearing mInterfaceName for "+ mApnType +
-                            //        " as it DISCONNECTED");
-                            //mInterfaceName = null;
-                            break;
-                        case CONNECTING:
-                            setDetailedState(DetailedState.CONNECTING, reason, apnName);
-                            break;
-                        case SUSPENDED:
-                            setDetailedState(DetailedState.SUSPENDED, reason, apnName);
-                            break;
-                        case CONNECTED:
-                            updateLinkProperitesAndCapatilities(intent);
-                            setDetailedState(DetailedState.CONNECTED, reason, apnName);
-                            break;
-                    }
-
-                    if (VDBG) {
-                        Slog.d(TAG, "TelephonyMgr.DataConnectionStateChanged");
-                        if (mNetworkInfo != null) {
-                            Slog.d(TAG, "NetworkInfo = " + mNetworkInfo);
-                            Slog.d(TAG, "subType = " + mNetworkInfo.getSubtype());
-                            Slog.d(TAG, "subType = " + mNetworkInfo.getSubtypeName());
-                        }
-                        if (mLinkProperties != null) {
-                            Slog.d(TAG, "LinkProperties = " + mLinkProperties);
-                        } else {
-                            Slog.d(TAG, "LinkProperties = " );
-                        }
-
-                        if (mNetworkCapabilities != null) {
-                            Slog.d(TAG, mNetworkCapabilities.toString());
-                        } else {
-                            Slog.d(TAG, "NetworkCapabilities = " );
-                        }
-                    }
-
-
-                    /* lets not sample traffic data across state changes */
-                    mSamplingDataTracker.resetSamplingData();
-                } else {
-                    // There was no state change. Check if LinkProperties has been updated.
-                    if (TextUtils.equals(reason, PhoneConstants.REASON_LINK_PROPERTIES_CHANGED)) {
-                        mLinkProperties = intent.getParcelableExtra(
-                                PhoneConstants.DATA_LINK_PROPERTIES_KEY);
-                        if (mLinkProperties == null) {
-                            loge("No link property in LINK_PROPERTIES change event.");
-                            mLinkProperties = new LinkProperties();
-                        }
-                        // Just update reason field in this NetworkInfo
-                        mNetworkInfo.setDetailedState(mNetworkInfo.getDetailedState(), reason,
-                                                      mNetworkInfo.getExtraInfo());
-                        Message msg = mTarget.obtainMessage(EVENT_CONFIGURATION_CHANGED,
-                                                            mNetworkInfo);
-                        msg.sendToTarget();
-                    }
-                }
-            } else if (intent.getAction().
-                    equals(TelephonyIntents.ACTION_DATA_CONNECTION_FAILED)) {
-                String apnType = intent.getStringExtra(PhoneConstants.DATA_APN_TYPE_KEY);
-                if (!TextUtils.equals(apnType, mApnType)) {
-                    if (DBG) {
-                        log(String.format(
-                                "Broadcast received: ACTION_ANY_DATA_CONNECTION_FAILED ignore, " +
-                                "mApnType=%s != received apnType=%s", mApnType, apnType));
-                    }
-                    return;
-                }
-                // Assume this isn't a provisioning network.
-                mNetworkInfo.setIsConnectedToProvisioningNetwork(false);
-                String reason = intent.getStringExtra(PhoneConstants.FAILURE_REASON_KEY);
-                String apnName = intent.getStringExtra(PhoneConstants.DATA_APN_KEY);
-                if (DBG) {
-                    log("Broadcast received: " + intent.getAction() +
-                                " reason=" + reason == null ? "null" : reason);
-                }
-                setDetailedState(DetailedState.FAILED, reason, apnName);
-            } else {
-                if (DBG) log("Broadcast received: ignore " + intent.getAction());
-            }
-        }
-    }
-
-    private void getPhoneService(boolean forceRefresh) {
-        if ((mPhoneService == null) || forceRefresh) {
-            mPhoneService = ITelephony.Stub.asInterface(ServiceManager.getService("phone"));
-        }
-    }
-
-    /**
-     * Report whether data connectivity is possible.
-     */
-    public boolean isAvailable() {
-        return mNetworkInfo.isAvailable();
-    }
-
-    /**
-     * Return the system properties name associated with the tcp buffer sizes
-     * for this network.
-     */
-    public String getTcpBufferSizesPropName() {
-        String networkTypeStr = "unknown";
-        TelephonyManager tm = new TelephonyManager(mContext);
-        //TODO We have to edit the parameter for getNetworkType regarding CDMA
-        switch(tm.getNetworkType()) {
-        case TelephonyManager.NETWORK_TYPE_GPRS:
-            networkTypeStr = "gprs";
-            break;
-        case TelephonyManager.NETWORK_TYPE_EDGE:
-            networkTypeStr = "edge";
-            break;
-        case TelephonyManager.NETWORK_TYPE_UMTS:
-            networkTypeStr = "umts";
-            break;
-        case TelephonyManager.NETWORK_TYPE_HSDPA:
-            networkTypeStr = "hsdpa";
-            break;
-        case TelephonyManager.NETWORK_TYPE_HSUPA:
-            networkTypeStr = "hsupa";
-            break;
-        case TelephonyManager.NETWORK_TYPE_HSPA:
-            networkTypeStr = "hspa";
-            break;
-        case TelephonyManager.NETWORK_TYPE_HSPAP:
-            networkTypeStr = "hspap";
-            break;
-        case TelephonyManager.NETWORK_TYPE_CDMA:
-            networkTypeStr = "cdma";
-            break;
-        case TelephonyManager.NETWORK_TYPE_1xRTT:
-            networkTypeStr = "1xrtt";
-            break;
-        case TelephonyManager.NETWORK_TYPE_EVDO_0:
-            networkTypeStr = "evdo";
-            break;
-        case TelephonyManager.NETWORK_TYPE_EVDO_A:
-            networkTypeStr = "evdo";
-            break;
-        case TelephonyManager.NETWORK_TYPE_EVDO_B:
-            networkTypeStr = "evdo";
-            break;
-        case TelephonyManager.NETWORK_TYPE_IDEN:
-            networkTypeStr = "iden";
-            break;
-        case TelephonyManager.NETWORK_TYPE_LTE:
-        case TelephonyManager.NETWORK_TYPE_IWLAN:
-            networkTypeStr = "lte";
-            break;
-        case TelephonyManager.NETWORK_TYPE_EHRPD:
-            networkTypeStr = "ehrpd";
-            break;
-        default:
-            loge("unknown network type: " + tm.getNetworkType());
-        }
-        return "net.tcp.buffersize." + networkTypeStr;
-    }
-
-    /**
-     * Tear down mobile data connectivity, i.e., disable the ability to create
-     * mobile data connections.
-     * TODO - make async and return nothing?
-     */
-    public boolean teardown() {
-        setTeardownRequested(true);
-        return (setEnableApn(mApnType, false) != PhoneConstants.APN_REQUEST_FAILED);
-    }
-
-    /**
-     * @return true if this is ready to operate
-     */
-    public boolean isReady() {
-        return mDataConnectionTrackerAc != null;
-    }
-
-    @Override
-    public void captivePortalCheckCompleted(boolean isCaptivePortal) {
-        if (mIsCaptivePortal.getAndSet(isCaptivePortal) != isCaptivePortal) {
-            // Captive portal change enable/disable failing fast
-            setEnableFailFastMobileData(
-                    isCaptivePortal ? DctConstants.ENABLED : DctConstants.DISABLED);
-        }
-    }
-
-    /**
-     * Record the detailed state of a network, and if it is a
-     * change from the previous state, send a notification to
-     * any listeners.
-     * @param state the new {@code DetailedState}
-     * @param reason a {@code String} indicating a reason for the state change,
-     * if one was supplied. May be {@code null}.
-     * @param extraInfo optional {@code String} providing extra information about the state change
-     */
-    private void setDetailedState(NetworkInfo.DetailedState state, String reason,
-            String extraInfo) {
-        if (DBG) log("setDetailed state, old ="
-                + mNetworkInfo.getDetailedState() + " and new state=" + state);
-        if (state != mNetworkInfo.getDetailedState()) {
-            boolean wasConnecting = (mNetworkInfo.getState() == NetworkInfo.State.CONNECTING);
-            String lastReason = mNetworkInfo.getReason();
-            /*
-             * If a reason was supplied when the CONNECTING state was entered, and no
-             * reason was supplied for entering the CONNECTED state, then retain the
-             * reason that was supplied when going to CONNECTING.
-             */
-            if (wasConnecting && state == NetworkInfo.DetailedState.CONNECTED && reason == null
-                    && lastReason != null)
-                reason = lastReason;
-            mNetworkInfo.setDetailedState(state, reason, extraInfo);
-            Message msg = mTarget.obtainMessage(EVENT_STATE_CHANGED, new NetworkInfo(mNetworkInfo));
-            msg.sendToTarget();
-        }
-    }
-
-    public void setTeardownRequested(boolean isRequested) {
-        mTeardownRequested = isRequested;
-    }
-
-    public boolean isTeardownRequested() {
-        return mTeardownRequested;
-    }
-
-    /**
-     * Re-enable mobile data connectivity after a {@link #teardown()}.
-     * TODO - make async and always get a notification?
-     */
-    public boolean reconnect() {
-        boolean retValue = false; //connected or expect to be?
-        setTeardownRequested(false);
-        switch (setEnableApn(mApnType, true)) {
-            case PhoneConstants.APN_ALREADY_ACTIVE:
-                // need to set self to CONNECTING so the below message is handled.
-                retValue = true;
-                break;
-            case PhoneConstants.APN_REQUEST_STARTED:
-                // set IDLE here , avoid the following second FAILED not sent out
-                mNetworkInfo.setDetailedState(DetailedState.IDLE, null, null);
-                retValue = true;
-                break;
-            case PhoneConstants.APN_REQUEST_FAILED:
-            case PhoneConstants.APN_TYPE_NOT_AVAILABLE:
-                break;
-            default:
-                loge("Error in reconnect - unexpected response.");
-                break;
-        }
-        return retValue;
-    }
-
-    /**
-     * Turn on or off the mobile radio. No connectivity will be possible while the
-     * radio is off. The operation is a no-op if the radio is already in the desired state.
-     * @param turnOn {@code true} if the radio should be turned on, {@code false} if
-     */
-    public boolean setRadio(boolean turnOn) {
-        getPhoneService(false);
-        /*
-         * If the phone process has crashed in the past, we'll get a
-         * RemoteException and need to re-reference the service.
-         */
-        for (int retry = 0; retry < 2; retry++) {
-            if (mPhoneService == null) {
-                loge("Ignoring mobile radio request because could not acquire PhoneService");
-                break;
-            }
-
-            try {
-                return mPhoneService.setRadio(turnOn);
-            } catch (RemoteException e) {
-                if (retry == 0) getPhoneService(true);
-            }
-        }
-
-        loge("Could not set radio power to " + (turnOn ? "on" : "off"));
-        return false;
-    }
-
-
-    public void setInternalDataEnable(boolean enabled) {
-        if (DBG) log("setInternalDataEnable: E enabled=" + enabled);
-        final AsyncChannel channel = mDataConnectionTrackerAc;
-        if (channel != null) {
-            channel.sendMessage(DctConstants.EVENT_SET_INTERNAL_DATA_ENABLE,
-                    enabled ? DctConstants.ENABLED : DctConstants.DISABLED);
-        }
-        if (VDBG) log("setInternalDataEnable: X enabled=" + enabled);
-    }
-
-    @Override
-    public void setUserDataEnable(boolean enabled) {
-        if (DBG) log("setUserDataEnable: E enabled=" + enabled);
-        final AsyncChannel channel = mDataConnectionTrackerAc;
-        if (channel != null) {
-            channel.sendMessage(DctConstants.CMD_SET_USER_DATA_ENABLE,
-                    enabled ? DctConstants.ENABLED : DctConstants.DISABLED);
-            mUserDataEnabled = enabled;
-        }
-        if (VDBG) log("setUserDataEnable: X enabled=" + enabled);
-    }
-
-    @Override
-    public void setPolicyDataEnable(boolean enabled) {
-        if (DBG) log("setPolicyDataEnable(enabled=" + enabled + ")");
-        final AsyncChannel channel = mDataConnectionTrackerAc;
-        if (channel != null) {
-            channel.sendMessage(DctConstants.CMD_SET_POLICY_DATA_ENABLE,
-                    enabled ? DctConstants.ENABLED : DctConstants.DISABLED);
-            mPolicyDataEnabled = enabled;
-        }
-    }
-
-    /**
-     * Eanble/disable FailFast
-     *
-     * @param enabled is DctConstants.ENABLED/DISABLED
-     */
-    public void setEnableFailFastMobileData(int enabled) {
-        if (DBG) log("setEnableFailFastMobileData(enabled=" + enabled + ")");
-        final AsyncChannel channel = mDataConnectionTrackerAc;
-        if (channel != null) {
-            channel.sendMessage(DctConstants.CMD_SET_ENABLE_FAIL_FAST_MOBILE_DATA, enabled);
-        }
-    }
-
-    /**
-     * carrier dependency is met/unmet
-     * @param met
-     */
-    public void setDependencyMet(boolean met) {
-        Bundle bundle = Bundle.forPair(DctConstants.APN_TYPE_KEY, mApnType);
-        try {
-            if (DBG) log("setDependencyMet: E met=" + met);
-            Message msg = Message.obtain();
-            msg.what = DctConstants.CMD_SET_DEPENDENCY_MET;
-            msg.arg1 = (met ? DctConstants.ENABLED : DctConstants.DISABLED);
-            msg.setData(bundle);
-            mDataConnectionTrackerAc.sendMessage(msg);
-            if (VDBG) log("setDependencyMet: X met=" + met);
-        } catch (NullPointerException e) {
-            loge("setDependencyMet: X mAc was null" + e);
-        }
-    }
-
-    /**
-     *  Inform DCT mobile provisioning has started, it ends when provisioning completes.
-     */
-    public void enableMobileProvisioning(String url) {
-        if (DBG) log("enableMobileProvisioning(url=" + url + ")");
-        final AsyncChannel channel = mDataConnectionTrackerAc;
-        if (channel != null) {
-            Message msg = Message.obtain();
-            msg.what = DctConstants.CMD_ENABLE_MOBILE_PROVISIONING;
-            msg.setData(Bundle.forPair(DctConstants.PROVISIONING_URL_KEY, url));
-            channel.sendMessage(msg);
-        }
-    }
-
-    /**
-     * Return if this network is the provisioning network. Valid only if connected.
-     * @param met
-     */
-    public boolean isProvisioningNetwork() {
-        boolean retVal;
-        try {
-            Message msg = Message.obtain();
-            msg.what = DctConstants.CMD_IS_PROVISIONING_APN;
-            msg.setData(Bundle.forPair(DctConstants.APN_TYPE_KEY, mApnType));
-            Message result = mDataConnectionTrackerAc.sendMessageSynchronously(msg);
-            retVal = result.arg1 == DctConstants.ENABLED;
-        } catch (NullPointerException e) {
-            loge("isProvisioningNetwork: X " + e);
-            retVal = false;
-        }
-        if (DBG) log("isProvisioningNetwork: retVal=" + retVal);
-        return retVal;
-    }
-
-    @Override
-    public String toString() {
-        final CharArrayWriter writer = new CharArrayWriter();
-        final PrintWriter pw = new PrintWriter(writer);
-        pw.print("Mobile data state: "); pw.println(mMobileDataState);
-        pw.print("Data enabled: user="); pw.print(mUserDataEnabled);
-        pw.print(", policy="); pw.println(mPolicyDataEnabled);
-        return writer.toString();
-    }
-
-    /**
-     * Internal method supporting the ENABLE_MMS feature.
-     * @param apnType the type of APN to be enabled or disabled (e.g., mms)
-     * @param enable {@code true} to enable the specified APN type,
-     * {@code false} to disable it.
-     * @return an integer value representing the outcome of the request.
-     */
-    private int setEnableApn(String apnType, boolean enable) {
-        getPhoneService(false);
-        /*
-         * If the phone process has crashed in the past, we'll get a
-         * RemoteException and need to re-reference the service.
-         */
-        for (int retry = 0; retry < 2; retry++) {
-            if (mPhoneService == null) {
-                loge("Ignoring feature request because could not acquire PhoneService");
-                break;
-            }
-
-//            try {
-//                if (enable) {
-//                    return mPhoneService.enableApnType(apnType);
-//                } else {
-//                    return mPhoneService.disableApnType(apnType);
-//                }
-//            } catch (RemoteException e) {
-//                if (retry == 0) getPhoneService(true);
-//            }
-        }
-
-        loge("Could not " + (enable ? "enable" : "disable") + " APN type \"" + apnType + "\"");
-        return PhoneConstants.APN_REQUEST_FAILED;
-    }
-
-    public static String networkTypeToApnType(int netType) {
-        switch(netType) {
-            case ConnectivityManager.TYPE_MOBILE:
-                return PhoneConstants.APN_TYPE_DEFAULT;  // TODO - use just one of these
-            case ConnectivityManager.TYPE_MOBILE_MMS:
-                return PhoneConstants.APN_TYPE_MMS;
-            case ConnectivityManager.TYPE_MOBILE_SUPL:
-                return PhoneConstants.APN_TYPE_SUPL;
-            case ConnectivityManager.TYPE_MOBILE_DUN:
-                return PhoneConstants.APN_TYPE_DUN;
-            case ConnectivityManager.TYPE_MOBILE_HIPRI:
-                return PhoneConstants.APN_TYPE_HIPRI;
-            case ConnectivityManager.TYPE_MOBILE_FOTA:
-                return PhoneConstants.APN_TYPE_FOTA;
-            case ConnectivityManager.TYPE_MOBILE_IMS:
-                return PhoneConstants.APN_TYPE_IMS;
-            case ConnectivityManager.TYPE_MOBILE_CBS:
-                return PhoneConstants.APN_TYPE_CBS;
-            case ConnectivityManager.TYPE_MOBILE_IA:
-                return PhoneConstants.APN_TYPE_IA;
-            case ConnectivityManager.TYPE_MOBILE_EMERGENCY:
-                return PhoneConstants.APN_TYPE_EMERGENCY;
-            default:
-                sloge("Error mapping networkType " + netType + " to apnType.");
-                return null;
-        }
-    }
-
-
-    /**
-     * @see android.net.NetworkStateTracker#getLinkProperties()
-     */
-    @Override
-    public LinkProperties getLinkProperties() {
-        return new LinkProperties(mLinkProperties);
-    }
-
-    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);
-    }
-
-    private void loge(String s) {
-        Slog.e(TAG, mApnType + ": " + s);
-    }
-
-    static private void sloge(String s) {
-        Slog.e(TAG, s);
-    }
-
-    @Override
-    public LinkQualityInfo getLinkQualityInfo() {
-        if (mNetworkInfo == null || mNetworkInfo.getType() == ConnectivityManager.TYPE_NONE) {
-            // no data available yet; just return
-            return null;
-        }
-
-        MobileLinkQualityInfo li = new MobileLinkQualityInfo();
-
-        li.setNetworkType(mNetworkInfo.getType());
-
-        mSamplingDataTracker.setCommonLinkQualityInfoFields(li);
-
-        if (mNetworkInfo.getSubtype() != TelephonyManager.NETWORK_TYPE_UNKNOWN) {
-            li.setMobileNetworkType(mNetworkInfo.getSubtype());
-
-            NetworkDataEntry entry = getNetworkDataEntry(mNetworkInfo.getSubtype());
-            if (entry != null) {
-                li.setTheoreticalRxBandwidth(entry.downloadBandwidth);
-                li.setTheoreticalRxBandwidth(entry.uploadBandwidth);
-                li.setTheoreticalLatency(entry.latency);
-            }
-
-            if (mSignalStrength != null) {
-                li.setNormalizedSignalStrength(getNormalizedSignalStrength(
-                        li.getMobileNetworkType(), mSignalStrength));
-            }
-        }
-
-        SignalStrength ss = mSignalStrength;
-        if (ss != null) {
-
-            li.setRssi(ss.getGsmSignalStrength());
-            li.setGsmErrorRate(ss.getGsmBitErrorRate());
-            li.setCdmaDbm(ss.getCdmaDbm());
-            li.setCdmaEcio(ss.getCdmaEcio());
-            li.setEvdoDbm(ss.getEvdoDbm());
-            li.setEvdoEcio(ss.getEvdoEcio());
-            li.setEvdoSnr(ss.getEvdoSnr());
-            li.setLteSignalStrength(ss.getLteSignalStrength());
-            li.setLteRsrp(ss.getLteRsrp());
-            li.setLteRsrq(ss.getLteRsrq());
-            li.setLteRssnr(ss.getLteRssnr());
-            li.setLteCqi(ss.getLteCqi());
-        }
-
-        if (VDBG) {
-            Slog.d(TAG, "Returning LinkQualityInfo with"
-                    + " MobileNetworkType = " + String.valueOf(li.getMobileNetworkType())
-                    + " Theoretical Rx BW = " + String.valueOf(li.getTheoreticalRxBandwidth())
-                    + " gsm Signal Strength = " + String.valueOf(li.getRssi())
-                    + " cdma Signal Strength = " + String.valueOf(li.getCdmaDbm())
-                    + " evdo Signal Strength = " + String.valueOf(li.getEvdoDbm())
-                    + " Lte Signal Strength = " + String.valueOf(li.getLteSignalStrength()));
-        }
-
-        return li;
-    }
-
-    static class NetworkDataEntry {
-        public int networkType;
-        public int downloadBandwidth;               // in kbps
-        public int uploadBandwidth;                 // in kbps
-        public int latency;                         // in millisecond
-
-        NetworkDataEntry(int i1, int i2, int i3, int i4) {
-            networkType = i1;
-            downloadBandwidth = i2;
-            uploadBandwidth = i3;
-            latency = i4;
-        }
-    }
-
-    private static NetworkDataEntry [] mTheoreticalBWTable = new NetworkDataEntry[] {
-            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_EDGE,      237,     118, UNKNOWN),
-            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_GPRS,       48,      40, UNKNOWN),
-            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_UMTS,      384,      64, UNKNOWN),
-            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_HSDPA,   14400, UNKNOWN, UNKNOWN),
-            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_HSUPA,   14400,    5760, UNKNOWN),
-            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_HSPA,    14400,    5760, UNKNOWN),
-            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_HSPAP,   21000,    5760, UNKNOWN),
-            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_CDMA,  UNKNOWN, UNKNOWN, UNKNOWN),
-            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_1xRTT, UNKNOWN, UNKNOWN, UNKNOWN),
-            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_EVDO_0,   2468,     153, UNKNOWN),
-            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_EVDO_A,   3072,    1800, UNKNOWN),
-            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_EVDO_B,  14700,    1800, UNKNOWN),
-            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_IDEN,  UNKNOWN, UNKNOWN, UNKNOWN),
-            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_LTE,    100000,   50000, UNKNOWN),
-            new NetworkDataEntry(TelephonyManager.NETWORK_TYPE_EHRPD, UNKNOWN, UNKNOWN, UNKNOWN),
-    };
-
-    private static NetworkDataEntry getNetworkDataEntry(int networkType) {
-        for (NetworkDataEntry entry : mTheoreticalBWTable) {
-            if (entry.networkType == networkType) {
-                return entry;
-            }
-        }
-
-        Slog.e(TAG, "Could not find Theoretical BW entry for " + String.valueOf(networkType));
-        return null;
-    }
-
-    private static int getNormalizedSignalStrength(int networkType, SignalStrength ss) {
-
-        int level;
-
-        switch(networkType) {
-            case TelephonyManager.NETWORK_TYPE_EDGE:
-            case TelephonyManager.NETWORK_TYPE_GPRS:
-            case TelephonyManager.NETWORK_TYPE_UMTS:
-            case TelephonyManager.NETWORK_TYPE_HSDPA:
-            case TelephonyManager.NETWORK_TYPE_HSUPA:
-            case TelephonyManager.NETWORK_TYPE_HSPA:
-            case TelephonyManager.NETWORK_TYPE_HSPAP:
-                level = ss.getGsmLevel();
-                break;
-            case TelephonyManager.NETWORK_TYPE_CDMA:
-            case TelephonyManager.NETWORK_TYPE_1xRTT:
-                level = ss.getCdmaLevel();
-                break;
-            case TelephonyManager.NETWORK_TYPE_EVDO_0:
-            case TelephonyManager.NETWORK_TYPE_EVDO_A:
-            case TelephonyManager.NETWORK_TYPE_EVDO_B:
-                level = ss.getEvdoLevel();
-                break;
-            case TelephonyManager.NETWORK_TYPE_LTE:
-                level = ss.getLteLevel();
-                break;
-            case TelephonyManager.NETWORK_TYPE_IDEN:
-            case TelephonyManager.NETWORK_TYPE_EHRPD:
-            default:
-                return UNKNOWN;
-        }
-
-        return (level * LinkQualityInfo.NORMALIZED_SIGNAL_STRENGTH_RANGE) /
-                SignalStrength.NUM_SIGNAL_STRENGTH_BINS;
-    }
-
-    @Override
-    public void startSampling(SamplingDataTracker.SamplingSnapshot s) {
-        mSamplingDataTracker.startSampling(s);
-    }
-
-    @Override
-    public void stopSampling(SamplingDataTracker.SamplingSnapshot s) {
-        mSamplingDataTracker.stopSampling(s);
-    }
-}
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index 5c12696..4ce9e68 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -51,7 +51,7 @@
  * {@link ConnectivityManager#registerNetworkCallback} calls.
  * It is used to direct traffic to the given {@code Network}, either on a {@link Socket} basis
  * through a targeted {@link SocketFactory} or process-wide via
- * {@link ConnectivityManager#setProcessDefaultNetwork}.
+ * {@link ConnectivityManager#bindProcessToNetwork}.
  */
 public class Network implements Parcelable {
 
@@ -245,7 +245,10 @@
      * @see java.net.URL#openConnection()
      */
     public URLConnection openConnection(URL url) throws IOException {
-        final ConnectivityManager cm = ConnectivityManager.getInstance();
+        final ConnectivityManager cm = ConnectivityManager.getInstanceOrNull();
+        if (cm == null) {
+            throw new IOException("No ConnectivityManager yet constructed, please construct one");
+        }
         // TODO: Should this be optimized to avoid fetching the global proxy for every request?
         ProxyInfo proxyInfo = cm.getGlobalProxy();
         if (proxyInfo == null) {
@@ -272,7 +275,6 @@
      * @throws IllegalArgumentException if the argument proxy is null.
      * @throws IOException if an error occurs while opening the connection.
      * @see java.net.URL#openConnection()
-     * @hide
      */
     public URLConnection openConnection(URL url, java.net.Proxy proxy) throws IOException {
         if (proxy == null) throw new IllegalArgumentException("proxy is null");
@@ -299,7 +301,7 @@
     /**
      * Binds the specified {@link DatagramSocket} to this {@code Network}. All data traffic on the
      * socket will be sent on this {@code Network}, irrespective of any process-wide network binding
-     * set by {@link ConnectivityManager#setProcessDefaultNetwork}. The socket must not be
+     * set by {@link ConnectivityManager#bindProcessToNetwork}. The socket must not be
      * connected.
      */
     public void bindSocket(DatagramSocket socket) throws IOException {
@@ -316,7 +318,7 @@
     /**
      * Binds the specified {@link Socket} to this {@code Network}. All data traffic on the socket
      * will be sent on this {@code Network}, irrespective of any process-wide network binding set by
-     * {@link ConnectivityManager#setProcessDefaultNetwork}. The socket must not be connected.
+     * {@link ConnectivityManager#bindProcessToNetwork}. The socket must not be connected.
      */
     public void bindSocket(Socket socket) throws IOException {
         // Apparently, the kernel doesn't update a connected TCP socket's routing upon mark changes.
@@ -338,6 +340,35 @@
         }
     }
 
+    /**
+     * Returns a handle representing this {@code Network}, for use with the NDK API.
+     */
+    public long getNetworkHandle() {
+        // The network handle is explicitly not the same as the netId.
+        //
+        // The netId is an implementation detail which might be changed in the
+        // future, or which alone (i.e. in the absence of some additional
+        // context) might not be sufficient to fully identify a Network.
+        //
+        // As such, the intention is to prevent accidental misuse of the API
+        // that might result if a developer assumed that handles and netIds
+        // were identical and passing a netId to a call expecting a handle
+        // "just worked".  Such accidental misuse, if widely deployed, might
+        // prevent future changes to the semantics of the netId field or
+        // inhibit the expansion of state required for Network objects.
+        //
+        // This extra layer of indirection might be seen as paranoia, and might
+        // never end up being necessary, but the added complexity is trivial.
+        // At some future date it may be desirable to realign the handle with
+        // Multiple Provisioning Domains API recommendations, as made by the
+        // IETF mif working group.
+        //
+        // The HANDLE_MAGIC value MUST be kept in sync with the corresponding
+        // value in the native/android/net.c NDK implementation.
+        final long HANDLE_MAGIC = 0xfacade;
+        return (((long) netId) << 32) | HANDLE_MAGIC;
+    }
+
     // implement the Parcelable interface
     public int describeContents() {
         return 0;
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 74d4ac2..c33aa2f 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -42,12 +42,18 @@
  * @hide
  */
 public abstract class NetworkAgent extends Handler {
+    // Guaranteed to be valid (not NETID_UNSET), otherwise registerNetworkAgent() would have thrown
+    // an exception.
+    public final int netId;
+
     private volatile AsyncChannel mAsyncChannel;
     private final String LOG_TAG;
     private static final boolean DBG = true;
     private static final boolean VDBG = false;
     private final Context mContext;
     private final ArrayList<Message>mPreConnectedQueue = new ArrayList<Message>();
+    private volatile long mLastBwRefreshTime = 0;
+    private static final long BW_REFRESH_MIN_WIN_MS = 500;
 
     private static final int BASE = Protocol.BASE_NETWORK_AGENT;
 
@@ -125,6 +131,13 @@
      */
     public static final int EVENT_SET_EXPLICITLY_SELECTED = BASE + 8;
 
+    /**
+     * Sent by ConnectivityService to the NetworkAgent to inform the agent to pull
+     * the underlying network connection for updated bandwidth information.
+     */
+    public static final int CMD_REQUEST_BANDWIDTH_UPDATE = BASE + 9;
+
+
     public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
             NetworkCapabilities nc, LinkProperties lp, int score) {
         this(looper, context, logTag, ni, nc, lp, score, null);
@@ -142,7 +155,7 @@
         if (VDBG) log("Registering NetworkAgent");
         ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(
                 Context.CONNECTIVITY_SERVICE);
-        cm.registerNetworkAgent(new Messenger(this), new NetworkInfo(ni),
+        netId = cm.registerNetworkAgent(new Messenger(this), new NetworkInfo(ni),
                 new LinkProperties(lp), new NetworkCapabilities(nc), score, misc);
     }
 
@@ -186,6 +199,15 @@
                 log("Unhandled Message " + msg);
                 break;
             }
+            case CMD_REQUEST_BANDWIDTH_UPDATE: {
+                if (VDBG) {
+                    log("CMD_REQUEST_BANDWIDTH_UPDATE request received.");
+                }
+                if (System.currentTimeMillis() > (mLastBwRefreshTime + BW_REFRESH_MIN_WIN_MS)) {
+                    pollLceData();
+                }
+                break;
+            }
             case CMD_REPORT_NETWORK_STATUS: {
                 if (VDBG) {
                     log("CMD_REPORT_NETWORK_STATUS(" +
@@ -228,6 +250,7 @@
      * Called by the bearer code when it has new NetworkCapabilities data.
      */
     public void sendNetworkCapabilities(NetworkCapabilities networkCapabilities) {
+        mLastBwRefreshTime = System.currentTimeMillis();
         queueOrSendMessage(EVENT_NETWORK_CAPABILITIES_CHANGED,
                 new NetworkCapabilities(networkCapabilities));
     }
@@ -276,6 +299,13 @@
     abstract protected void unwanted();
 
     /**
+     * Called when ConnectivityService request a bandwidth update. The parent factory
+     * shall try to overwrite this method and produce a bandwidth update if capable.
+     */
+    protected void pollLceData() {
+    }
+
+    /**
      * Called when the system determines the usefulness of this network.
      *
      * Networks claiming internet connectivity will have their internet
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index a7f9c5b..8c8bfab 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -148,9 +148,9 @@
      */
     public static final int NET_CAPABILITY_TRUSTED        = 14;
 
-    /*
+    /**
      * Indicates that this network is not a VPN.  This capability is set by default and should be
-     * explicitly cleared when creating VPN networks.
+     * explicitly cleared for VPN networks.
      */
     public static final int NET_CAPABILITY_NOT_VPN        = 15;
 
diff --git a/core/java/android/net/NetworkStateTracker.java b/core/java/android/net/NetworkStateTracker.java
deleted file mode 100644
index c80782c..0000000
--- a/core/java/android/net/NetworkStateTracker.java
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
- * Copyright (C) 2008 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.net;
-
-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}
- * with three services. Events to the ConnectivityService when
- * changes occur, an API for controlling the network and storage
- * for network specific information.
- *
- * The Connectivity will call startMonitoring before any other
- * method is called.
- *
- * {@hide}
- */
-public interface NetworkStateTracker {
-
-    /**
-     * -------------------------------------------------------------
-     * Event Interface back to ConnectivityService.
-     *
-     * The events that are to be sent back to the Handler passed
-     * to startMonitoring when the particular event occurs.
-     * -------------------------------------------------------------
-     */
-
-    /**
-     * The network state has changed and the NetworkInfo object
-     * contains the new state.
-     *
-     * msg.what = EVENT_STATE_CHANGED
-     * msg.obj = NetworkInfo object
-     */
-    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 = BASE_NETWORK_STATE_TRACKER + 1;
-
-    /**
-     * msg.what = EVENT_RESTORE_DEFAULT_NETWORK
-     * msg.obj = FeatureUser object
-     */
-    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 = 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;
-
-    /**
-     * -------------------------------------------------------------
-     * Control Interface
-     * -------------------------------------------------------------
-     */
-    /**
-     * Begin monitoring data connectivity.
-     *
-     * This is the first method called when this interface is used.
-     *
-     * @param context is the current Android context
-     * @param target is the Hander to which to return the events.
-     */
-    public void startMonitoring(Context context, Handler target);
-
-    /**
-     * Fetch NetworkInfo for the network
-     */
-    public NetworkInfo getNetworkInfo();
-
-    /**
-     * Return the LinkProperties for the connection.
-     *
-     * @return a copy of the LinkProperties, is never null.
-     */
-    public LinkProperties getLinkProperties();
-
-    /**
-     * @return a copy of this connections capabilities, may be empty but never null.
-     */
-    public NetworkCapabilities getNetworkCapabilities();
-
-    /**
-     * Get interesting information about this network link
-     * @return a copy of link information, null if not available
-     */
-    public LinkQualityInfo getLinkQualityInfo();
-
-    /**
-     * Return the system properties name associated with the tcp buffer sizes
-     * for this network.
-     */
-    public String getTcpBufferSizesPropName();
-
-    /**
-     * Disable connectivity to a network
-     * @return {@code true} if a teardown occurred, {@code false} if the
-     * teardown did not occur.
-     */
-    public boolean teardown();
-
-    /**
-     * Reenable connectivity to a network after a {@link #teardown()}.
-     * @return {@code true} if we're connected or expect to be connected
-     */
-    public boolean reconnect();
-
-    /**
-     * Captive portal check has completed
-     */
-    public void captivePortalCheckCompleted(boolean isCaptive);
-
-    /**
-     * Turn the wireless radio off for a network.
-     * @param turnOn {@code true} to turn the radio on, {@code false}
-     */
-    public boolean setRadio(boolean turnOn);
-
-    /**
-     * Returns an indication of whether this network is available for
-     * connections. A value of {@code false} means that some quasi-permanent
-     * condition prevents connectivity to this network.
-     *
-     * NOTE that this is broken on multi-connection devices.  Should be fixed in J release
-     * TODO - fix on multi-pdp devices
-     */
-    public boolean isAvailable();
-
-    /**
-     * User control of data connection through this network, typically persisted
-     * internally.
-     */
-    public void setUserDataEnable(boolean enabled);
-
-    /**
-     * Policy control of data connection through this network, typically not
-     * persisted internally. Usually used when {@link NetworkPolicy#limitBytes}
-     * is passed.
-     */
-    public void setPolicyDataEnable(boolean enabled);
-
-    /**
-     * -------------------------------------------------------------
-     * Storage API used by ConnectivityService for saving
-     * Network specific information.
-     * -------------------------------------------------------------
-     */
-
-    /**
-     * Check if private DNS route is set for the network
-     */
-    public boolean isPrivateDnsRouteSet();
-
-    /**
-     * Set a flag indicating private DNS route is set
-     */
-    public void privateDnsRouteSet(boolean enabled);
-
-    /**
-     * Check if default route is set
-     */
-    public boolean isDefaultRouteSet();
-
-    /**
-     * Set a flag indicating default route is set for the network
-     */
-    public void defaultRouteSet(boolean enabled);
-
-    /**
-     * Check if tear down was requested
-     */
-    public boolean isTeardownRequested();
-
-    /**
-     * Indicate tear down requested from connectivity
-     */
-    public void setTeardownRequested(boolean isRequested);
-
-    /**
-     * An external dependency has been met/unmet
-     */
-    public void setDependencyMet(boolean met);
-
-    /*
-     * Called once to setup async channel between this and
-     * the underlying network specific code.
-     */
-    public void supplyMessenger(Messenger messenger);
-
-    /*
-     * Network interface name that we'll lookup for sampling data
-     */
-    public String getNetworkInterfaceName();
-
-    /*
-     * Save the starting sample
-     */
-    public void startSampling(SamplingDataTracker.SamplingSnapshot s);
-
-    /*
-     * Save the ending sample
-     */
-    public void stopSampling(SamplingDataTracker.SamplingSnapshot s);
-
-    /*
-     * Record the current netId
-     */
-    public void setNetId(int netId);
-
-    /*
-     * ?
-     */
-    public Network getNetwork();
-
-}
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index d2a2997..29dd8ad 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -16,9 +16,11 @@
 
 package android.net;
 
+import java.io.FileDescriptor;
 import java.net.InetAddress;
 import java.net.Inet4Address;
 import java.net.Inet6Address;
+import java.net.SocketException;
 import java.net.UnknownHostException;
 import java.util.Collection;
 import java.util.Locale;
@@ -56,6 +58,30 @@
 
     /**
      * Start the DHCP client daemon, in order to have it request addresses
+     * for the named interface.  This returns {@code true} if the DHCPv4 daemon
+     * starts, {@code false} otherwise.  This call blocks until such time as a
+     * result is available or the default discovery timeout has been reached.
+     * Callers should check {@link #getDhcpResults} to determine whether DHCP
+     * succeeded or failed, and if it succeeded, to fetch the {@link DhcpResults}.
+     * @param interfaceName the name of the interface to configure
+     * @return {@code true} for success, {@code false} for failure
+     */
+    public native static boolean startDhcp(String interfaceName);
+
+    /**
+     * Initiate renewal on the DHCP client daemon for the named interface.  This
+     * returns {@code true} if the DHCPv4 daemon has been notified, {@code false}
+     * otherwise.  This call blocks until such time as a result is available or
+     * the default renew timeout has been reached.  Callers should check
+     * {@link #getDhcpResults} to determine whether DHCP succeeded or failed,
+     * and if it succeeded, to fetch the {@link DhcpResults}.
+     * @param interfaceName the name of the interface to configure
+     * @return {@code true} for success, {@code false} for failure
+     */
+    public native static boolean startDhcpRenew(String interfaceName);
+
+    /**
+     * Start the DHCP client daemon, in order to have it request addresses
      * for the named interface, and then configure the interface with those
      * addresses. This call blocks until it obtains a result (either success
      * or failure) from the daemon.
@@ -64,17 +90,31 @@
      * the IP address information.
      * @return {@code true} for success, {@code false} for failure
      */
-    public native static boolean runDhcp(String interfaceName, DhcpResults dhcpResults);
+    public static boolean runDhcp(String interfaceName, DhcpResults dhcpResults) {
+        return startDhcp(interfaceName) && getDhcpResults(interfaceName, dhcpResults);
+    }
 
     /**
-     * Initiate renewal on the Dhcp client daemon. This call blocks until it obtains
+     * Initiate renewal on the DHCP client daemon. This call blocks until it obtains
      * a result (either success or failure) from the daemon.
      * @param interfaceName the name of the interface to configure
      * @param dhcpResults if the request succeeds, this object is filled in with
      * the IP address information.
      * @return {@code true} for success, {@code false} for failure
      */
-    public native static boolean runDhcpRenew(String interfaceName, DhcpResults dhcpResults);
+    public static boolean runDhcpRenew(String interfaceName, DhcpResults dhcpResults) {
+        return startDhcpRenew(interfaceName) && getDhcpResults(interfaceName, dhcpResults);
+    }
+
+    /**
+     * Fetch results from the DHCP client daemon. This call returns {@code true} if
+     * if there are results available to be read, {@code false} otherwise.
+     * @param interfaceName the name of the interface to configure
+     * @param dhcpResults if the request succeeds, this object is filled in with
+     * the IP address information.
+     * @return {@code true} for success, {@code false} for failure
+     */
+    public native static boolean getDhcpResults(String interfaceName, DhcpResults dhcpResults);
 
     /**
      * Shut down the DHCP client daemon.
@@ -101,6 +141,11 @@
     public native static String getDhcpError();
 
     /**
+     * Attaches a socket filter that accepts DHCP packets to the given socket.
+     */
+    public native static void attachDhcpFilter(FileDescriptor fd) throws SocketException;
+
+    /**
      * Binds the current process to the network designated by {@code netId}.  All sockets created
      * in the future (and not explicitly bound via a bound {@link SocketFactory} (see
      * {@link Network#getSocketFactory}) will be bound to this network.  Note that if this
@@ -114,7 +159,7 @@
      * Return the netId last passed to {@link #bindProcessToNetwork}, or NETID_UNSET if
      * {@link #unbindProcessToNetwork} has been called since {@link #bindProcessToNetwork}.
      */
-    public native static int getNetworkBoundToProcess();
+    public native static int getBoundNetworkForProcess();
 
     /**
      * Binds host resolutions performed by this process to the network designated by {@code netId}.
@@ -133,6 +178,15 @@
     public native static int bindSocketToNetwork(int socketfd, int netId);
 
     /**
+     * Protect {@code fd} from VPN connections.  After protecting, data sent through
+     * this socket will go directly to the underlying network, so its traffic will not be
+     * forwarded through the VPN.
+     */
+    public static boolean protectFromVpn(FileDescriptor fd) {
+        return protectFromVpn(fd.getInt$());
+    }
+
+    /**
      * Protect {@code socketfd} from VPN connections.  After protecting, data sent through
      * this socket will go directly to the underlying network, so its traffic will not be
      * forwarded through the VPN.
@@ -192,6 +246,25 @@
     }
 
     /**
+     * Convert an IPv4 netmask to a prefix length, checking that the netmask is contiguous.
+     * @param netmask as a {@code Inet4Address}.
+     * @return the network prefix length
+     * @throws IllegalArgumentException the specified netmask was not contiguous.
+     * @hide
+     */
+    public static int netmaskToPrefixLength(Inet4Address netmask) {
+        // inetAddressToInt returns an int in *network* byte order.
+        int i = Integer.reverseBytes(inetAddressToInt(netmask));
+        int prefixLength = Integer.bitCount(i);
+        int trailingZeros = Integer.numberOfTrailingZeros(i);
+        if (trailingZeros != 32 - prefixLength) {
+            throw new IllegalArgumentException("Non-contiguous netmask: " + Integer.toHexString(i));
+        }
+        return prefixLength;
+    }
+
+
+    /**
      * Create an InetAddress from a string where the string must be a standard
      * representation of a V4 or V6 address.  Avoids doing a DNS lookup on failure
      * but it will throw an IllegalArgumentException in that case.
@@ -271,6 +344,22 @@
     }
 
     /**
+     * Returns the implicit netmask of an IPv4 address, as was the custom before 1993.
+     */
+    public static int getImplicitNetmask(Inet4Address address) {
+        int firstByte = address.getAddress()[0] & 0xff;  // Convert to an unsigned value.
+        if (firstByte < 128) {
+            return 8;
+        } else if (firstByte < 192) {
+            return 16;
+        } else if (firstByte < 224) {
+            return 24;
+        } else {
+            return 32;  // Will likely not end well for other reasons.
+        }
+    }
+
+    /**
      * Utility method to parse strings such as "192.0.2.5/24" or "2001:db8::cafe:d00d/64".
      * @hide
      */
diff --git a/core/java/android/net/Proxy.java b/core/java/android/net/Proxy.java
index 3477b02..e07f211 100644
--- a/core/java/android/net/Proxy.java
+++ b/core/java/android/net/Proxy.java
@@ -44,14 +44,9 @@
     private static final ProxySelector sDefaultProxySelector;
 
     /**
-     * Used to notify an app that's caching the default connection proxy
-     * that either the default connection or its proxy has changed.
-     * The intent will have the following extra value:</p>
-     * <ul>
-     *   <li><em>EXTRA_PROXY_INFO</em> - The ProxyProperties for the proxy.  Non-null,
-     *                                   though if the proxy is undefined the host string
-     *                                   will be empty.
-     * </ul>
+     * Used to notify an app that's caching the proxy that either the default
+     * connection has changed or any connection's proxy has changed. The new
+     * proxy should be queried using {@link ConnectivityManager#getDefaultProxy()}.
      *
      * <p class="note">This is a protected intent that can only be sent by the system
      */
@@ -60,6 +55,11 @@
     /**
      * Intent extra included with {@link #PROXY_CHANGE_ACTION} intents.
      * It describes the new proxy being used (as a {@link ProxyInfo} object).
+     * @deprecated Because {@code PROXY_CHANGE_ACTION} is sent whenever the proxy
+     * for any network on the system changes, applications should always use
+     * {@link ConnectivityManager#getDefaultProxy()} or
+     * {@link ConnectivityManager#getLinkProperties(Network)}.{@link LinkProperties#getHttpProxy()}
+     * to get the proxy for the Network(s) they are using.
      */
     public static final String EXTRA_PROXY_INFO = "android.intent.extra.PROXY_INFO";
 
diff --git a/core/java/android/net/ProxyDataTracker.java b/core/java/android/net/ProxyDataTracker.java
deleted file mode 100644
index 7d23125..0000000
--- a/core/java/android/net/ProxyDataTracker.java
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net;
-
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.ServiceConnection;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.Messenger;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.util.Log;
-
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicInteger;
-
-/**
- * A data tracker responsible for bringing up and tearing down the system proxy server.
- *
- * {@hide}
- */
-public class ProxyDataTracker extends BaseNetworkStateTracker {
-    private static final String TAG = "ProxyDataTracker";
-    private static final String NETWORK_TYPE = "PROXY";
-
-    // TODO: investigate how to get these DNS addresses from the system.
-    private static final String DNS1 = "8.8.8.8";
-    private static final String DNS2 = "8.8.4.4";
-    private static final String INTERFACE_NAME = "ifb0";
-    private static final String REASON_ENABLED = "enabled";
-    private static final String REASON_DISABLED = "disabled";
-    private static final String REASON_PROXY_DOWN = "proxy_down";
-
-    private static final int MSG_TEAR_DOWN_REQUEST = 1;
-    private static final int MSG_SETUP_REQUEST = 2;
-
-    private static final String PERMISSION_PROXY_STATUS_SENDER =
-            "android.permission.ACCESS_NETWORK_CONDITIONS";
-    private static final String ACTION_PROXY_STATUS_CHANGE =
-            "com.android.net.PROXY_STATUS_CHANGE";
-    private static final String KEY_IS_PROXY_AVAILABLE = "is_proxy_available";
-    private static final String KEY_REPLY_TO_MESSENGER_BINDER = "reply_to_messenger_binder";
-    private static final String KEY_REPLY_TO_MESSENGER_BINDER_BUNDLE =
-            "reply_to_messenger_binder_bundle";
-
-    private Handler mTarget;
-    private Messenger mProxyStatusService;
-    private AtomicBoolean mReconnectRequested = new AtomicBoolean(false);
-    private AtomicBoolean mIsProxyAvailable = new AtomicBoolean(false);
-    private final AtomicInteger mDefaultGatewayAddr = new AtomicInteger(0);
-
-    private final BroadcastReceiver mProxyStatusServiceListener = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (intent.getAction().equals(ACTION_PROXY_STATUS_CHANGE)) {
-                mIsProxyAvailable.set(intent.getBooleanExtra(KEY_IS_PROXY_AVAILABLE, false));
-                if (mIsProxyAvailable.get()) {
-                    Bundle bundle = intent.getBundleExtra(KEY_REPLY_TO_MESSENGER_BINDER_BUNDLE);
-                    if (bundle == null || bundle.getBinder(KEY_REPLY_TO_MESSENGER_BINDER) == null) {
-                        Log.e(TAG, "no messenger binder in the intent to send future requests");
-                        mIsProxyAvailable.set(false);
-                        return;
-                    }
-                    mProxyStatusService =
-                            new Messenger(bundle.getBinder(KEY_REPLY_TO_MESSENGER_BINDER));
-                    // If there is a pending reconnect request, do it now.
-                    if (mReconnectRequested.get()) {
-                        reconnect();
-                    }
-                } else {
-                    setDetailedState(NetworkInfo.DetailedState.DISCONNECTED,
-                            REASON_PROXY_DOWN, null);
-                }
-            } else {
-                Log.d(TAG, "Unrecognized broadcast intent");
-            }
-        }
-    };
-
-    /**
-     * Create a new ProxyDataTracker
-     */
-    public ProxyDataTracker() {
-        mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_PROXY, 0, NETWORK_TYPE, "");
-        mLinkProperties = new LinkProperties();
-        mNetworkCapabilities = new NetworkCapabilities();
-        mNetworkInfo.setIsAvailable(true);
-        try {
-            mLinkProperties.addDnsServer(InetAddress.getByName(DNS1));
-            mLinkProperties.addDnsServer(InetAddress.getByName(DNS2));
-            mLinkProperties.setInterfaceName(INTERFACE_NAME);
-        } catch (UnknownHostException e) {
-            Log.e(TAG, "Could not add DNS address", e);
-        }
-    }
-
-    @Override
-    public Object clone() throws CloneNotSupportedException {
-        throw new CloneNotSupportedException();
-    }
-
-    @Override
-    public void startMonitoring(Context context, Handler target) {
-        mContext = context;
-        mTarget = target;
-        mContext.registerReceiver(mProxyStatusServiceListener,
-                new IntentFilter(ACTION_PROXY_STATUS_CHANGE),
-                PERMISSION_PROXY_STATUS_SENDER,
-                null);
-    }
-
-    /**
-     * Disable connectivity to the network.
-     */
-    public boolean teardown() {
-        setTeardownRequested(true);
-        mReconnectRequested.set(false);
-        try {
-            if (mIsProxyAvailable.get() && mProxyStatusService != null) {
-                mProxyStatusService.send(Message.obtain(null, MSG_TEAR_DOWN_REQUEST));
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "Unable to connect to proxy status service", e);
-            return false;
-        }
-        setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, REASON_DISABLED, null);
-        return true;
-    }
-
-    /**
-     * Re-enable proxy data connectivity after a {@link #teardown()}.
-     */
-    public boolean reconnect() {
-        mReconnectRequested.set(true);
-        setTeardownRequested(false);
-        if (!mIsProxyAvailable.get()) {
-            Log.w(TAG, "Reconnect requested even though proxy service is not up. Bailing.");
-            return false;
-        }
-        setDetailedState(NetworkInfo.DetailedState.CONNECTING, REASON_ENABLED, null);
-
-        try {
-            mProxyStatusService.send(Message.obtain(null, MSG_SETUP_REQUEST));
-        } catch (RemoteException e) {
-            Log.e(TAG, "Unable to connect to proxy status service", e);
-            setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, REASON_PROXY_DOWN, null);
-            return false;
-        }
-        // We'll assume proxy is set up successfully. If not, a status change broadcast will be
-        // received afterwards to indicate any failure.
-        setDetailedState(NetworkInfo.DetailedState.CONNECTED, REASON_ENABLED, null);
-        return true;
-    }
-
-    /**
-     * Fetch default gateway address for the network
-     */
-    public int getDefaultGatewayAddr() {
-        return mDefaultGatewayAddr.get();
-    }
-
-    /**
-     * Return the system properties name associated with the tcp buffer sizes
-     * for this network.
-     */
-    public String getTcpBufferSizesPropName() {
-        return "net.tcp.buffersize.wifi";
-    }
-
-    /**
-     * Record the detailed state of a network, and if it is a
-     * change from the previous state, send a notification to
-     * any listeners.
-     * @param state the new @{code DetailedState}
-     * @param reason a {@code String} indicating a reason for the state change,
-     * if one was supplied. May be {@code null}.
-     * @param extraInfo optional {@code String} providing extra information about the state change
-     */
-    private void setDetailedState(NetworkInfo.DetailedState state, String reason,
-            String extraInfo) {
-        mNetworkInfo.setDetailedState(state, reason, extraInfo);
-        Message msg = mTarget.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo);
-        msg.sendToTarget();
-    }
-}
diff --git a/core/java/android/net/SamplingDataTracker.java b/core/java/android/net/SamplingDataTracker.java
deleted file mode 100644
index acd56f2..0000000
--- a/core/java/android/net/SamplingDataTracker.java
+++ /dev/null
@@ -1,300 +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.net;
-
-
-import android.os.SystemClock;
-import android.util.Slog;
-
-import java.io.BufferedReader;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
-import java.io.IOException;
-import java.util.Iterator;
-import java.util.Map;
-
-/**
- * @hide
- */
-public class SamplingDataTracker
-{
-    private static final boolean DBG = false;
-    private static final String  TAG = "SamplingDataTracker";
-
-    public static class SamplingSnapshot
-    {
-        public long mTxByteCount;
-        public long mRxByteCount;
-        public long mTxPacketCount;
-        public long mRxPacketCount;
-        public long mTxPacketErrorCount;
-        public long mRxPacketErrorCount;
-        public long mTimestamp;
-    }
-
-    public static void getSamplingSnapshots(Map<String, SamplingSnapshot> mapIfaceToSample) {
-
-        BufferedReader reader = null;
-        try {
-            reader = new BufferedReader(new FileReader("/proc/net/dev"));
-
-            // Skip over the line bearing column titles (there are 2 lines)
-            String line;
-            reader.readLine();
-            reader.readLine();
-
-            while ((line = reader.readLine()) != null) {
-
-                // remove leading whitespace
-                line = line.trim();
-
-                String[] tokens = line.split("[ ]+");
-                if (tokens.length < 17) {
-                    continue;
-                }
-
-                /* column format is
-                 * Interface  (Recv)bytes packets errs drop fifo frame compressed multicast \
-                 *            (Transmit)bytes packets errs drop fifo colls carrier compress
-                */
-
-                String currentIface = tokens[0].split(":")[0];
-                if (DBG) Slog.d(TAG, "Found data for interface " + currentIface);
-                if (mapIfaceToSample.containsKey(currentIface)) {
-
-                    try {
-                        SamplingSnapshot ss = new SamplingSnapshot();
-
-                        ss.mTxByteCount        = Long.parseLong(tokens[1]);
-                        ss.mTxPacketCount      = Long.parseLong(tokens[2]);
-                        ss.mTxPacketErrorCount = Long.parseLong(tokens[3]);
-                        ss.mRxByteCount        = Long.parseLong(tokens[9]);
-                        ss.mRxPacketCount      = Long.parseLong(tokens[10]);
-                        ss.mRxPacketErrorCount = Long.parseLong(tokens[11]);
-
-                        ss.mTimestamp          = SystemClock.elapsedRealtime();
-
-                        if (DBG) {
-                            Slog.d(TAG, "Interface = " + currentIface);
-                            Slog.d(TAG, "ByteCount = " + String.valueOf(ss.mTxByteCount));
-                            Slog.d(TAG, "TxPacketCount = " + String.valueOf(ss.mTxPacketCount));
-                            Slog.d(TAG, "TxPacketErrorCount = "
-                                    + String.valueOf(ss.mTxPacketErrorCount));
-                            Slog.d(TAG, "RxByteCount = " + String.valueOf(ss.mRxByteCount));
-                            Slog.d(TAG, "RxPacketCount = " + String.valueOf(ss.mRxPacketCount));
-                            Slog.d(TAG, "RxPacketErrorCount = "
-                                    + String.valueOf(ss.mRxPacketErrorCount));
-                            Slog.d(TAG, "Timestamp = " + String.valueOf(ss.mTimestamp));
-                            Slog.d(TAG, "---------------------------");
-                        }
-
-                        mapIfaceToSample.put(currentIface, ss);
-
-                    } catch (NumberFormatException e) {
-                        // just ignore this data point
-                    }
-                }
-            }
-
-            if (DBG) {
-                Iterator it = mapIfaceToSample.entrySet().iterator();
-                while (it.hasNext()) {
-                    Map.Entry kvpair = (Map.Entry)it.next();
-                    if (kvpair.getValue() == null) {
-                        Slog.d(TAG, "could not find snapshot for interface " + kvpair.getKey());
-                    }
-                }
-            }
-        } catch(FileNotFoundException e) {
-            Slog.e(TAG, "could not find /proc/net/dev");
-        } catch (IOException e) {
-            Slog.e(TAG, "could not read /proc/net/dev");
-        } finally {
-            try {
-                if (reader != null) {
-                    reader.close();
-                }
-            } catch (IOException e) {
-                Slog.e(TAG, "could not close /proc/net/dev");
-            }
-        }
-    }
-
-    // Snapshots from previous sampling interval
-    private SamplingSnapshot mBeginningSample;
-    private SamplingSnapshot mEndingSample;
-
-    // Starting snapshot of current interval
-    private SamplingSnapshot mLastSample;
-
-    // Protects sampling data from concurrent access
-    public final Object mSamplingDataLock = new Object();
-
-    // We need long enough time for a good sample
-    private final int MINIMUM_SAMPLING_INTERVAL = 15 * 1000;
-
-    // statistics is useless unless we have enough data
-    private final int MINIMUM_SAMPLED_PACKETS   = 30;
-
-    public void startSampling(SamplingSnapshot s) {
-        synchronized(mSamplingDataLock) {
-            mLastSample = s;
-        }
-    }
-
-    public void stopSampling(SamplingSnapshot s) {
-        synchronized(mSamplingDataLock) {
-            if (mLastSample != null) {
-                if (s.mTimestamp - mLastSample.mTimestamp > MINIMUM_SAMPLING_INTERVAL
-                        && getSampledPacketCount(mLastSample, s) > MINIMUM_SAMPLED_PACKETS) {
-                    mBeginningSample = mLastSample;
-                    mEndingSample = s;
-                    mLastSample = null;
-                } else {
-                    if (DBG) Slog.d(TAG, "Throwing current sample away because it is too small");
-                }
-            }
-        }
-    }
-
-    public void resetSamplingData() {
-        if (DBG) Slog.d(TAG, "Resetting sampled network data");
-        synchronized(mSamplingDataLock) {
-
-            // We could just take another sample here and treat it as an
-            // 'ending sample' effectively shortening sampling interval, but that
-            // requires extra work (specifically, reading the sample needs to be
-            // done asynchronously)
-
-            mLastSample = null;
-        }
-    }
-
-    public long getSampledTxByteCount() {
-        synchronized(mSamplingDataLock) {
-            if (mBeginningSample != null && mEndingSample != null) {
-                return mEndingSample.mTxByteCount - mBeginningSample.mTxByteCount;
-            } else {
-                return LinkQualityInfo.UNKNOWN_LONG;
-            }
-        }
-    }
-
-    public long getSampledTxPacketCount() {
-        synchronized(mSamplingDataLock) {
-            if (mBeginningSample != null && mEndingSample != null) {
-                return mEndingSample.mTxPacketCount - mBeginningSample.mTxPacketCount;
-            } else {
-                return LinkQualityInfo.UNKNOWN_LONG;
-            }
-        }
-    }
-
-    public long getSampledTxPacketErrorCount() {
-        synchronized(mSamplingDataLock) {
-            if (mBeginningSample != null && mEndingSample != null) {
-                return mEndingSample.mTxPacketErrorCount - mBeginningSample.mTxPacketErrorCount;
-            } else {
-                return LinkQualityInfo.UNKNOWN_LONG;
-            }
-        }
-    }
-
-    public long getSampledRxByteCount() {
-        synchronized(mSamplingDataLock) {
-            if (mBeginningSample != null && mEndingSample != null) {
-                return mEndingSample.mRxByteCount - mBeginningSample.mRxByteCount;
-            } else {
-                return LinkQualityInfo.UNKNOWN_LONG;
-            }
-        }
-    }
-
-    public long getSampledRxPacketCount() {
-        synchronized(mSamplingDataLock) {
-            if (mBeginningSample != null && mEndingSample != null) {
-                return mEndingSample.mRxPacketCount - mBeginningSample.mRxPacketCount;
-            } else {
-                return LinkQualityInfo.UNKNOWN_LONG;
-            }
-        }
-    }
-
-    public long getSampledPacketCount() {
-        return getSampledPacketCount(mBeginningSample, mEndingSample);
-    }
-
-    public long getSampledPacketCount(SamplingSnapshot begin, SamplingSnapshot end) {
-        if (begin != null && end != null) {
-            long rxPacketCount = end.mRxPacketCount - begin.mRxPacketCount;
-            long txPacketCount = end.mTxPacketCount - begin.mTxPacketCount;
-            return rxPacketCount + txPacketCount;
-        } else {
-            return LinkQualityInfo.UNKNOWN_LONG;
-        }
-    }
-
-    public long getSampledPacketErrorCount() {
-        if (mBeginningSample != null && mEndingSample != null) {
-            long rxPacketErrorCount = getSampledRxPacketErrorCount();
-            long txPacketErrorCount = getSampledTxPacketErrorCount();
-            return rxPacketErrorCount + txPacketErrorCount;
-        } else {
-            return LinkQualityInfo.UNKNOWN_LONG;
-        }
-    }
-
-    public long getSampledRxPacketErrorCount() {
-        synchronized(mSamplingDataLock) {
-            if (mBeginningSample != null && mEndingSample != null) {
-                return mEndingSample.mRxPacketErrorCount - mBeginningSample.mRxPacketErrorCount;
-            } else {
-                return LinkQualityInfo.UNKNOWN_LONG;
-            }
-        }
-    }
-
-    public long getSampleTimestamp() {
-        synchronized(mSamplingDataLock) {
-            if (mEndingSample != null) {
-                return mEndingSample.mTimestamp;
-            } else {
-                return LinkQualityInfo.UNKNOWN_LONG;
-            }
-        }
-    }
-
-    public int getSampleDuration() {
-        synchronized(mSamplingDataLock) {
-            if (mBeginningSample != null && mEndingSample != null) {
-                return (int) (mEndingSample.mTimestamp - mBeginningSample.mTimestamp);
-            } else {
-                return LinkQualityInfo.UNKNOWN_INT;
-            }
-        }
-    }
-
-    public void setCommonLinkQualityInfoFields(LinkQualityInfo li) {
-        synchronized(mSamplingDataLock) {
-            li.setLastDataSampleTime(getSampleTimestamp());
-            li.setDataSampleDuration(getSampleDuration());
-            li.setPacketCount(getSampledPacketCount());
-            li.setPacketErrorCount(getSampledPacketErrorCount());
-        }
-    }
-}
-
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index c26af06..a0e65eb 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -679,7 +679,7 @@
          *
          * By default, all traffic from apps is forwarded through the VPN interface and it is not
          * possible for apps to side-step the VPN. If this method is called, apps may use methods
-         * such as {@link ConnectivityManager#setProcessDefaultNetwork} to instead send/receive
+         * such as {@link ConnectivityManager#bindProcessToNetwork} to instead send/receive
          * directly over the underlying network or any other network they have permissions for.
          *
          * @return this {@link Builder} object to facilitate chaining of method calls.
diff --git a/core/java/android/net/dhcp/DhcpPacket.java b/core/java/android/net/dhcp/DhcpPacket.java
deleted file mode 100644
index c7c25f0..0000000
--- a/core/java/android/net/dhcp/DhcpPacket.java
+++ /dev/null
@@ -1,895 +0,0 @@
-package android.net.dhcp;
-
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.charset.StandardCharsets;
-import java.nio.ShortBuffer;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Defines basic data and operations needed to build and use packets for the
- * DHCP protocol.  Subclasses create the specific packets used at each
- * stage of the negotiation.
- */
-abstract class DhcpPacket {
-    protected static final String TAG = "DhcpPacket";
-
-    /**
-     * Packet encapsulations.
-     */
-    public static final int ENCAP_L2 = 0;    // EthernetII header included
-    public static final int ENCAP_L3 = 1;    // IP/UDP header included
-    public static final int ENCAP_BOOTP = 2; // BOOTP contents only
-
-    /**
-     * IP layer definitions.
-     */
-    private static final byte IP_TYPE_UDP = (byte) 0x11;
-
-    /**
-     * IP: Version 4, Header Length 20 bytes
-     */
-    private static final byte IP_VERSION_HEADER_LEN = (byte) 0x45;
-
-    /**
-     * IP: Flags 0, Fragment Offset 0, Don't Fragment
-     */
-    private static final short IP_FLAGS_OFFSET = (short) 0x4000;
-
-    /**
-     * IP: TOS
-     */
-    private static final byte IP_TOS_LOWDELAY = (byte) 0x10;
-
-    /**
-     * IP: TTL -- use default 64 from RFC1340
-     */
-    private static final byte IP_TTL = (byte) 0x40;
-
-    /**
-     * The client DHCP port.
-     */
-    static final short DHCP_CLIENT = (short) 68;
-
-    /**
-     * The server DHCP port.
-     */
-    static final short DHCP_SERVER = (short) 67;
-
-    /**
-     * The message op code indicating a request from a client.
-     */
-    protected static final byte DHCP_BOOTREQUEST = (byte) 1;
-
-    /**
-     * The message op code indicating a response from the server.
-     */
-    protected static final byte DHCP_BOOTREPLY = (byte) 2;
-
-    /**
-     * The code type used to identify an Ethernet MAC address in the
-     * Client-ID field.
-     */
-    protected static final byte CLIENT_ID_ETHER = (byte) 1;
-
-    /**
-     * The maximum length of a packet that can be constructed.
-     */
-    protected static final int MAX_LENGTH = 1500;
-
-    /**
-     * DHCP Optional Type: DHCP Subnet Mask
-     */
-    protected static final byte DHCP_SUBNET_MASK = 1;
-    protected InetAddress mSubnetMask;
-
-    /**
-     * DHCP Optional Type: DHCP Router
-     */
-    protected static final byte DHCP_ROUTER = 3;
-    protected InetAddress mGateway;
-
-    /**
-     * DHCP Optional Type: DHCP DNS Server
-     */
-    protected static final byte DHCP_DNS_SERVER = 6;
-    protected List<InetAddress> mDnsServers;
-
-    /**
-     * DHCP Optional Type: DHCP Host Name
-     */
-    protected static final byte DHCP_HOST_NAME = 12;
-    protected String mHostName;
-
-    /**
-     * DHCP Optional Type: DHCP DOMAIN NAME
-     */
-    protected static final byte DHCP_DOMAIN_NAME = 15;
-    protected String mDomainName;
-
-    /**
-     * DHCP Optional Type: DHCP BROADCAST ADDRESS
-     */
-    protected static final byte DHCP_BROADCAST_ADDRESS = 28;
-    protected InetAddress mBroadcastAddress;
-
-    /**
-     * DHCP Optional Type: DHCP Requested IP Address
-     */
-    protected static final byte DHCP_REQUESTED_IP = 50;
-    protected InetAddress mRequestedIp;
-
-    /**
-     * DHCP Optional Type: DHCP Lease Time
-     */
-    protected static final byte DHCP_LEASE_TIME = 51;
-    protected Integer mLeaseTime;
-
-    /**
-     * DHCP Optional Type: DHCP Message Type
-     */
-    protected static final byte DHCP_MESSAGE_TYPE = 53;
-    // the actual type values
-    protected static final byte DHCP_MESSAGE_TYPE_DISCOVER = 1;
-    protected static final byte DHCP_MESSAGE_TYPE_OFFER = 2;
-    protected static final byte DHCP_MESSAGE_TYPE_REQUEST = 3;
-    protected static final byte DHCP_MESSAGE_TYPE_DECLINE = 4;
-    protected static final byte DHCP_MESSAGE_TYPE_ACK = 5;
-    protected static final byte DHCP_MESSAGE_TYPE_NAK = 6;
-    protected static final byte DHCP_MESSAGE_TYPE_INFORM = 8;
-
-    /**
-     * DHCP Optional Type: DHCP Server Identifier
-     */
-    protected static final byte DHCP_SERVER_IDENTIFIER = 54;
-    protected InetAddress mServerIdentifier;
-
-    /**
-     * DHCP Optional Type: DHCP Parameter List
-     */
-    protected static final byte DHCP_PARAMETER_LIST = 55;
-    protected byte[] mRequestedParams;
-
-    /**
-     * DHCP Optional Type: DHCP MESSAGE
-     */
-    protected static final byte DHCP_MESSAGE = 56;
-    protected String mMessage;
-
-    /**
-     * DHCP Optional Type: DHCP Renewal Time Value
-     */
-    protected static final byte DHCP_RENEWAL_TIME = 58;
-
-    /**
-     * DHCP Optional Type: Vendor Class Identifier
-     */
-    protected static final byte DHCP_VENDOR_CLASS_ID = 60;
-
-    /**
-     * DHCP Optional Type: DHCP Client Identifier
-     */
-    protected static final byte DHCP_CLIENT_IDENTIFIER = 61;
-
-    /**
-     * The transaction identifier used in this particular DHCP negotiation
-     */
-    protected final int mTransId;
-
-    /**
-     * The IP address of the client host.  This address is typically
-     * proposed by the client (from an earlier DHCP negotiation) or
-     * supplied by the server.
-     */
-    protected final InetAddress mClientIp;
-    protected final InetAddress mYourIp;
-    private final InetAddress mNextIp;
-    private final InetAddress mRelayIp;
-
-    /**
-     * Does the client request a broadcast response?
-     */
-    protected boolean mBroadcast;
-
-    /**
-     * The six-octet MAC of the client.
-     */
-    protected final byte[] mClientMac;
-
-    /**
-     * Asks the packet object to signal the next operation in the DHCP
-     * protocol.  The available actions are methods defined in the
-     * DhcpStateMachine interface.
-     */
-    public abstract void doNextOp(DhcpStateMachine stateMachine);
-
-    /**
-     * Asks the packet object to create a ByteBuffer serialization of
-     * the packet for transmission.
-     */
-    public abstract ByteBuffer buildPacket(int encap, short destUdp,
-        short srcUdp);
-
-    /**
-     * Allows the concrete class to fill in packet-type-specific details,
-     * typically optional parameters at the end of the packet.
-     */
-    abstract void finishPacket(ByteBuffer buffer);
-
-    protected DhcpPacket(int transId, InetAddress clientIp, InetAddress yourIp,
-                         InetAddress nextIp, InetAddress relayIp,
-                         byte[] clientMac, boolean broadcast) {
-        mTransId = transId;
-        mClientIp = clientIp;
-        mYourIp = yourIp;
-        mNextIp = nextIp;
-        mRelayIp = relayIp;
-        mClientMac = clientMac;
-        mBroadcast = broadcast;
-    }
-
-    /**
-     * Returns the transaction ID.
-     */
-    public int getTransactionId() {
-        return mTransId;
-    }
-
-    /**
-     * Creates a new L3 packet (including IP header) containing the
-     * DHCP udp packet.  This method relies upon the delegated method
-     * finishPacket() to insert the per-packet contents.
-     */
-    protected void fillInPacket(int encap, InetAddress destIp,
-        InetAddress srcIp, short destUdp, short srcUdp, ByteBuffer buf,
-        byte requestCode, boolean broadcast) {
-        byte[] destIpArray = destIp.getAddress();
-        byte[] srcIpArray = srcIp.getAddress();
-        int ipLengthOffset = 0;
-        int ipChecksumOffset = 0;
-        int endIpHeader = 0;
-        int udpHeaderOffset = 0;
-        int udpLengthOffset = 0;
-        int udpChecksumOffset = 0;
-
-        buf.clear();
-        buf.order(ByteOrder.BIG_ENDIAN);
-
-        // if a full IP packet needs to be generated, put the IP & UDP
-        // headers in place, and pre-populate with artificial values
-        // needed to seed the IP checksum.
-        if (encap == ENCAP_L3) {
-            // fake IP header, used in the IP-header checksum
-            buf.put(IP_VERSION_HEADER_LEN);
-            buf.put(IP_TOS_LOWDELAY);    // tos: IPTOS_LOWDELAY
-            ipLengthOffset = buf.position();
-            buf.putShort((short)0);  // length
-            buf.putShort((short)0);  // id
-            buf.putShort(IP_FLAGS_OFFSET); // ip offset: don't fragment
-            buf.put(IP_TTL);    // TTL: use default 64 from RFC1340
-            buf.put(IP_TYPE_UDP);
-            ipChecksumOffset = buf.position();
-            buf.putShort((short) 0); // checksum
-
-            buf.put(srcIpArray);
-            buf.put(destIpArray);
-            endIpHeader = buf.position();
-
-            // UDP header
-            udpHeaderOffset = buf.position();
-            buf.putShort(srcUdp);
-            buf.putShort(destUdp);
-            udpLengthOffset = buf.position();
-            buf.putShort((short) 0); // length
-            udpChecksumOffset = buf.position();
-            buf.putShort((short) 0); // UDP checksum -- initially zero
-        }
-
-        // DHCP payload
-        buf.put(requestCode);
-        buf.put((byte) 1); // Hardware Type: Ethernet
-        buf.put((byte) mClientMac.length); // Hardware Address Length
-        buf.put((byte) 0); // Hop Count
-        buf.putInt(mTransId);  // Transaction ID
-        buf.putShort((short) 0); // Elapsed Seconds
-
-        if (broadcast) {
-            buf.putShort((short) 0x8000); // Flags
-        } else {
-            buf.putShort((short) 0x0000); // Flags
-        }
-
-        buf.put(mClientIp.getAddress());
-        buf.put(mYourIp.getAddress());
-        buf.put(mNextIp.getAddress());
-        buf.put(mRelayIp.getAddress());
-        buf.put(mClientMac);
-        buf.position(buf.position() +
-                     (16 - mClientMac.length) // pad addr to 16 bytes
-                     + 64     // empty server host name (64 bytes)
-                     + 128);  // empty boot file name (128 bytes)
-        buf.putInt(0x63825363); // magic number
-        finishPacket(buf);
-
-        // round up to an even number of octets
-        if ((buf.position() & 1) == 1) {
-            buf.put((byte) 0);
-        }
-
-        // If an IP packet is being built, the IP & UDP checksums must be
-        // computed.
-        if (encap == ENCAP_L3) {
-            // fix UDP header: insert length
-            short udpLen = (short)(buf.position() - udpHeaderOffset);
-            buf.putShort(udpLengthOffset, udpLen);
-            // fix UDP header: checksum
-            // checksum for UDP at udpChecksumOffset
-            int udpSeed = 0;
-
-            // apply IPv4 pseudo-header.  Read IP address src and destination
-            // values from the IP header and accumulate checksum.
-            udpSeed += intAbs(buf.getShort(ipChecksumOffset + 2));
-            udpSeed += intAbs(buf.getShort(ipChecksumOffset + 4));
-            udpSeed += intAbs(buf.getShort(ipChecksumOffset + 6));
-            udpSeed += intAbs(buf.getShort(ipChecksumOffset + 8));
-
-            // accumulate extra data for the pseudo-header
-            udpSeed += IP_TYPE_UDP;
-            udpSeed += udpLen;
-            // and compute UDP checksum
-            buf.putShort(udpChecksumOffset, (short) checksum(buf, udpSeed,
-                                                             udpHeaderOffset,
-                                                             buf.position()));
-            // fix IP header: insert length
-            buf.putShort(ipLengthOffset, (short)buf.position());
-            // fixup IP-header checksum
-            buf.putShort(ipChecksumOffset,
-                         (short) checksum(buf, 0, 0, endIpHeader));
-        }
-    }
-
-    /**
-     * Converts a signed short value to an unsigned int value.  Needed
-     * because Java does not have unsigned types.
-     */
-    private int intAbs(short v) {
-        if (v < 0) {
-            int r = v + 65536;
-            return r;
-        } else {
-            return(v);
-        }
-    }
-
-    /**
-     * Performs an IP checksum (used in IP header and across UDP
-     * payload) on the specified portion of a ByteBuffer.  The seed
-     * allows the checksum to commence with a specified value.
-     */
-    private int checksum(ByteBuffer buf, int seed, int start, int end) {
-        int sum = seed;
-        int bufPosition = buf.position();
-
-        // set position of original ByteBuffer, so that the ShortBuffer
-        // will be correctly initialized
-        buf.position(start);
-        ShortBuffer shortBuf = buf.asShortBuffer();
-
-        // re-set ByteBuffer position
-        buf.position(bufPosition);
-
-        short[] shortArray = new short[(end - start) / 2];
-        shortBuf.get(shortArray);
-
-        for (short s : shortArray) {
-            sum += intAbs(s);
-        }
-
-        start += shortArray.length * 2;
-
-        // see if a singleton byte remains
-        if (end != start) {
-            short b = buf.get(start);
-
-            // make it unsigned
-            if (b < 0) {
-                b += 256;
-            }
-
-            sum += b * 256;
-        }
-
-        sum = ((sum >> 16) & 0xFFFF) + (sum & 0xFFFF);
-        sum = ((sum + ((sum >> 16) & 0xFFFF)) & 0xFFFF);
-        int negated = ~sum;
-        return intAbs((short) negated);
-    }
-
-    /**
-     * Adds an optional parameter containing a single byte value.
-     */
-    protected void addTlv(ByteBuffer buf, byte type, byte value) {
-        buf.put(type);
-        buf.put((byte) 1);
-        buf.put(value);
-    }
-
-    /**
-     * Adds an optional parameter containing an array of bytes.
-     */
-    protected void addTlv(ByteBuffer buf, byte type, byte[] payload) {
-        if (payload != null) {
-            buf.put(type);
-            buf.put((byte) payload.length);
-            buf.put(payload);
-        }
-    }
-
-    /**
-     * Adds an optional parameter containing an IP address.
-     */
-    protected void addTlv(ByteBuffer buf, byte type, InetAddress addr) {
-        if (addr != null) {
-            addTlv(buf, type, addr.getAddress());
-        }
-    }
-
-    /**
-     * Adds an optional parameter containing a list of IP addresses.
-     */
-    protected void addTlv(ByteBuffer buf, byte type, List<InetAddress> addrs) {
-        if (addrs != null && addrs.size() > 0) {
-            buf.put(type);
-            buf.put((byte)(4 * addrs.size()));
-
-            for (InetAddress addr : addrs) {
-                buf.put(addr.getAddress());
-            }
-        }
-    }
-
-    /**
-     * Adds an optional parameter containing a simple integer
-     */
-    protected void addTlv(ByteBuffer buf, byte type, Integer value) {
-        if (value != null) {
-            buf.put(type);
-            buf.put((byte) 4);
-            buf.putInt(value.intValue());
-        }
-    }
-
-    /**
-     * Adds an optional parameter containing and ASCII string.
-     */
-    protected void addTlv(ByteBuffer buf, byte type, String str) {
-        if (str != null) {
-            buf.put(type);
-            buf.put((byte) str.length());
-
-            for (int i = 0; i < str.length(); i++) {
-                buf.put((byte) str.charAt(i));
-            }
-        }
-    }
-
-    /**
-     * Adds the special end-of-optional-parameters indicator.
-     */
-    protected void addTlvEnd(ByteBuffer buf) {
-        buf.put((byte) 0xFF);
-    }
-
-    /**
-     * Converts a MAC from an array of octets to an ASCII string.
-     */
-    public static String macToString(byte[] mac) {
-        String macAddr = "";
-
-        for (int i = 0; i < mac.length; i++) {
-            String hexString = "0" + Integer.toHexString(mac[i]);
-
-            // substring operation grabs the last 2 digits: this
-            // allows signed bytes to be converted correctly.
-            macAddr += hexString.substring(hexString.length() - 2);
-
-            if (i != (mac.length - 1)) {
-                macAddr += ":";
-            }
-        }
-
-        return macAddr;
-    }
-
-    public String toString() {
-        String macAddr = macToString(mClientMac);
-
-        return macAddr;
-    }
-
-    /**
-     * Reads a four-octet value from a ByteBuffer and construct
-     * an IPv4 address from that value.
-     */
-    private static InetAddress readIpAddress(ByteBuffer packet) {
-        InetAddress result = null;
-        byte[] ipAddr = new byte[4];
-        packet.get(ipAddr);
-
-        try {
-            result = InetAddress.getByAddress(ipAddr);
-        } catch (UnknownHostException ex) {
-            // ipAddr is numeric, so this should not be
-            // triggered.  However, if it is, just nullify
-            result = null;
-        }
-
-        return result;
-    }
-
-    /**
-     * Reads a string of specified length from the buffer.
-     */
-    private static String readAsciiString(ByteBuffer buf, int byteCount) {
-        byte[] bytes = new byte[byteCount];
-        buf.get(bytes);
-        return new String(bytes, 0, bytes.length, StandardCharsets.US_ASCII);
-    }
-
-    /**
-     * Creates a concrete DhcpPacket from the supplied ByteBuffer.  The
-     * buffer may have an L2 encapsulation (which is the full EthernetII
-     * format starting with the source-address MAC) or an L3 encapsulation
-     * (which starts with the IP header).
-     * <br>
-     * A subset of the optional parameters are parsed and are stored
-     * in object fields.
-     */
-    public static DhcpPacket decodeFullPacket(ByteBuffer packet, int pktType)
-    {
-        // bootp parameters
-        int transactionId;
-        InetAddress clientIp;
-        InetAddress yourIp;
-        InetAddress nextIp;
-        InetAddress relayIp;
-        byte[] clientMac;
-        List<InetAddress> dnsServers = new ArrayList<InetAddress>();
-        InetAddress gateway = null; // aka router
-        Integer leaseTime = null;
-        InetAddress serverIdentifier = null;
-        InetAddress netMask = null;
-        String message = null;
-        String vendorId = null;
-        byte[] expectedParams = null;
-        String hostName = null;
-        String domainName = null;
-        InetAddress ipSrc = null;
-        InetAddress ipDst = null;
-        InetAddress bcAddr = null;
-        InetAddress requestedIp = null;
-
-        // dhcp options
-        byte dhcpType = (byte) 0xFF;
-
-        packet.order(ByteOrder.BIG_ENDIAN);
-
-        // check to see if we need to parse L2, IP, and UDP encaps
-        if (pktType == ENCAP_L2) {
-            // System.out.println("buffer len " + packet.limit());
-            byte[] l2dst = new byte[6];
-            byte[] l2src = new byte[6];
-
-            packet.get(l2dst);
-            packet.get(l2src);
-
-            short l2type = packet.getShort();
-
-            if (l2type != 0x0800)
-                return null;
-        }
-
-        if ((pktType == ENCAP_L2) || (pktType == ENCAP_L3)) {
-            // assume l2type is 0x0800, i.e. IP
-            byte ipType = packet.get();
-            // System.out.println("ipType is " + ipType);
-            byte ipDiffServicesField = packet.get();
-            short ipTotalLength = packet.getShort();
-            short ipIdentification = packet.getShort();
-            byte ipFlags = packet.get();
-            byte ipFragOffset = packet.get();
-            byte ipTTL = packet.get();
-            byte ipProto = packet.get();
-            short ipChksm = packet.getShort();
-
-            ipSrc = readIpAddress(packet);
-            ipDst = readIpAddress(packet);
-
-            if (ipProto != IP_TYPE_UDP) // UDP
-                return null;
-
-            // assume UDP
-            short udpSrcPort = packet.getShort();
-            short udpDstPort = packet.getShort();
-            short udpLen = packet.getShort();
-            short udpChkSum = packet.getShort();
-
-            if ((udpSrcPort != DHCP_SERVER) && (udpSrcPort != DHCP_CLIENT))
-                return null;
-        }
-
-        // assume bootp
-        byte type = packet.get();
-        byte hwType = packet.get();
-        byte addrLen = packet.get();
-        byte hops = packet.get();
-        transactionId = packet.getInt();
-        short elapsed = packet.getShort();
-        short bootpFlags = packet.getShort();
-        boolean broadcast = (bootpFlags & 0x8000) != 0;
-        byte[] ipv4addr = new byte[4];
-
-        try {
-            packet.get(ipv4addr);
-            clientIp = InetAddress.getByAddress(ipv4addr);
-            packet.get(ipv4addr);
-            yourIp = InetAddress.getByAddress(ipv4addr);
-            packet.get(ipv4addr);
-            nextIp = InetAddress.getByAddress(ipv4addr);
-            packet.get(ipv4addr);
-            relayIp = InetAddress.getByAddress(ipv4addr);
-        } catch (UnknownHostException ex) {
-            return null;
-        }
-
-        clientMac = new byte[addrLen];
-        packet.get(clientMac);
-
-        // skip over address padding (16 octets allocated)
-        packet.position(packet.position() + (16 - addrLen)
-                        + 64    // skip server host name (64 chars)
-                        + 128); // skip boot file name (128 chars)
-
-        int dhcpMagicCookie = packet.getInt();
-
-        if (dhcpMagicCookie !=  0x63825363)
-            return null;
-
-        // parse options
-        boolean notFinishedOptions = true;
-
-        while ((packet.position() < packet.limit()) && notFinishedOptions) {
-            byte optionType = packet.get();
-
-            if (optionType == (byte) 0xFF) {
-                notFinishedOptions = false;
-            } else {
-                byte optionLen = packet.get();
-                int expectedLen = 0;
-
-                switch(optionType) {
-                    case DHCP_SUBNET_MASK:
-                        netMask = readIpAddress(packet);
-                        expectedLen = 4;
-                        break;
-                    case DHCP_ROUTER:
-                        gateway = readIpAddress(packet);
-                        expectedLen = 4;
-                        break;
-                    case DHCP_DNS_SERVER:
-                        expectedLen = 0;
-
-                        for (expectedLen = 0; expectedLen < optionLen;
-                             expectedLen += 4) {
-                            dnsServers.add(readIpAddress(packet));
-                        }
-                        break;
-                    case DHCP_HOST_NAME:
-                        expectedLen = optionLen;
-                        hostName = readAsciiString(packet, optionLen);
-                        break;
-                    case DHCP_DOMAIN_NAME:
-                        expectedLen = optionLen;
-                        domainName = readAsciiString(packet, optionLen);
-                        break;
-                    case DHCP_BROADCAST_ADDRESS:
-                        bcAddr = readIpAddress(packet);
-                        expectedLen = 4;
-                        break;
-                    case DHCP_REQUESTED_IP:
-                        requestedIp = readIpAddress(packet);
-                        expectedLen = 4;
-                        break;
-                    case DHCP_LEASE_TIME:
-                        leaseTime = Integer.valueOf(packet.getInt());
-                        expectedLen = 4;
-                        break;
-                    case DHCP_MESSAGE_TYPE:
-                        dhcpType = packet.get();
-                        expectedLen = 1;
-                        break;
-                    case DHCP_SERVER_IDENTIFIER:
-                        serverIdentifier = readIpAddress(packet);
-                        expectedLen = 4;
-                        break;
-                    case DHCP_PARAMETER_LIST:
-                        expectedParams = new byte[optionLen];
-                        packet.get(expectedParams);
-                        expectedLen = optionLen;
-                        break;
-                    case DHCP_MESSAGE:
-                        expectedLen = optionLen;
-                        message = readAsciiString(packet, optionLen);
-                        break;
-                    case DHCP_VENDOR_CLASS_ID:
-                        expectedLen = optionLen;
-                        vendorId = readAsciiString(packet, optionLen);
-                        break;
-                    case DHCP_CLIENT_IDENTIFIER: { // Client identifier
-                        byte[] id = new byte[optionLen];
-                        packet.get(id);
-                        expectedLen = optionLen;
-                    } break;
-                    default:
-                        // ignore any other parameters
-                        for (int i = 0; i < optionLen; i++) {
-                            expectedLen++;
-                            byte throwaway = packet.get();
-                        }
-                }
-
-                if (expectedLen != optionLen) {
-                    return null;
-                }
-            }
-        }
-
-        DhcpPacket newPacket;
-
-        switch(dhcpType) {
-            case -1: return null;
-            case DHCP_MESSAGE_TYPE_DISCOVER:
-                newPacket = new DhcpDiscoverPacket(
-                    transactionId, clientMac, broadcast);
-                break;
-            case DHCP_MESSAGE_TYPE_OFFER:
-                newPacket = new DhcpOfferPacket(
-                    transactionId, broadcast, ipSrc, yourIp, clientMac);
-                break;
-            case DHCP_MESSAGE_TYPE_REQUEST:
-                newPacket = new DhcpRequestPacket(
-                    transactionId, clientIp, clientMac, broadcast);
-                break;
-            case DHCP_MESSAGE_TYPE_DECLINE:
-                newPacket = new DhcpDeclinePacket(
-                    transactionId, clientIp, yourIp, nextIp, relayIp,
-                    clientMac);
-                break;
-            case DHCP_MESSAGE_TYPE_ACK:
-                newPacket = new DhcpAckPacket(
-                    transactionId, broadcast, ipSrc, yourIp, clientMac);
-                break;
-            case DHCP_MESSAGE_TYPE_NAK:
-                newPacket = new DhcpNakPacket(
-                    transactionId, clientIp, yourIp, nextIp, relayIp,
-                    clientMac);
-                break;
-            case DHCP_MESSAGE_TYPE_INFORM:
-                newPacket = new DhcpInformPacket(
-                    transactionId, clientIp, yourIp, nextIp, relayIp,
-                    clientMac);
-                break;
-            default:
-                System.out.println("Unimplemented type: " + dhcpType);
-                return null;
-        }
-
-        newPacket.mBroadcastAddress = bcAddr;
-        newPacket.mDnsServers = dnsServers;
-        newPacket.mDomainName = domainName;
-        newPacket.mGateway = gateway;
-        newPacket.mHostName = hostName;
-        newPacket.mLeaseTime = leaseTime;
-        newPacket.mMessage = message;
-        newPacket.mRequestedIp = requestedIp;
-        newPacket.mRequestedParams = expectedParams;
-        newPacket.mServerIdentifier = serverIdentifier;
-        newPacket.mSubnetMask = netMask;
-        return newPacket;
-    }
-
-    /**
-     * Parse a packet from an array of bytes.
-     */
-    public static DhcpPacket decodeFullPacket(byte[] packet, int pktType)
-    {
-        ByteBuffer buffer = ByteBuffer.wrap(packet).order(ByteOrder.BIG_ENDIAN);
-        return decodeFullPacket(buffer, pktType);
-    }
-
-    /**
-     * Builds a DHCP-DISCOVER packet from the required specified
-     * parameters.
-     */
-    public static ByteBuffer buildDiscoverPacket(int encap, int transactionId,
-        byte[] clientMac, boolean broadcast, byte[] expectedParams) {
-        DhcpPacket pkt = new DhcpDiscoverPacket(
-            transactionId, clientMac, broadcast);
-        pkt.mRequestedParams = expectedParams;
-        return pkt.buildPacket(encap, DHCP_SERVER, DHCP_CLIENT);
-    }
-
-    /**
-     * Builds a DHCP-OFFER packet from the required specified
-     * parameters.
-     */
-    public static ByteBuffer buildOfferPacket(int encap, int transactionId,
-        boolean broadcast, InetAddress serverIpAddr, InetAddress clientIpAddr,
-        byte[] mac, Integer timeout, InetAddress netMask, InetAddress bcAddr,
-        InetAddress gateway, List<InetAddress> dnsServers,
-        InetAddress dhcpServerIdentifier, String domainName) {
-        DhcpPacket pkt = new DhcpOfferPacket(
-            transactionId, broadcast, serverIpAddr, clientIpAddr, mac);
-        pkt.mGateway = gateway;
-        pkt.mDnsServers = dnsServers;
-        pkt.mLeaseTime = timeout;
-        pkt.mDomainName = domainName;
-        pkt.mServerIdentifier = dhcpServerIdentifier;
-        pkt.mSubnetMask = netMask;
-        pkt.mBroadcastAddress = bcAddr;
-        return pkt.buildPacket(encap, DHCP_CLIENT, DHCP_SERVER);
-    }
-
-    /**
-     * Builds a DHCP-ACK packet from the required specified parameters.
-     */
-    public static ByteBuffer buildAckPacket(int encap, int transactionId,
-        boolean broadcast, InetAddress serverIpAddr, InetAddress clientIpAddr,
-        byte[] mac, Integer timeout, InetAddress netMask, InetAddress bcAddr,
-        InetAddress gateway, List<InetAddress> dnsServers,
-        InetAddress dhcpServerIdentifier, String domainName) {
-        DhcpPacket pkt = new DhcpAckPacket(
-            transactionId, broadcast, serverIpAddr, clientIpAddr, mac);
-        pkt.mGateway = gateway;
-        pkt.mDnsServers = dnsServers;
-        pkt.mLeaseTime = timeout;
-        pkt.mDomainName = domainName;
-        pkt.mSubnetMask = netMask;
-        pkt.mServerIdentifier = dhcpServerIdentifier;
-        pkt.mBroadcastAddress = bcAddr;
-        return pkt.buildPacket(encap, DHCP_CLIENT, DHCP_SERVER);
-    }
-
-    /**
-     * Builds a DHCP-NAK packet from the required specified parameters.
-     */
-    public static ByteBuffer buildNakPacket(int encap, int transactionId,
-        InetAddress serverIpAddr, InetAddress clientIpAddr, byte[] mac) {
-        DhcpPacket pkt = new DhcpNakPacket(transactionId, clientIpAddr,
-            serverIpAddr, serverIpAddr, serverIpAddr, mac);
-        pkt.mMessage = "requested address not available";
-        pkt.mRequestedIp = clientIpAddr;
-        return pkt.buildPacket(encap, DHCP_CLIENT, DHCP_SERVER);
-    }
-
-    /**
-     * Builds a DHCP-REQUEST packet from the required specified parameters.
-     */
-    public static ByteBuffer buildRequestPacket(int encap,
-        int transactionId, InetAddress clientIp, boolean broadcast,
-        byte[] clientMac, InetAddress requestedIpAddress,
-        InetAddress serverIdentifier, byte[] requestedParams, String hostName) {
-        DhcpPacket pkt = new DhcpRequestPacket(transactionId, clientIp,
-            clientMac, broadcast);
-        pkt.mRequestedIp = requestedIpAddress;
-        pkt.mServerIdentifier = serverIdentifier;
-        pkt.mHostName = hostName;
-        pkt.mRequestedParams = requestedParams;
-        ByteBuffer result = pkt.buildPacket(encap, DHCP_SERVER, DHCP_CLIENT);
-        return result;
-    }
-}
diff --git a/core/java/android/net/dhcp/DhcpStateMachine.java b/core/java/android/net/dhcp/DhcpStateMachine.java
deleted file mode 100644
index bc9a798..0000000
--- a/core/java/android/net/dhcp/DhcpStateMachine.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2010 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.net.dhcp;
-
-import java.net.InetAddress;
-import java.util.List;
-
-/**
- * This class defines the "next steps" which occur after a given DHCP
- * packet has been received.
- */
-interface DhcpStateMachine {
-    /**
-     * Signals that an offer packet has been received with the specified
-     * parameters.
-     */
-    public void onOfferReceived(boolean broadcast, int transactionId,
-        byte[] myMac, InetAddress offeredIpAddress,
-        InetAddress serverIpAddress);
-
-    /**
-     * Signals that a NAK packet has been received.
-     */
-    public void onNakReceived();
-
-    /**
-     * Signals that the final ACK has been received from the server.
-     */
-    public void onAckReceived(InetAddress myIpAddress, InetAddress myNetMask,
-        InetAddress myGateway, List<InetAddress> myDnsServers,
-        InetAddress myDhcpServer, int leaseTime);
-
-    /**
-     * Signals that a client's DISCOVER packet has been received with the
-     * specified parameters.
-     */
-    public void onDiscoverReceived(boolean broadcast, int transactionId,
-        byte[] clientMac, byte[] requestedParameterList);
-
-    /**
-     * Signals that a client's REQUEST packet has been received with the
-     * specified parameters.
-     */
-    public void onRequestReceived(boolean broadcast, int transactionId,
-        byte[] clientMac, InetAddress requestedIp, byte[] requestedParams,
-        String clientHostName);
-
-    /**
-     * Signals that a client's INFORM packet has been received with the
-     * specified parameters.
-     */
-    public void onInformReceived(int transactionId, byte[] clientMac,
-        InetAddress preassignedIp, byte[] requestedParams);
-
-    /**
-     * Signals that a client's DECLINE packet has been received with the
-     * specified parameters.
-     */
-    public void onDeclineReceived(byte[] clientMac, InetAddress declinedIp);
-}
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index f023df7..dc8e76f 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -33,9 +33,12 @@
 import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.DataUsageFeedback;
+import android.telecom.PhoneAccount;
 import android.telecom.PhoneAccountHandle;
+import android.telecom.TelecomManager;
 import android.telephony.PhoneNumberUtils;
 import android.text.TextUtils;
+import android.util.Log;
 
 import com.android.internal.telephony.CallerInfo;
 import com.android.internal.telephony.PhoneConstants;
@@ -46,6 +49,8 @@
  * The CallLog provider contains information about placed and received calls.
  */
 public class CallLog {
+    private static final String LOG_TAG = "CallLog";
+
     public static final String AUTHORITY = "call_log";
 
     /**
@@ -336,24 +341,44 @@
         // that was encoded into call log databases.
 
         /**
-         * The component name of the account in string form.
+         * The component name of the account used to place or receive the call; in string form.
          * <P>Type: TEXT</P>
          */
         public static final String PHONE_ACCOUNT_COMPONENT_NAME = "subscription_component_name";
 
         /**
-         * The identifier of a account that is unique to a specified component.
+         * The identifier for the account used to place or receive the call.
          * <P>Type: TEXT</P>
          */
         public static final String PHONE_ACCOUNT_ID = "subscription_id";
 
         /**
-         * The identifier of a account that is unique to a specified component. Equivalent value
-         * to {@link #PHONE_ACCOUNT_ID}. For ContactsProvider internal use only.
+         * The address associated with the account used to place or receive the call; in string
+         * form. For SIM-based calls, this is the user's own phone number.
+         * <P>Type: TEXT</P>
+         *
+         * @hide
+         */
+        public static final String PHONE_ACCOUNT_ADDRESS = "phone_account_address";
+
+        /**
+         * Indicates that the entry will be hidden from all queries until the associated
+         * {@link android.telecom.PhoneAccount} is registered with the system.
          * <P>Type: INTEGER</P>
          *
          * @hide
          */
+        public static final String PHONE_ACCOUNT_HIDDEN = "phone_account_hidden";
+
+        /**
+         * The subscription ID used to place this call.  This is no longer used and has been
+         * replaced with PHONE_ACCOUNT_COMPONENT_NAME/PHONE_ACCOUNT_ID.
+         * For ContactsProvider internal use only.
+         * <P>Type: INTEGER</P>
+         *
+         * @Deprecated
+         * @hide
+         */
         public static final String SUB_ID = "sub_id";
 
         /**
@@ -422,6 +447,22 @@
             final ContentResolver resolver = context.getContentResolver();
             int numberPresentation = PRESENTATION_ALLOWED;
 
+            TelecomManager tm = null;
+            try {
+                tm = TelecomManager.from(context);
+            } catch (UnsupportedOperationException e) {}
+
+            String accountAddress = null;
+            if (tm != null && accountHandle != null) {
+                PhoneAccount account = tm.getPhoneAccount(accountHandle);
+                if (account != null) {
+                    Uri address = account.getSubscriptionAddress();
+                    if (address != null) {
+                        accountAddress = address.getSchemeSpecificPart();
+                    }
+                }
+            }
+
             // Remap network specified number presentation types
             // PhoneConstants.PRESENTATION_xxx to calllog number presentation types
             // Calls.PRESENTATION_xxx, in order to insulate the persistent calllog
@@ -463,6 +504,7 @@
             }
             values.put(PHONE_ACCOUNT_COMPONENT_NAME, accountComponentString);
             values.put(PHONE_ACCOUNT_ID, accountId);
+            values.put(PHONE_ACCOUNT_ADDRESS, accountAddress);
             values.put(NEW, Integer.valueOf(1));
 
             if (callType == MISSED_TYPE) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d773d4a..7819c73 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2395,8 +2395,6 @@
          * It was about AudioManager's setting and thus affected all the applications which
          * relied on the setting, while this is purely about the vibration setting for incoming
          * calls.
-         *
-         * @hide
          */
         public static final String VIBRATE_WHEN_RINGING = "vibrate_when_ringing";
 
@@ -2411,7 +2409,6 @@
          * DTMF tone type played by the dialer when dialing.
          *                 0 = Normal
          *                 1 = Long
-         * @hide
          */
         public static final String DTMF_TONE_TYPE_WHEN_DIALING = "dtmf_tone_type";
 
@@ -5436,6 +5433,14 @@
                "hdmi_control_auto_device_off_enabled";
 
        /**
+        * Whether to use the DHCP client from Lollipop and earlier instead of the newer Android DHCP
+        * client.
+        * (0 = false, 1 = true)
+        * @hide
+        */
+       public static final String LEGACY_DHCP_CLIENT = "legacy_dhcp_client";
+
+       /**
         * Whether TV will switch to MHL port when a mobile device is plugged in.
         * (0 = false, 1 = true)
         * @hide
@@ -5830,6 +5835,14 @@
                 "wifi_scan_always_enabled";
 
        /**
+        * Settings to allow BLE scans to be enabled even when Bluetooth is turned off for
+        * connectivity.
+        * @hide
+        */
+       public static final String BLE_SCAN_ALWAYS_AVAILABLE =
+               "ble_scan_always_enabled";
+
+       /**
         * Used to save the Wifi_ON state prior to tethering.
         * This state will be checked to restore Wifi after
         * the user turns off tethering.
@@ -6293,6 +6306,9 @@
         /** {@hide} */
         public static final String
                 BLUETOOTH_MAP_PRIORITY_PREFIX = "bluetooth_map_priority_";
+        /** {@hide} */
+        public static final String
+                BLUETOOTH_SAP_PRIORITY_PREFIX = "bluetooth_sap_priority_";
 
         /**
          * Get the key that retrieves a bluetooth headset's priority.
@@ -6325,6 +6341,15 @@
         public static final String getBluetoothMapPriorityKey(String address) {
             return BLUETOOTH_MAP_PRIORITY_PREFIX + address.toUpperCase(Locale.ROOT);
         }
+
+        /**
+         * Get the key that retrieves a bluetooth map priority.
+         * @hide
+         */
+        public static final String getBluetoothSapPriorityKey(String address) {
+            return BLUETOOTH_SAP_PRIORITY_PREFIX + address.toUpperCase(Locale.ROOT);
+        }
+
         /**
          * Scaling factor for normal window animations. Setting to 0 will
          * disable window animations.
@@ -6394,7 +6419,6 @@
         /**
          * Setting to 1 will hide carrier network settings.
          * Default is 0.
-         * @hide
          */
         public static final String HIDE_CARRIER_NETWORK_SETTINGS =
                 "hide_carrier_network_settings";
diff --git a/core/java/android/provider/VoicemailContract.java b/core/java/android/provider/VoicemailContract.java
index d71ad03..92119b9 100644
--- a/core/java/android/provider/VoicemailContract.java
+++ b/core/java/android/provider/VoicemailContract.java
@@ -19,10 +19,18 @@
 import android.Manifest;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
 import android.content.Intent;
 import android.database.ContentObserver;
+import android.database.Cursor;
 import android.net.Uri;
 import android.provider.CallLog.Calls;
+import android.telecom.PhoneAccountHandle;
+import android.telecom.Voicemail;
+
+import java.util.List;
 
 /**
  * The contract between the voicemail provider and applications. Contains
@@ -199,13 +207,106 @@
          */
         public static final String _DATA = "_data";
 
+        // Note: PHONE_ACCOUNT_* constant values are "subscription_*" due to a historic naming
+        // that was encoded into call log databases.
+
+        /**
+         * The component name of the account in string form.
+         * <P>Type: TEXT</P>
+         */
+        public static final String PHONE_ACCOUNT_COMPONENT_NAME = "subscription_component_name";
+
+        /**
+         * The identifier of a account that is unique to a specified component.
+         * <P>Type: TEXT</P>
+         */
+        public static final String PHONE_ACCOUNT_ID = "subscription_id";
+
+        /**
+         * Flag used to indicate that local, unsynced changes are present.
+         * Currently, this is used to indicate that the voicemail was read or deleted.
+         * The value will be 1 if dirty is true, 0 if false.
+         * <P>Type: INTEGER (boolean)</P>
+         */
+        public static final String DIRTY = "dirty";
+
+        /**
+         * Flag used to indicate that the voicemail was deleted but not synced to the server.
+         * A deleted row should be ignored.
+         * The value will be 1 if deleted is true, 0 if false.
+         * <P>Type: INTEGER (boolean)</P>
+         */
+        public static final String DELETED = "deleted";
+
         /**
          * A convenience method to build voicemail URI specific to a source package by appending
          * {@link VoicemailContract#PARAM_KEY_SOURCE_PACKAGE} param to the base URI.
          */
         public static Uri buildSourceUri(String packageName) {
             return Voicemails.CONTENT_URI.buildUpon()
-                    .appendQueryParameter(PARAM_KEY_SOURCE_PACKAGE, packageName).build();
+                    .appendQueryParameter(PARAM_KEY_SOURCE_PACKAGE, packageName)
+                    .build();
+        }
+
+        /**
+         * Inserts a new voicemail into the voicemail content provider.
+         *
+         * @param context The context of the app doing the inserting
+         * @param voicemail Data to be inserted
+         * @return {@link Uri} of the newly inserted {@link Voicemail}
+         *
+         * @hide
+         */
+        public static Uri insert(Context context, Voicemail voicemail) {
+            ContentResolver contentResolver = context.getContentResolver();
+            ContentValues contentValues = getContentValues(voicemail);
+            return contentResolver.insert(Voicemails.CONTENT_URI, contentValues);
+        }
+
+        /**
+         * Inserts a list of voicemails into the voicemail content provider.
+         *
+         * @param context The context of the app doing the inserting
+         * @param voicemails Data to be inserted
+         * @return the number of voicemails inserted
+         *
+         * @hide
+         */
+        public static int insert(Context context, List<Voicemail> voicemails) {
+            ContentResolver contentResolver = context.getContentResolver();
+            int count = voicemails.size();
+            for (int i = 0; i < count; i++) {
+                ContentValues contentValues = getContentValues(voicemails.get(i));
+                contentResolver.insert(Voicemails.CONTENT_URI, contentValues);
+            }
+            return count;
+        }
+
+        /**
+         * Clears all voicemails accessible to this voicemail content provider for the calling
+         * package. By default, a package only has permission to delete voicemails it inserted.
+         *
+         * @return the number of voicemails deleted
+         *
+         * @hide
+         */
+        public static int deleteAll(Context context) {
+            return context.getContentResolver().delete(
+                    buildSourceUri(context.getPackageName()), "", new String[0]);
+        }
+
+        /**
+         * Maps structured {@link Voicemail} to {@link ContentValues} in content provider.
+         */
+        private static ContentValues getContentValues(Voicemail voicemail) {
+            ContentValues contentValues = new ContentValues();
+            contentValues.put(Voicemails.DATE, String.valueOf(voicemail.getTimestampMillis()));
+            contentValues.put(Voicemails.NUMBER, voicemail.getNumber());
+            contentValues.put(Voicemails.DURATION, String.valueOf(voicemail.getDuration()));
+            contentValues.put(Voicemails.SOURCE_PACKAGE, voicemail.getSourcePackage());
+            contentValues.put(Voicemails.SOURCE_DATA, voicemail.getSourceData());
+            contentValues.put(Voicemails.IS_READ, voicemail.isRead() ? 1 : 0);
+            return contentValues;
         }
     }
 
@@ -222,10 +323,27 @@
         private Status() {
         }
         /**
-         * The package name of the voicemail source. There can only be a one entry per source.
+         * The package name of the voicemail source. There can only be a one entry per account
+         * per source.
          * <P>Type: TEXT</P>
          */
         public static final String SOURCE_PACKAGE = SOURCE_PACKAGE_FIELD;
+
+        // Note: Multiple entries may exist for a single source if they are differentiated by the
+        // PHONE_ACCOUNT_* fields.
+
+        /**
+         * The component name of the account in string form.
+         * <P>Type: TEXT</P>
+         */
+        public static final String PHONE_ACCOUNT_COMPONENT_NAME = "phone_account_component_name";
+
+        /**
+         * The identifier of a account that is unique to a specified component.
+         * <P>Type: TEXT</P>
+         */
+        public static final String PHONE_ACCOUNT_ID = "phone_account_id";
+
         /**
          * The URI to call to invoke source specific voicemail settings screen. On a user request
          * to setup voicemail an intent with action VIEW with this URI will be fired by the system.
@@ -318,5 +436,52 @@
             return Status.CONTENT_URI.buildUpon()
                     .appendQueryParameter(PARAM_KEY_SOURCE_PACKAGE, packageName).build();
         }
+
+        /**
+         * A helper method to set the status of a voicemail source.
+         *
+         * @param context The context from the package calling the method. This will be the source.
+         * @param accountHandle The handle for the account the source is associated with.
+         * @param configurationState See {@link Status#CONFIGURATION_STATE}
+         * @param dataChannelState See {@link Status#DATA_CHANNEL_STATE}
+         * @param notificationChannelState See {@link Status#NOTIFICATION_CHANNEL_STATE}
+         *
+         * @hide
+         */
+        public static void setStatus(Context context, PhoneAccountHandle accountHandle,
+                int configurationState, int dataChannelState, int notificationChannelState) {
+            ContentResolver contentResolver = context.getContentResolver();
+            Uri statusUri = buildSourceUri(context.getPackageName());
+            ContentValues values = new ContentValues();
+            values.put(Status.PHONE_ACCOUNT_COMPONENT_NAME,
+                    accountHandle.getComponentName().toString());
+            values.put(Status.PHONE_ACCOUNT_ID, accountHandle.getId());
+            values.put(Status.CONFIGURATION_STATE, configurationState);
+            values.put(Status.DATA_CHANNEL_STATE, dataChannelState);
+            values.put(Status.NOTIFICATION_CHANNEL_STATE, notificationChannelState);
+
+            if (isStatusPresent(contentResolver, statusUri)) {
+                contentResolver.update(statusUri, values, null, null);
+            } else {
+                contentResolver.insert(statusUri, values);
+            }
+        }
+
+        /**
+         * Determines if a voicemail source exists in the status table.
+         *
+         * @param contentResolver A content resolver constructed from the appropriate context.
+         * @param statusUri The content uri for the source.
+         * @return {@code true} if a status entry for this source exists
+         */
+        private static boolean isStatusPresent(ContentResolver contentResolver, Uri statusUri) {
+            Cursor cursor = null;
+            try {
+                cursor = contentResolver.query(statusUri, null, null, null, null);
+                return cursor != null && cursor.getCount() != 0;
+            } finally {
+                if (cursor != null) cursor.close();
+            }
+        }
     }
 }
diff --git a/core/java/android/service/carrier/CarrierConfigService.java b/core/java/android/service/carrier/CarrierConfigService.java
new file mode 100644
index 0000000..1880d16
--- /dev/null
+++ b/core/java/android/service/carrier/CarrierConfigService.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2015 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.service.carrier;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+
+/**
+ * A service that sets carrier configuration for telephony services.
+ * <p>
+ * To extend this class, you must declare the service in your manifest file to require the
+ * {@link android.Manifest.permission#BIND_CARRIER_CONFIG_SERVICE} permission and include an intent
+ * filter with the {@link #SERVICE_INTERFACE} action. For example:
+ * </p>
+ *
+ * <pre>{@code
+ * <service android:name=".MyCarrierConfigService"
+ *       android:label="@string/service_name"
+ *       android:permission="android.permission.BIND_CARRIER_CONFIG_SERVICE">
+ *  <intent-filter>
+ *      <action android:name="android.service.carrier.CarrierConfigService" />
+ *  </intent-filter>
+ * </service>
+ * }</pre>
+ */
+public abstract class CarrierConfigService extends Service {
+
+    public static final String SERVICE_INTERFACE = "android.service.carrier.CarrierConfigService";
+
+    private final ICarrierConfigService.Stub mStubWrapper;
+
+    public CarrierConfigService() {
+        mStubWrapper = new ICarrierConfigServiceWrapper();
+    }
+
+    /**
+     * Override this method to set carrier configuration.
+     * <p>
+     * This method will be called by telephony services to get carrier-specific configuration
+     * values. The returned config will be saved by the system until,
+     * <ol>
+     * <li>The carrier app package is updated, or</li>
+     * <li>The carrier app requests a reload with
+     * {@link android.telephony.CarrierConfigManager#reloadCarrierConfigForSubId
+     * reloadCarrierConfigForSubId}.</li>
+     * </ol>
+     * This method can be called after a SIM card loads, which may be before or after boot.
+     * </p>
+     * <p>
+     * This method should not block for a long time. If expensive operations (e.g. network access)
+     * are required, this method can schedule the work and return null. Then, use
+     * {@link android.telephony.CarrierConfigManager#reloadCarrierConfigForSubId
+     * reloadCarrierConfigForSubId} to trigger a reload when the config is ready.
+     * </p>
+     * <p>
+     * Implementations should use the keys defined in {@link android.telephony.CarrierConfigManager
+     * CarrierConfigManager}. Any configuration values not set in the returned {@link Bundle} may be
+     * overridden by the system's default configuration service.
+     * </p>
+     *
+     * @param id contains details about the current carrier that can be used do decide what
+     *            configuration values to return.
+     * @return a {@link Bundle} object containing the configuration or null if default values should
+     *         be used.
+     */
+    public abstract Bundle onLoadConfig(CarrierIdentifier id);
+
+    /** @hide */
+    @Override
+    public final IBinder onBind(Intent intent) {
+        if (!SERVICE_INTERFACE.equals(intent.getAction())) {
+            return null;
+        }
+        return mStubWrapper;
+    }
+
+    /**
+     * A wrapper around ICarrierConfigService that forwards calls to implementations of
+     * {@link CarrierConfigService}.
+     *
+     * @hide
+     */
+    private class ICarrierConfigServiceWrapper extends ICarrierConfigService.Stub {
+
+        @Override
+        public Bundle getCarrierConfig(CarrierIdentifier id) {
+            return CarrierConfigService.this.onLoadConfig(id);
+        }
+    }
+}
diff --git a/core/java/android/service/carrier/CarrierIdentifier.aidl b/core/java/android/service/carrier/CarrierIdentifier.aidl
new file mode 100644
index 0000000..48b1398
--- /dev/null
+++ b/core/java/android/service/carrier/CarrierIdentifier.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2015, 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.service.carrier;
+
+parcelable CarrierIdentifier;
diff --git a/core/java/android/service/carrier/CarrierIdentifier.java b/core/java/android/service/carrier/CarrierIdentifier.java
new file mode 100644
index 0000000..495fea6
--- /dev/null
+++ b/core/java/android/service/carrier/CarrierIdentifier.java
@@ -0,0 +1,117 @@
+/**
+ * Copyright (c) 2015, 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.service.carrier;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Used to pass info to CarrierConfigService implementations so they can decide what values to
+ * return.
+ */
+public class CarrierIdentifier implements Parcelable {
+
+    /** Used to create a {@link CarrierIdentifier} from a {@link Parcel}. */
+    public static final Creator<CarrierIdentifier> CREATOR = new Creator<CarrierIdentifier>() {
+            @Override
+        public CarrierIdentifier createFromParcel(Parcel parcel) {
+            return new CarrierIdentifier(parcel);
+        }
+
+            @Override
+        public CarrierIdentifier[] newArray(int i) {
+            return new CarrierIdentifier[i];
+        }
+    };
+
+    private String mMcc;
+    private String mMnc;
+    private String mSpn;
+    private String mImsi;
+    private String mGid1;
+    private String mGid2;
+
+    public CarrierIdentifier(String mcc, String mnc, String spn, String imsi, String gid1,
+            String gid2) {
+        mMcc = mcc;
+        mMnc = mnc;
+        mSpn = spn;
+        mImsi = imsi;
+        mGid1 = gid1;
+        mGid2 = gid2;
+    }
+
+    /** @hide */
+    public CarrierIdentifier(Parcel parcel) {
+        readFromParcel(parcel);
+    }
+
+    /** Get the mobile country code. */
+    public String getMcc() {
+        return mMcc;
+    }
+
+    /** Get the mobile network code. */
+    public String getMnc() {
+        return mMnc;
+    }
+
+    /** Get the service provider name. */
+    public String getSpn() {
+        return mSpn;
+    }
+
+    /** Get the international mobile subscriber identity. */
+    public String getImsi() {
+        return mImsi;
+    }
+
+    /** Get the group identifier level 1. */
+    public String getGid1() {
+        return mGid1;
+    }
+
+    /** Get the group identifier level 2. */
+    public String getGid2() {
+        return mGid2;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeString(mMcc);
+        out.writeString(mMnc);
+        out.writeString(mSpn);
+        out.writeString(mImsi);
+        out.writeString(mGid1);
+        out.writeString(mGid2);
+    }
+
+    /** @hide */
+    public void readFromParcel(Parcel in) {
+        mMcc = in.readString();
+        mMnc = in.readString();
+        mSpn = in.readString();
+        mImsi = in.readString();
+        mGid1 = in.readString();
+        mGid2 = in.readString();
+    }
+}
diff --git a/core/java/android/service/carrier/ICarrierConfigService.aidl b/core/java/android/service/carrier/ICarrierConfigService.aidl
new file mode 100644
index 0000000..d8390b6
--- /dev/null
+++ b/core/java/android/service/carrier/ICarrierConfigService.aidl
@@ -0,0 +1,32 @@
+/**
+ * Copyright (c) 2015, 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.service.carrier;
+
+import android.os.Bundle;
+import android.service.carrier.CarrierIdentifier;
+
+/**
+ * Service used to get carrier config from carrier apps.
+ *
+ * @see android.service.carrier.CarrierConfigService
+ * @hide
+ */
+interface ICarrierConfigService {
+
+    /** @see android.service.carrier.CarrierConfigService#onLoadConfig */
+    Bundle getCarrierConfig(in CarrierIdentifier id);
+}
\ No newline at end of file
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index 59ec058..ad34f02 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -723,6 +723,12 @@
             mSurface.release();
         }
         mSurface = surfaceTexture;
+
+        // If the view is visible, update the listener in the new surface to use
+        // the existing listener in the view.
+        if (((mViewFlags & VISIBILITY_MASK) == VISIBLE)) {
+            mSurface.setOnFrameAvailableListener(mUpdateListener, mAttachInfo.mHandler);
+        }
         mUpdateSurface = true;
         invalidateParentIfNeeded();
     }
diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java
index d9ebc25..a106f48 100644
--- a/core/java/com/android/internal/util/Protocol.java
+++ b/core/java/com/android/internal/util/Protocol.java
@@ -52,6 +52,7 @@
     public static final int BASE_WIFI_RTT_SERVICE                                   = 0x00027300;
     public static final int BASE_WIFI_PASSPOINT_MANAGER                             = 0x00028000;
     public static final int BASE_WIFI_PASSPOINT_SERVICE                             = 0x00028100;
+    public static final int BASE_WIFI_LOGGER                                        = 0x00028300;
     public static final int BASE_DHCP                                               = 0x00030000;
     public static final int BASE_DATA_CONNECTION                                    = 0x00040000;
     public static final int BASE_DATA_CONNECTION_AC                                 = 0x00041000;
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 8b9f574..52da137 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -24,6 +24,14 @@
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/Log.h>
 #include <arpa/inet.h>
+#include <net/if.h>
+#include <linux/filter.h>
+#include <linux/if.h>
+#include <linux/if_ether.h>
+#include <linux/if_packet.h>
+#include <net/if_ether.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
 #include <cutils/properties.h>
 
 extern "C" {
@@ -31,27 +39,18 @@
 int ifc_disable(const char *ifname);
 int ifc_reset_connections(const char *ifname, int reset_mask);
 
-int dhcp_do_request(const char * const ifname,
-                    const char *ipaddr,
-                    const char *gateway,
-                    uint32_t *prefixLength,
-                    const char *dns[],
-                    const char *server,
-                    uint32_t *lease,
-                    const char *vendorInfo,
-                    const char *domains,
-                    const char *mtu);
-
-int dhcp_do_request_renew(const char * const ifname,
-                    const char *ipaddr,
-                    const char *gateway,
-                    uint32_t *prefixLength,
-                    const char *dns[],
-                    const char *server,
-                    uint32_t *lease,
-                    const char *vendorInfo,
-                    const char *domains,
-                    const char *mtu);
+int dhcp_start(const char * const ifname);
+int dhcp_start_renew(const char * const ifname);
+int dhcp_get_results(const char * const ifname,
+                     const char *ipaddr,
+                     const char *gateway,
+                     uint32_t *prefixLength,
+                     const char *dns[],
+                     const char *server,
+                     uint32_t *lease,
+                     const char *vendorInfo,
+                     const char *domains,
+                     const char *mtu);
 
 int dhcp_stop(const char *ifname);
 int dhcp_release_lease(const char *ifname);
@@ -62,6 +61,8 @@
 
 namespace android {
 
+static const uint16_t kDhcpClientPort = 68;
+
 /*
  * The following remembers the jfieldID's of the fields
  * of the DhcpInfo Java object, so that we don't have
@@ -93,8 +94,8 @@
     return (jint)result;
 }
 
-static jboolean android_net_utils_runDhcpCommon(JNIEnv* env, jobject clazz, jstring ifname,
-        jobject dhcpResults, bool renew)
+static jboolean android_net_utils_getDhcpResults(JNIEnv* env, jobject clazz, jstring ifname,
+        jobject dhcpResults)
 {
     int result;
     char  ipaddr[PROPERTY_VALUE_MAX];
@@ -114,15 +115,10 @@
     const char *nameStr = env->GetStringUTFChars(ifname, NULL);
     if (nameStr == NULL) return (jboolean)false;
 
-    if (renew) {
-        result = ::dhcp_do_request_renew(nameStr, ipaddr, gateway, &prefixLength,
-                dns, server, &lease, vendorInfo, domains, mtu);
-    } else {
-        result = ::dhcp_do_request(nameStr, ipaddr, gateway, &prefixLength,
-                dns, server, &lease, vendorInfo, domains, mtu);
-    }
+    result = ::dhcp_get_results(nameStr, ipaddr, gateway, &prefixLength,
+            dns, server, &lease, vendorInfo, domains, mtu);
     if (result != 0) {
-        ALOGD("dhcp_do_request failed : %s (%s)", nameStr, renew ? "renew" : "new");
+        ALOGD("dhcp_get_results failed : %s (%s)", nameStr, ::dhcp_get_errmsg());
     }
 
     env->ReleaseStringUTFChars(ifname, nameStr);
@@ -182,19 +178,28 @@
     return (jboolean)(result == 0);
 }
 
-
-static jboolean android_net_utils_runDhcp(JNIEnv* env, jobject clazz, jstring ifname, jobject info)
+static jboolean android_net_utils_startDhcp(JNIEnv* env, jobject clazz, jstring ifname)
 {
-    return android_net_utils_runDhcpCommon(env, clazz, ifname, info, false);
+    const char *nameStr = env->GetStringUTFChars(ifname, NULL);
+    if (nameStr == NULL) return (jboolean)false;
+    if (::dhcp_start(nameStr) != 0) {
+        ALOGD("dhcp_start failed : %s", nameStr);
+        return (jboolean)false;
+    }
+    return (jboolean)true;
 }
 
-static jboolean android_net_utils_runDhcpRenew(JNIEnv* env, jobject clazz, jstring ifname,
-        jobject info)
+static jboolean android_net_utils_startDhcpRenew(JNIEnv* env, jobject clazz, jstring ifname)
 {
-    return android_net_utils_runDhcpCommon(env, clazz, ifname, info, true);
+    const char *nameStr = env->GetStringUTFChars(ifname, NULL);
+    if (nameStr == NULL) return (jboolean)false;
+    if (::dhcp_start_renew(nameStr) != 0) {
+        ALOGD("dhcp_start_renew failed : %s", nameStr);
+        return (jboolean)false;
+    }
+    return (jboolean)true;
 }
 
-
 static jboolean android_net_utils_stopDhcp(JNIEnv* env, jobject clazz, jstring ifname)
 {
     int result;
@@ -220,12 +225,50 @@
     return env->NewStringUTF(::dhcp_get_errmsg());
 }
 
+static void android_net_utils_attachDhcpFilter(JNIEnv *env, jobject clazz, jobject javaFd)
+{
+    int fd = jniGetFDFromFileDescriptor(env, javaFd);
+    uint32_t ip_offset = sizeof(ether_header);
+    uint32_t proto_offset = ip_offset + offsetof(iphdr, protocol);
+    uint32_t flags_offset = ip_offset +  offsetof(iphdr, frag_off);
+    uint32_t dport_indirect_offset = ip_offset + offsetof(udphdr, dest);
+    struct sock_filter filter_code[] = {
+        // Check the protocol is UDP.
+        BPF_STMT(BPF_LD  | BPF_B   | BPF_ABS,  proto_offset),
+        BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K,    IPPROTO_UDP, 0, 6),
+
+        // Check this is not a fragment.
+        BPF_STMT(BPF_LD  | BPF_H    | BPF_ABS, flags_offset),
+        BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K,   0x1fff, 4, 0),
+
+        // Get the IP header length.
+        BPF_STMT(BPF_LDX | BPF_B    | BPF_MSH, ip_offset),
+
+        // Check the destination port.
+        BPF_STMT(BPF_LD  | BPF_H    | BPF_IND, dport_indirect_offset),
+        BPF_JUMP(BPF_JMP | BPF_JEQ  | BPF_K,   kDhcpClientPort, 0, 1),
+
+        // Accept or reject.
+        BPF_STMT(BPF_RET | BPF_K,              0xffff),
+        BPF_STMT(BPF_RET | BPF_K,              0)
+    };
+    struct sock_fprog filter = {
+        sizeof(filter_code) / sizeof(filter_code[0]),
+        filter_code,
+    };
+
+    if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) {
+        jniThrowExceptionFmt(env, "java/net/SocketException",
+                "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno));
+    }
+}
+
 static jboolean android_net_utils_bindProcessToNetwork(JNIEnv *env, jobject thiz, jint netId)
 {
     return (jboolean) !setNetworkForProcess(netId);
 }
 
-static jint android_net_utils_getNetworkBoundToProcess(JNIEnv *env, jobject thiz)
+static jint android_net_utils_getBoundNetworkForProcess(JNIEnv *env, jobject thiz)
 {
     return getNetworkForProcess();
 }
@@ -247,6 +290,7 @@
     return (jboolean) !protectFromVpn(socket);
 }
 
+
 // ----------------------------------------------------------------------------
 
 /*
@@ -255,16 +299,18 @@
 static JNINativeMethod gNetworkUtilMethods[] = {
     /* name, signature, funcPtr */
     { "resetConnections", "(Ljava/lang/String;I)I",  (void *)android_net_utils_resetConnections },
-    { "runDhcp", "(Ljava/lang/String;Landroid/net/DhcpResults;)Z",  (void *)android_net_utils_runDhcp },
-    { "runDhcpRenew", "(Ljava/lang/String;Landroid/net/DhcpResults;)Z",  (void *)android_net_utils_runDhcpRenew },
+    { "startDhcp", "(Ljava/lang/String;)Z",  (void *)android_net_utils_startDhcp },
+    { "startDhcpRenew", "(Ljava/lang/String;)Z",  (void *)android_net_utils_startDhcpRenew },
+    { "getDhcpResults", "(Ljava/lang/String;Landroid/net/DhcpResults;)Z",  (void *)android_net_utils_getDhcpResults },
     { "stopDhcp", "(Ljava/lang/String;)Z",  (void *)android_net_utils_stopDhcp },
     { "releaseDhcpLease", "(Ljava/lang/String;)Z",  (void *)android_net_utils_releaseDhcpLease },
     { "getDhcpError", "()Ljava/lang/String;", (void*) android_net_utils_getDhcpError },
     { "bindProcessToNetwork", "(I)Z", (void*) android_net_utils_bindProcessToNetwork },
-    { "getNetworkBoundToProcess", "()I", (void*) android_net_utils_getNetworkBoundToProcess },
+    { "getBoundNetworkForProcess", "()I", (void*) android_net_utils_getBoundNetworkForProcess },
     { "bindProcessToNetworkForHostResolution", "(I)Z", (void*) android_net_utils_bindProcessToNetworkForHostResolution },
     { "bindSocketToNetwork", "(II)I", (void*) android_net_utils_bindSocketToNetwork },
     { "protectFromVpn", "(I)Z", (void*)android_net_utils_protectFromVpn },
+    { "attachDhcpFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDhcpFilter },
 };
 
 int register_android_net_NetworkUtils(JNIEnv* env)
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index a39ff8e..6b0ce71 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -129,6 +129,100 @@
     return surfaceObj;
 }
 
+int android_view_Surface_mapPublicFormatToHalFormat(PublicFormat f) {
+
+    switch(f) {
+        case JPEG:
+        case DEPTH_POINT_CLOUD:
+            return HAL_PIXEL_FORMAT_BLOB;
+        case DEPTH16:
+            return HAL_PIXEL_FORMAT_Y16;
+        case RAW_SENSOR:
+            return HAL_PIXEL_FORMAT_RAW16;
+        default:
+            // Most formats map 1:1
+            return static_cast<int>(f);
+    }
+}
+
+android_dataspace android_view_Surface_mapPublicFormatToHalDataspace(
+        PublicFormat f) {
+    switch(f) {
+        case JPEG:
+            return HAL_DATASPACE_JFIF;
+        case DEPTH_POINT_CLOUD:
+        case DEPTH16:
+            return HAL_DATASPACE_DEPTH;
+        case RAW_SENSOR:
+        case RAW10:
+            return HAL_DATASPACE_ARBITRARY;
+        case YUV_420_888:
+        case NV21:
+        case YV12:
+            return HAL_DATASPACE_JFIF;
+        default:
+            // Most formats map to UNKNOWN
+            return HAL_DATASPACE_UNKNOWN;
+    }
+}
+
+PublicFormat android_view_Surface_mapHalFormatDataspaceToPublicFormat(
+        int format, android_dataspace dataSpace) {
+    switch(format) {
+        case HAL_PIXEL_FORMAT_RGBA_8888:
+        case HAL_PIXEL_FORMAT_RGBX_8888:
+        case HAL_PIXEL_FORMAT_RGB_888:
+        case HAL_PIXEL_FORMAT_RGB_565:
+        case HAL_PIXEL_FORMAT_Y8:
+        case HAL_PIXEL_FORMAT_RAW10:
+        case HAL_PIXEL_FORMAT_YCbCr_420_888:
+        case HAL_PIXEL_FORMAT_YV12:
+            // Enums overlap in both name and value
+            return static_cast<PublicFormat>(format);
+        case HAL_PIXEL_FORMAT_RAW16:
+            // Name differs, though value is the same
+            return RAW_SENSOR;
+        case HAL_PIXEL_FORMAT_YCbCr_422_SP:
+            // Name differs, though the value is the same
+            return NV16;
+        case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+            // Name differs, though the value is the same
+            return NV21;
+        case HAL_PIXEL_FORMAT_YCbCr_422_I:
+            // Name differs, though the value is the same
+            return YUY2;
+        case HAL_PIXEL_FORMAT_Y16:
+            // Dataspace-dependent
+            switch (dataSpace) {
+                case HAL_DATASPACE_DEPTH:
+                    return DEPTH16;
+                default:
+                    // Assume non-depth Y16 is just Y16.
+                    return Y16;
+            }
+            break;
+        case HAL_PIXEL_FORMAT_BLOB:
+            // Dataspace-dependent
+            switch (dataSpace) {
+                case HAL_DATASPACE_DEPTH:
+                    return DEPTH_POINT_CLOUD;
+                case HAL_DATASPACE_JFIF:
+                    return JPEG;
+                default:
+                    // Assume otherwise-marked blobs are also JPEG
+                    return JPEG;
+            }
+            break;
+        case HAL_PIXEL_FORMAT_BGRA_8888:
+        case HAL_PIXEL_FORMAT_RAW_OPAQUE:
+        case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
+            // Not defined in public API
+            return UNKNOWN;
+
+        default:
+            return UNKNOWN;
+    }
+}
 // ----------------------------------------------------------------------------
 
 static inline bool isSurfaceValid(const sp<Surface>& sur) {
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ccdb5db..ed21e80 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -208,6 +208,7 @@
     <protected-broadcast android:name="android.intent.action.MEDIA_UNMOUNTABLE" />
     <protected-broadcast android:name="android.intent.action.MEDIA_EJECT" />
 
+    <protected-broadcast android:name="android.net.conn.CAPTIVE_PORTAL" />
     <protected-broadcast android:name="android.net.conn.CONNECTIVITY_CHANGE" />
     <protected-broadcast android:name="android.net.conn.CONNECTIVITY_CHANGE_IMMEDIATE" />
     <protected-broadcast android:name="android.net.conn.DATA_ACTIVITY_CHANGE" />
@@ -2923,6 +2924,15 @@
         android:description="@string/permdesc_bindCarrierMessagingService"
         android:protectionLevel="signature|system" />
 
+    <!-- The system process that pulls carrier configuration from carrier apps will
+         have this permission. Carrier apps that provide
+         {@link android.service.carrier.CarrierConfigService} should require this
+         permission for clients binding to their service. -->
+    <permission android:name="android.permission.BIND_CARRIER_CONFIG_SERVICE"
+        android:label="@string/permlab_bindCarrierConfigService"
+        android:description="@string/permdesc_bindCarrierConfigService"
+        android:protectionLevel="signature|system" />
+
     <!-- The system process is explicitly the only one allowed to launch the
          confirmation UI for full backup/restore -->
     <uses-permission android:name="android.permission.CONFIRM_FULL_BACKUP"/>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 3de2268..c22a1cb 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -437,6 +437,9 @@
          point on the move. A value of 0 means no periodic scans will be used in the framework. -->
     <integer translatable="false" name="config_wifi_framework_scan_interval">300000</integer>
 
+    <!-- Integer indicating disconnect mode scan interval in milliseconds -->
+    <integer translatable="false" name="config_wifi_disconnected_scan_interval">15000</integer>
+
     <!-- Integer indicating associated partial scan interval in milliseconds -->
     <integer translatable="false" name="config_wifi_framework_associated_scan_interval">20000</integer>
 
@@ -486,6 +489,21 @@
     <!-- Wifi driver supports batched scan -->
     <bool translatable="false" name="config_wifi_batched_scan_supported">false</bool>
 
+    <!-- Wifi HAL supported PNO -->
+    <bool translatable="false" name="config_wifi_hal_pno_enable">false</bool>
+
+    <!-- Idle Receive current for wifi radio. 0 by default-->
+    <integer translatable="false" name="config_wifi_idle_receive_cur_ma">1</integer>
+
+    <!-- Rx current for wifi radio. 0 by default-->
+    <integer translatable="false" name="config_wifi_active_rx_cur_ma">2</integer>
+
+    <!-- Tx current for wifi radio. 0 by default-->
+    <integer translatable="false" name="config_wifi_tx_cur_ma">3</integer>
+
+    <!-- Operating volatage for wifi radio. 0 by default-->
+    <integer translatable="false" name="config_wifi_operating_voltage_mv">4</integer>
+
     <!-- Flag indicating whether the we should enable the automatic brightness in Settings.
          Software implementation will be used if config_hardware_auto_brightness_available is not set -->
     <bool name="config_automatic_brightness_available">false</bool>
@@ -1087,6 +1105,18 @@
          device does not support multiple advertisement-->
     <integer translatable="false" name="config_bluetooth_max_advertisers">0</integer>
 
+    <!-- Idle current for bluetooth controller. 0 by default-->
+    <integer translatable="false" name="config_bluetooth_idle_cur_ma">1</integer>
+
+    <!-- Rx current for bluetooth controller. 0 by default-->
+    <integer translatable="false" name="config_bluetooth_rx_cur_ma">2</integer>
+
+    <!-- Tx current for bluetooth controller. 0 by default-->
+    <integer translatable="false" name="config_bluetooth_tx_cur_ma">3</integer>
+
+    <!-- Operating volatage for bluetooth controller. 0 by default-->
+    <integer translatable="false" name="config_bluetooth_operating_voltage_mv">4</integer>
+
     <!-- The default data-use polling period. -->
     <integer name="config_datause_polling_period_sec">600</integer>
 
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 9711f93..072d633 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2397,6 +2397,11 @@
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_bindCarrierMessagingService">Allows the holder to bind to the top-level interface of a carrier messaging service. Should never be needed for normal apps.</string>
 
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_bindCarrierConfigService">bind to a carrier config service</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_bindCarrierConfigService">Allows the holder to bind to a carrier config service. Should never be needed for normal apps.</string>
+
     <!-- Policy administration -->
 
     <!-- Title of policy access to limiting the user's password choices -->
@@ -3842,10 +3847,10 @@
     </plurals>
 
     <!-- A notification is shown when a wifi captive portal network is detected.  This is the notification's title. -->
-    <string name="wifi_available_sign_in">Sign into Wi-Fi network</string>
+    <string name="wifi_available_sign_in">Sign in to Wi-Fi network</string>
 
     <!-- A notification is shown when a captive portal network is detected.  This is the notification's title. -->
-    <string name="network_available_sign_in">Sign into network</string>
+    <string name="network_available_sign_in">Sign in to network</string>
 
     <!-- A notification is shown when a captive portal network is detected.  This is the notification's message. -->
     <string name="network_available_sign_in_detailed"><xliff:g id="network_ssid">%1$s</xliff:g></string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 816d203..d33f667 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -328,6 +328,10 @@
   <java-symbol type="integer" name="config_wifi_framework_current_network_boost" />
   <java-symbol type="string"  name="config_wifi_random_mac_oui" />
   <java-symbol type="integer"  name="config_wifi_network_switching_blacklist_time" />
+  <java-symbol type="integer"  name="config_wifi_idle_receive_cur_ma" />
+  <java-symbol type="integer"  name="config_wifi_active_rx_cur_ma" />
+  <java-symbol type="integer"  name="config_wifi_tx_cur_ma" />
+  <java-symbol type="integer"  name="config_wifi_operating_voltage_mv" />
 
   <java-symbol type="bool" name="editable_voicemailnumber" />
 
@@ -344,6 +348,10 @@
   <java-symbol type="integer" name="config_wifi_framework_current_network_boost" />
   <java-symbol type="integer" name="config_bluetooth_max_advertisers" />
   <java-symbol type="integer" name="config_bluetooth_max_scan_filters" />
+  <java-symbol type="integer" name="config_bluetooth_idle_cur_ma" />
+  <java-symbol type="integer" name="config_bluetooth_rx_cur_ma" />
+  <java-symbol type="integer" name="config_bluetooth_tx_cur_ma" />
+  <java-symbol type="integer" name="config_bluetooth_operating_voltage_mv" />
   <java-symbol type="integer" name="config_cursorWindowSize" />
   <java-symbol type="integer" name="config_doublePressOnPowerBehavior" />
   <java-symbol type="integer" name="config_extraFreeKbytesAdjust" />
@@ -363,7 +371,9 @@
   <java-symbol type="integer" name="config_triplePressOnPowerBehavior" />
   <java-symbol type="integer" name="config_wifi_framework_scan_interval" />
   <java-symbol type="integer" name="config_wifi_supplicant_scan_interval" />
+  <java-symbol type="integer" name="config_wifi_disconnected_scan_interval" />
   <java-symbol type="integer" name="config_wifi_scan_interval_p2p_connected" />
+  <java-symbol type="bool" name="config_wifi_hal_pno_enable" />
   <java-symbol type="integer" name="db_connection_pool_size" />
   <java-symbol type="integer" name="db_journal_size_limit" />
   <java-symbol type="integer" name="db_wal_autocheckpoint" />
diff --git a/core/tests/coretests/src/android/net/NetworkUtilsTest.java b/core/tests/coretests/src/android/net/NetworkUtilsTest.java
new file mode 100644
index 0000000..8d51c3b
--- /dev/null
+++ b/core/tests/coretests/src/android/net/NetworkUtilsTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2015 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.net;
+
+import android.net.NetworkUtils;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+
+import junit.framework.TestCase;
+
+public class NetworkUtilsTest extends TestCase {
+
+    private InetAddress Address(String addr) {
+        return InetAddress.parseNumericAddress(addr);
+    }
+
+    private Inet4Address IPv4Address(String addr) {
+        return (Inet4Address) Address(addr);
+    }
+
+    @SmallTest
+    public void testGetImplicitNetmask() {
+        assertEquals(8, NetworkUtils.getImplicitNetmask(IPv4Address("4.2.2.2")));
+        assertEquals(8, NetworkUtils.getImplicitNetmask(IPv4Address("10.5.6.7")));
+        assertEquals(16, NetworkUtils.getImplicitNetmask(IPv4Address("173.194.72.105")));
+        assertEquals(16, NetworkUtils.getImplicitNetmask(IPv4Address("172.23.68.145")));
+        assertEquals(24, NetworkUtils.getImplicitNetmask(IPv4Address("192.0.2.1")));
+        assertEquals(24, NetworkUtils.getImplicitNetmask(IPv4Address("192.168.5.1")));
+        assertEquals(32, NetworkUtils.getImplicitNetmask(IPv4Address("224.0.0.1")));
+        assertEquals(32, NetworkUtils.getImplicitNetmask(IPv4Address("255.6.7.8")));
+    }
+
+    private void assertInvalidNetworkMask(Inet4Address addr) {
+        try {
+            NetworkUtils.netmaskToPrefixLength(addr);
+            fail("Invalid netmask " + addr.getHostAddress() + " did not cause exception");
+        } catch (IllegalArgumentException expected) {
+        }
+    }
+
+    @SmallTest
+    public void testNetmaskToPrefixLength() {
+        assertEquals(0, NetworkUtils.netmaskToPrefixLength(IPv4Address("0.0.0.0")));
+        assertEquals(9, NetworkUtils.netmaskToPrefixLength(IPv4Address("255.128.0.0")));
+        assertEquals(17, NetworkUtils.netmaskToPrefixLength(IPv4Address("255.255.128.0")));
+        assertEquals(23, NetworkUtils.netmaskToPrefixLength(IPv4Address("255.255.254.0")));
+        assertEquals(31, NetworkUtils.netmaskToPrefixLength(IPv4Address("255.255.255.254")));
+        assertEquals(32, NetworkUtils.netmaskToPrefixLength(IPv4Address("255.255.255.255")));
+
+        assertInvalidNetworkMask(IPv4Address("0.0.0.1"));
+        assertInvalidNetworkMask(IPv4Address("255.255.255.253"));
+        assertInvalidNetworkMask(IPv4Address("255.255.0.255"));
+    }
+}
diff --git a/docs/html/images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png b/docs/html/images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png
index ec89e37..a02fd89 100644
--- a/docs/html/images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png
+++ b/docs/html/images/camera2/metadata/android.tonemap.curveRed/gamma_tonemap.png
Binary files differ
diff --git a/docs/html/images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png b/docs/html/images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png
index cf0c63d..c309ac5 100644
--- a/docs/html/images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png
+++ b/docs/html/images/camera2/metadata/android.tonemap.curveRed/inverse_tonemap.png
Binary files differ
diff --git a/docs/html/images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png b/docs/html/images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png
index f226a54..414fad4 100644
--- a/docs/html/images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png
+++ b/docs/html/images/camera2/metadata/android.tonemap.curveRed/linear_tonemap.png
Binary files differ
diff --git a/docs/html/images/camera2/metadata/android.tonemap.curveRed/rec709_tonemap.png b/docs/html/images/camera2/metadata/android.tonemap.curveRed/rec709_tonemap.png
new file mode 100644
index 0000000..c147a87
--- /dev/null
+++ b/docs/html/images/camera2/metadata/android.tonemap.curveRed/rec709_tonemap.png
Binary files differ
diff --git a/docs/html/images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png b/docs/html/images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png
index ded0645..4ce2125 100644
--- a/docs/html/images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png
+++ b/docs/html/images/camera2/metadata/android.tonemap.curveRed/srgb_tonemap.png
Binary files differ
diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java
index 3efb9c0..49c4247 100644
--- a/graphics/java/android/graphics/ImageFormat.java
+++ b/graphics/java/android/graphics/ImageFormat.java
@@ -356,6 +356,38 @@
     public static final int RAW10 = 0x25;
 
     /**
+     * Android dense depth image format.
+     *
+     * Each pixel is 16 bits, representing a depth ranging measurement from
+     * a depth camera or similar sensor.
+     *
+     * <p>This format assumes
+     * <ul>
+     * <li>an even width</li>
+     * <li>an even height</li>
+     * <li>a horizontal stride multiple of 16 pixels</li>
+     * </ul>
+     * </p>
+     *
+     * <pre> y_size = stride * height </pre>
+     *
+     * When produced by a camera, the units are millimeters.
+     */
+    public static final int DEPTH16 = 0x44363159;
+
+    /**
+     * Android sparse depth point cloud format.
+     *
+     * <p>A variable-length list of 3D points, with each point represented
+     * by a triple of floats.</p>
+     *
+     * <p>The number of points is {@code (size of the buffer in bytes) / 12}.
+     *
+     * The coordinate system and units depend on the source of the point cloud data.
+     */
+    public static final int DEPTH_POINT_CLOUD = 0x101;
+
+    /**
      * Use this function to retrieve the number of bits per pixel of an
      * ImageFormat.
      *
@@ -376,6 +408,7 @@
             case Y8:
                 return 8;
             case Y16:
+            case DEPTH16:
                 return 16;
             case NV21:
                 return 12;
@@ -412,6 +445,8 @@
             case YUV_420_888:
             case RAW_SENSOR:
             case RAW10:
+            case DEPTH16:
+            case DEPTH_POINT_CLOUD:
                 return true;
         }
 
diff --git a/include/android_runtime/android_view_Surface.h b/include/android_runtime/android_view_Surface.h
index 53e8b49..10c75f1 100644
--- a/include/android_runtime/android_view_Surface.h
+++ b/include/android_runtime/android_view_Surface.h
@@ -26,6 +26,33 @@
 class Surface;
 class IGraphicBufferProducer;
 
+/**
+ * Enum mirroring the public API definitions for image and pixel formats.
+ * Some of these are hidden in the public API
+ *
+ * Keep up to date with android.graphics.ImageFormat and
+ * android.graphics.PixelFormat
+ */
+enum PublicFormat {
+    UNKNOWN           = 0x0,
+    RGBA_8888         = 0x1,
+    RGBX_8888         = 0x2,
+    RGB_888           = 0x3,
+    RGB_565           = 0x4,
+    NV16              = 0x10,
+    NV21              = 0x11,
+    YUY2              = 0x14,
+    RAW_SENSOR        = 0x20,
+    YUV_420_888       = 0x23,
+    RAW10             = 0x25,
+    JPEG              = 0x100,
+    DEPTH_POINT_CLOUD = 0x101,
+    YV12              = 0x32315659,
+    Y8                = 0x20203859, // @hide
+    Y16               = 0x20363159, // @hide
+    DEPTH16           = 0x44363159
+};
+
 /* Gets the underlying ANativeWindow for a Surface. */
 extern sp<ANativeWindow> android_view_Surface_getNativeWindow(
         JNIEnv* env, jobject surfaceObj);
@@ -40,6 +67,21 @@
 extern jobject android_view_Surface_createFromIGraphicBufferProducer(JNIEnv* env,
         const sp<IGraphicBufferProducer>& bufferProducer);
 
+/* Convert from android.graphics.ImageFormat/PixelFormat enums to graphics.h HAL
+ * format */
+extern int android_view_Surface_mapPublicFormatToHalFormat(PublicFormat f);
+
+/* Convert from android.graphics.ImageFormat/PixelFormat enums to graphics.h HAL
+ * dataspace */
+extern android_dataspace android_view_Surface_mapPublicFormatToHalDataspace(
+        PublicFormat f);
+
+/* Convert from HAL format, dataspace pair to
+ * android.graphics.ImageFormat/PixelFormat.
+ * For unknown/unspecified pairs, returns PublicFormat::UNKNOWN */
+extern PublicFormat android_view_Surface_mapHalFormatDataspaceToPublicFormat(
+        int format, android_dataspace dataSpace);
+
 } // namespace android
 
 #endif // _ANDROID_VIEW_SURFACE_H
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index 8d6a588..824a7ad 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -483,6 +483,8 @@
             case ImageFormat.Y16:
             case ImageFormat.RAW_SENSOR:
             case ImageFormat.RAW10:
+            case ImageFormat.DEPTH16:
+            case ImageFormat.DEPTH_POINT_CLOUD:
                 return 1;
             default:
                 throw new UnsupportedOperationException(
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 890b039..57a7b2c 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -95,6 +95,9 @@
     void setBufferFormat(int format) { mFormat = format; }
     int getBufferFormat() { return mFormat; }
 
+    void setBufferDataspace(android_dataspace dataSpace) { mDataSpace = dataSpace; }
+    android_dataspace getBufferDataspace() { return mDataSpace; }
+
     void setBufferWidth(int width) { mWidth = width; }
     int getBufferWidth() { return mWidth; }
 
@@ -111,6 +114,7 @@
     jobject mWeakThiz;
     jclass mClazz;
     int mFormat;
+    android_dataspace mDataSpace;
     int mWidth;
     int mHeight;
 };
@@ -263,29 +267,6 @@
     env->SetLongField(thiz, gSurfaceImageClassInfo.mLockedBuffer, reinterpret_cast<jlong>(buffer));
 }
 
-// Some formats like JPEG defined with different values between android.graphics.ImageFormat and
-// graphics.h, need convert to the one defined in graphics.h here.
-static int Image_getPixelFormat(JNIEnv* env, int format)
-{
-    int jpegFormat;
-    jfieldID fid;
-
-    ALOGV("%s: format = 0x%x", __FUNCTION__, format);
-
-    jclass imageFormatClazz = env->FindClass("android/graphics/ImageFormat");
-    ALOG_ASSERT(imageFormatClazz != NULL);
-
-    fid = env->GetStaticFieldID(imageFormatClazz, "JPEG", "I");
-    jpegFormat = env->GetStaticIntField(imageFormatClazz, fid);
-
-    // Translate the JPEG to BLOB for camera purpose.
-    if (format == jpegFormat) {
-        format = HAL_PIXEL_FORMAT_BLOB;
-    }
-
-    return format;
-}
-
 static uint32_t Image_getJpegSize(CpuConsumer::LockedBuffer* buffer, bool usingRGBAOverride)
 {
     ALOG_ASSERT(buffer != NULL, "Input buffer is NULL!!!");
@@ -430,7 +411,7 @@
             pData = buffer->data;
             dataSize = Image_getJpegSize(buffer, usingRGBAOverride);
             break;
-        case HAL_PIXEL_FORMAT_RAW_SENSOR:
+        case HAL_PIXEL_FORMAT_RAW16:
             // Single plane 16bpp bayer data.
             bytesPerPixel = 2;
             ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
@@ -483,7 +464,7 @@
 }
 
 static jint Image_imageGetPixelStride(JNIEnv* env, CpuConsumer::LockedBuffer* buffer, int idx,
-        int32_t readerFormat)
+        int32_t halReaderFormat)
 {
     ALOGV("%s: buffer index: %d", __FUNCTION__, idx);
     ALOG_ASSERT((idx < IMAGE_READER_MAX_NUM_PLANES) && (idx >= 0), "Index is out of range:%d", idx);
@@ -493,7 +474,7 @@
 
     int32_t fmt = buffer->flexFormat;
 
-    fmt = applyFormatOverrides(fmt, readerFormat);
+    fmt = applyFormatOverrides(fmt, halReaderFormat);
 
     switch (fmt) {
         case HAL_PIXEL_FORMAT_YCbCr_420_888:
@@ -518,7 +499,7 @@
             pixelStride = 0;
             break;
         case HAL_PIXEL_FORMAT_Y16:
-        case HAL_PIXEL_FORMAT_RAW_SENSOR:
+        case HAL_PIXEL_FORMAT_RAW16:
         case HAL_PIXEL_FORMAT_RGB_565:
             // Single plane 16bpp data.
             ALOG_ASSERT(idx == 0, "Wrong index: %d", idx);
@@ -544,7 +525,7 @@
 }
 
 static jint Image_imageGetRowStride(JNIEnv* env, CpuConsumer::LockedBuffer* buffer, int idx,
-        int32_t readerFormat)
+        int32_t halReaderFormat)
 {
     ALOGV("%s: buffer index: %d", __FUNCTION__, idx);
     ALOG_ASSERT((idx < IMAGE_READER_MAX_NUM_PLANES) && (idx >= 0));
@@ -554,7 +535,7 @@
 
     int32_t fmt = buffer->flexFormat;
 
-    fmt = applyFormatOverrides(fmt, readerFormat);
+    fmt = applyFormatOverrides(fmt, halReaderFormat);
 
     switch (fmt) {
         case HAL_PIXEL_FORMAT_YCbCr_420_888:
@@ -585,7 +566,7 @@
             rowStride = buffer->stride;
             break;
         case HAL_PIXEL_FORMAT_Y16:
-        case HAL_PIXEL_FORMAT_RAW_SENSOR:
+        case HAL_PIXEL_FORMAT_RAW16:
             // In native side, strides are specified in pixels, not in bytes.
             // Single plane 16bpp bayer data. even width/height,
             // row stride multiple of 16 pixels (32 bytes)
@@ -683,11 +664,16 @@
 {
     status_t res;
     int nativeFormat;
+    android_dataspace nativeDataspace;
 
     ALOGV("%s: width:%d, height: %d, format: 0x%x, maxImages:%d",
           __FUNCTION__, width, height, format, maxImages);
 
-    nativeFormat = Image_getPixelFormat(env, format);
+    PublicFormat publicFormat = static_cast<PublicFormat>(format);
+    nativeFormat = android_view_Surface_mapPublicFormatToHalFormat(
+        publicFormat);
+    nativeDataspace = android_view_Surface_mapPublicFormatToHalDataspace(
+        publicFormat);
 
     sp<IGraphicBufferProducer> gbProducer;
     sp<IGraphicBufferConsumer> gbConsumer;
@@ -711,10 +697,11 @@
     consumer->setFrameAvailableListener(ctx);
     ImageReader_setNativeContext(env, thiz, ctx);
     ctx->setBufferFormat(nativeFormat);
+    ctx->setBufferDataspace(nativeDataspace);
     ctx->setBufferWidth(width);
     ctx->setBufferHeight(height);
 
-    // Set the width/height/format to the CpuConsumer
+    // Set the width/height/format/dataspace to the CpuConsumer
     res = consumer->setDefaultBufferSize(width, height);
     if (res != OK) {
         jniThrowException(env, "java/lang/IllegalStateException",
@@ -726,6 +713,12 @@
         jniThrowException(env, "java/lang/IllegalStateException",
                           "Failed to set CpuConsumer buffer format");
     }
+    res = consumer->setDefaultBufferDataSpace(nativeDataspace);
+    if (res != OK) {
+        jniThrowException(env, "java/lang/IllegalStateException",
+                          "Failed to set CpuConsumer buffer dataSpace");
+    }
+
 }
 
 static void ImageReader_close(JNIEnv* env, jobject thiz)
@@ -885,6 +878,8 @@
 static jobject Image_createSurfacePlane(JNIEnv* env, jobject thiz, int idx, int readerFormat)
 {
     int rowStride, pixelStride;
+    PublicFormat publicReaderFormat = static_cast<PublicFormat>(readerFormat);
+
     ALOGV("%s: buffer index: %d", __FUNCTION__, idx);
 
     CpuConsumer::LockedBuffer* buffer = Image_getLockedBuffer(env, thiz);
@@ -894,10 +889,11 @@
         jniThrowException(env, "java/lang/IllegalStateException", "Image was released");
     }
 
-    readerFormat = Image_getPixelFormat(env, readerFormat);
+    int halReaderFormat = android_view_Surface_mapPublicFormatToHalFormat(
+        publicReaderFormat);
 
-    rowStride = Image_imageGetRowStride(env, buffer, idx, readerFormat);
-    pixelStride = Image_imageGetPixelStride(env, buffer, idx, readerFormat);
+    rowStride = Image_imageGetRowStride(env, buffer, idx, halReaderFormat);
+    pixelStride = Image_imageGetPixelStride(env, buffer, idx, halReaderFormat);
 
     jobject surfPlaneObj = env->NewObject(gSurfacePlaneClassInfo.clazz,
             gSurfacePlaneClassInfo.ctor, thiz, idx, rowStride, pixelStride);
@@ -910,6 +906,7 @@
     uint8_t *base = NULL;
     uint32_t size = 0;
     jobject byteBuffer;
+    PublicFormat readerPublicFormat = static_cast<PublicFormat>(readerFormat);
 
     ALOGV("%s: buffer index: %d", __FUNCTION__, idx);
 
@@ -919,10 +916,11 @@
         jniThrowException(env, "java/lang/IllegalStateException", "Image was released");
     }
 
-    readerFormat = Image_getPixelFormat(env, readerFormat);
+    int readerHalFormat = android_view_Surface_mapPublicFormatToHalFormat(
+            readerPublicFormat);
 
     // Create byteBuffer from native buffer
-    Image_getLockedBufferInfo(env, buffer, idx, &base, &size, readerFormat);
+    Image_getLockedBufferInfo(env, buffer, idx, &base, &size, readerHalFormat);
 
     if (size > static_cast<uint32_t>(INT32_MAX)) {
         // Byte buffer have 'int capacity', so check the range
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
index cc50c43..362bbc4 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
@@ -316,6 +316,11 @@
                 throws RemoteException {
             Log.v(TAG, String.format("Camera %d has status changed to 0x%x", cameraId, status));
         }
+        public void onTorchStatusChanged(int status, String cameraId)
+                throws RemoteException {
+            Log.v(TAG, String.format("Camera %s has torch status changed to 0x%x",
+                    cameraId, status));
+        }
     }
 
     /**
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
index 3cae19d..e05e1fc 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
@@ -25,6 +25,7 @@
 import android.hardware.camera2.ICameraDeviceUser;
 import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.impl.CaptureResultExtras;
+import android.hardware.camera2.params.OutputConfiguration;
 import android.hardware.camera2.utils.BinderHolder;
 import android.media.Image;
 import android.media.ImageReader;
@@ -67,6 +68,7 @@
     private CameraBinderTestUtils mUtils;
     private ICameraDeviceCallbacks.Stub mMockCb;
     private Surface mSurface;
+    private OutputConfiguration mOutputConfiguration;
     private HandlerThread mHandlerThread;
     private Handler mHandler;
     ImageReader mImageReader;
@@ -147,6 +149,7 @@
                         MAX_NUM_IMAGES);
         mImageReader.setOnImageAvailableListener(new ImageDropperListener(), mHandler);
         mSurface = mImageReader.getSurface();
+        mOutputConfiguration = new OutputConfiguration(mSurface);
     }
 
     private CaptureRequest.Builder createDefaultBuilder(boolean needStream) throws Exception {
@@ -161,8 +164,7 @@
         assertFalse(request.isEmpty());
         assertFalse(metadata.isEmpty());
         if (needStream) {
-            int streamId = mCameraUser.createStream(/* ignored */10, /* ignored */20,
-                    /* ignored */30, mSurface);
+            int streamId = mCameraUser.createStream(mOutputConfiguration);
             assertEquals(0, streamId);
             request.addTarget(mSurface);
         }
@@ -235,12 +237,11 @@
 
     @SmallTest
     public void testCreateStream() throws Exception {
-        int streamId = mCameraUser.createStream(/* ignored */10, /* ignored */20, /* ignored */30,
-                mSurface);
+        int streamId = mCameraUser.createStream(mOutputConfiguration);
         assertEquals(0, streamId);
 
         assertEquals(CameraBinderTestUtils.ALREADY_EXISTS,
-                mCameraUser.createStream(/* ignored */0, /* ignored */0, /* ignored */0, mSurface));
+                mCameraUser.createStream(mOutputConfiguration));
 
         assertEquals(CameraBinderTestUtils.NO_ERROR, mCameraUser.deleteStream(streamId));
     }
@@ -257,20 +258,19 @@
     public void testCreateStreamTwo() throws Exception {
 
         // Create first stream
-        int streamId = mCameraUser.createStream(/* ignored */0, /* ignored */0, /* ignored */0,
-                mSurface);
+        int streamId = mCameraUser.createStream(mOutputConfiguration);
         assertEquals(0, streamId);
 
         assertEquals(CameraBinderTestUtils.ALREADY_EXISTS,
-                mCameraUser.createStream(/* ignored */0, /* ignored */0, /* ignored */0, mSurface));
+                mCameraUser.createStream(mOutputConfiguration));
 
         // Create second stream with a different surface.
         SurfaceTexture surfaceTexture = new SurfaceTexture(/* ignored */0);
         surfaceTexture.setDefaultBufferSize(640, 480);
         Surface surface2 = new Surface(surfaceTexture);
+        OutputConfiguration output2 = new OutputConfiguration(surface2);
 
-        int streamId2 = mCameraUser.createStream(/* ignored */0, /* ignored */0, /* ignored */0,
-                surface2);
+        int streamId2 = mCameraUser.createStream(output2);
         assertEquals(1, streamId2);
 
         // Clean up streams
diff --git a/native/android/Android.mk b/native/android/Android.mk
index cda38e0..6b77b4f 100644
--- a/native/android/Android.mk
+++ b/native/android/Android.mk
@@ -12,9 +12,10 @@
     looper.cpp \
     native_activity.cpp \
     native_window.cpp \
+    net.c \
     obb.cpp \
     sensor.cpp \
-    storage_manager.cpp
+    storage_manager.cpp \
 
 LOCAL_SHARED_LIBRARIES := \
     liblog \
@@ -25,14 +26,17 @@
     libbinder \
     libui \
     libgui \
-    libandroid_runtime
+    libandroid_runtime \
+    libnetd_client \
 
 LOCAL_STATIC_LIBRARIES := \
     libstorage
 
 LOCAL_C_INCLUDES += \
     frameworks/base/native/include \
-    frameworks/base/core/jni/android
+    frameworks/base/core/jni/android \
+    bionic/libc/dns/include \
+    system/netd/include \
 
 LOCAL_MODULE:= libandroid
 
diff --git a/native/android/net.c b/native/android/net.c
new file mode 100644
index 0000000..de4b90c
--- /dev/null
+++ b/native/android/net.c
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+
+#include <android/multinetwork.h>
+#include <errno.h>
+#include <NetdClient.h>    // the functions that communicate with netd
+#include <resolv_netid.h>  // android_getaddrinfofornet()
+#include <stdlib.h>
+#include <sys/limits.h>
+
+
+static int getnetidfromhandle(net_handle_t handle, unsigned *netid) {
+    static const uint32_t k32BitMask = 0xffffffff;
+    // This value MUST be kept in sync with the corresponding value in
+    // the android.net.Network#getNetworkHandle() implementation.
+    static const uint32_t kHandleMagic = 0xfacade;
+
+    // Check for minimum acceptable version of the API in the low bits.
+    if (handle != NETWORK_UNSPECIFIED &&
+        (handle & k32BitMask) != kHandleMagic) {
+        return 0;
+    }
+
+    if (netid != NULL) {
+        *netid = ((handle >> (CHAR_BIT * sizeof(k32BitMask))) & k32BitMask);
+    }
+    return 1;
+}
+
+
+int android_setsocknetwork(net_handle_t network, int fd) {
+    unsigned netid;
+    if (!getnetidfromhandle(network, &netid)) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    int rval = setNetworkForSocket(netid, fd);
+    if (rval < 0) {
+        errno = -rval;
+        rval = -1;
+    }
+    return rval;
+}
+
+int android_setprocnetwork(net_handle_t network) {
+    unsigned netid;
+    if (!getnetidfromhandle(network, &netid)) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    int rval = setNetworkForProcess(netid);
+    if (rval < 0) {
+        errno = -rval;
+        rval = -1;
+    }
+    return rval;
+}
+
+int android_getaddrinfofornetwork(net_handle_t network,
+        const char *node, const char *service,
+        const struct addrinfo *hints, struct addrinfo **res) {
+    unsigned netid;
+    if (!getnetidfromhandle(network, &netid)) {
+        errno = EINVAL;
+        return EAI_SYSTEM;
+    }
+
+    return android_getaddrinfofornet(node, service, hints, netid, 0, res);
+}
diff --git a/obex/Android.mk b/obex/Android.mk
index fbfe9be..e7c1fd3 100644
--- a/obex/Android.mk
+++ b/obex/Android.mk
@@ -7,3 +7,14 @@
 LOCAL_MODULE:= javax.obex
 
 include $(BUILD_JAVA_LIBRARY)
+
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_MODULE:= javax.obexstatic
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
\ No newline at end of file
diff --git a/obex/javax/obex/ClientOperation.java b/obex/javax/obex/ClientOperation.java
index 75278b5..cc20d39 100644
--- a/obex/javax/obex/ClientOperation.java
+++ b/obex/javax/obex/ClientOperation.java
@@ -1,5 +1,6 @@
 /*
- * Copyright (c) 2014 The Android Open Source Project
+ * Copyright (c) 2015 The Android Open Source Project
+ * Copyright (C) 2015 Samsung LSI
  * Copyright (c) 2008-2009, Motorola, Inc.
  *
  * All rights reserved.
@@ -40,6 +41,8 @@
 import java.io.DataOutputStream;
 import java.io.ByteArrayOutputStream;
 
+import android.util.Log;
+
 /**
  * This class implements the <code>Operation</code> interface. It will read and
  * write data via puts and gets.
@@ -47,6 +50,10 @@
  */
 public final class ClientOperation implements Operation, BaseStream {
 
+    private static final String TAG = "ClientOperation";
+
+    private static final boolean V = ObexHelper.VDBG;
+
     private ClientSession mParent;
 
     private boolean mInputOpen;
@@ -75,6 +82,19 @@
 
     private boolean mEndOfBodySent;
 
+    private boolean mSendBodyHeader = true;
+    // A latch - when triggered, there is not way back ;-)
+    private boolean mSrmActive = false;
+
+    // Assume SRM disabled - until support is confirmed
+    // by the server
+    private boolean mSrmEnabled = false;
+    // keep waiting until final-bit is received in request
+    // to handle the case where the SRM enable header is in
+    // a different OBEX packet than the SRMP header.
+    private boolean mSrmWaitingForRemote = true;
+
+
     /**
      * Creates new OperationImpl to read and write data to a server
      * @param maxSize the maximum packet size
@@ -164,7 +184,7 @@
              * Since we are not sending any headers or returning any headers then
              * we just need to write and read the same bytes
              */
-            mParent.sendRequest(ObexHelper.OBEX_OPCODE_ABORT, null, mReplyHeader, null);
+            mParent.sendRequest(ObexHelper.OBEX_OPCODE_ABORT, null, mReplyHeader, null, false);
 
             if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_OK) {
                 throw new IOException("Invalid response code from server");
@@ -215,6 +235,7 @@
         try {
             return (String)mReplyHeader.getHeader(HeaderSet.TYPE);
         } catch (IOException e) {
+            if(V) Log.d(TAG, "Exception occured - returning null",e);
             return null;
         }
     }
@@ -236,6 +257,7 @@
                 return temp.longValue();
             }
         } catch (IOException e) {
+            if(V) Log.d(TAG,"Exception occured - returning -1",e);
             return -1;
         }
     }
@@ -408,7 +430,9 @@
     }
 
     /**
-     * Sends a request to the client of the specified type
+     * Sends a request to the client of the specified type.
+     * This function will enable SRM and set SRM active if the server
+     * response allows this.
      * @param opCode the request code to send to the client
      * @return <code>true</code> if there is more data to send;
      *         <code>false</code> if there is no more data to send
@@ -431,13 +455,16 @@
          * length, but it is a waste of resources if we can't send much of
          * the body.
          */
-        if ((ObexHelper.BASE_PACKET_LENGTH + headerArray.length) > mMaxPacketSize) {
+        final int MINIMUM_BODY_LENGTH = 3;
+        if ((ObexHelper.BASE_PACKET_LENGTH + headerArray.length + MINIMUM_BODY_LENGTH)
+                > mMaxPacketSize) {
             int end = 0;
             int start = 0;
             // split & send the headerArray in multiple packets.
 
             while (end != headerArray.length) {
                 //split the headerArray
+
                 end = ObexHelper.findHeaderEnd(headerArray, start, mMaxPacketSize
                         - ObexHelper.BASE_PACKET_LENGTH);
                 // can not split
@@ -459,7 +486,7 @@
 
                 byte[] sendHeader = new byte[end - start];
                 System.arraycopy(headerArray, start, sendHeader, 0, sendHeader.length);
-                if (!mParent.sendRequest(opCode, sendHeader, mReplyHeader, mPrivateInput)) {
+                if (!mParent.sendRequest(opCode, sendHeader, mReplyHeader, mPrivateInput, false)) {
                     return false;
                 }
 
@@ -470,12 +497,20 @@
                 start = end;
             }
 
+            // Enable SRM if it should be enabled
+            checkForSrm();
+
             if (bodyLength > 0) {
                 return true;
             } else {
                 return false;
             }
         } else {
+            /* All headers will fit into a single package */
+            if(mSendBodyHeader == false) {
+                /* As we are not to send any body data, set the FINAL_BIT */
+                opCode |= ObexHelper.OBEX_OPCODE_FINAL_BIT_MASK;
+            }
             out.write(headerArray);
         }
 
@@ -499,11 +534,11 @@
              * (End of Body) otherwise, we need to send 0x48 (Body)
              */
             if ((mPrivateOutput.isClosed()) && (!returnValue) && (!mEndOfBodySent)
-                    && ((opCode & 0x80) != 0)) {
-                out.write(0x49);
+                    && ((opCode & ObexHelper.OBEX_OPCODE_FINAL_BIT_MASK) != 0)) {
+                out.write(HeaderSet.END_OF_BODY);
                 mEndOfBodySent = true;
             } else {
-                out.write(0x48);
+                out.write(HeaderSet.BODY);
             }
 
             bodyLength += 3;
@@ -517,12 +552,11 @@
 
         if (mPrivateOutputOpen && bodyLength <= 0 && !mEndOfBodySent) {
             // only 0x82 or 0x83 can send 0x49
-            if ((opCode & 0x80) == 0) {
-                out.write(0x48);
+            if ((opCode & ObexHelper.OBEX_OPCODE_FINAL_BIT_MASK) == 0) {
+                out.write(HeaderSet.BODY);
             } else {
-                out.write(0x49);
+                out.write(HeaderSet.END_OF_BODY);
                 mEndOfBodySent = true;
-
             }
 
             bodyLength = 3;
@@ -531,15 +565,20 @@
         }
 
         if (out.size() == 0) {
-            if (!mParent.sendRequest(opCode, null, mReplyHeader, mPrivateInput)) {
+            if (!mParent.sendRequest(opCode, null, mReplyHeader, mPrivateInput, mSrmActive)) {
                 return false;
             }
+            // Enable SRM if it should be enabled
+            checkForSrm();
             return returnValue;
         }
         if ((out.size() > 0)
-                && (!mParent.sendRequest(opCode, out.toByteArray(), mReplyHeader, mPrivateInput))) {
+                && (!mParent.sendRequest(opCode, out.toByteArray(),
+                        mReplyHeader, mPrivateInput, mSrmActive))) {
             return false;
         }
+        // Enable SRM if it should be enabled
+        checkForSrm();
 
         // send all of the output data in 0x48,
         // send 0x49 with empty body
@@ -549,6 +588,35 @@
         return returnValue;
     }
 
+    private void checkForSrm() throws IOException {
+        Byte srmMode = (Byte)mReplyHeader.getHeader(HeaderSet.SINGLE_RESPONSE_MODE);
+        if(mParent.isSrmSupported() == true && srmMode != null
+                && srmMode == ObexHelper.OBEX_SRM_ENABLE) {
+            mSrmEnabled = true;
+        }
+        /**
+         * Call this only when a complete obex packet have been received.
+         * (This is not optimal, but the current design is not really suited to
+         * the way SRM is specified.)
+         * The BT usage of SRM is not really safe - it assumes that the SRMP will fit
+         * into every OBEX packet, hence if another header occupies the entire packet,
+         * the scheme will not work - unlikely though.
+         */
+        if(mSrmEnabled) {
+            mSrmWaitingForRemote = false;
+            Byte srmp = (Byte)mReplyHeader.getHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER);
+            if(srmp != null && srmp == ObexHelper.OBEX_SRMP_WAIT) {
+                mSrmWaitingForRemote = true;
+                // Clear the wait header, as the absence of the header in the next packet
+                // indicates don't wait anymore.
+                mReplyHeader.setHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER, null);
+            }
+        }
+        if((mSrmWaitingForRemote == false) && (mSrmEnabled == true)) {
+            mSrmActive = true;
+        }
+    }
+
     /**
      * This method starts the processing thread results. It will send the
      * initial request. If the response takes more then one packet, a thread
@@ -564,40 +632,35 @@
 
         if (mGetOperation) {
             if (!mOperationDone) {
-                if (!mGetFinalFlag) {
-                    mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
-                    while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
-                        more = sendRequest(0x03);
-                    }
-
-                    if (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
-                        mParent.sendRequest(0x83, null, mReplyHeader, mPrivateInput);
-                    }
-                    if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
-                        mOperationDone = true;
-                    }
-                } else {
-                    more = sendRequest(0x83);
-
-                    if (more) {
-                        throw new IOException("FINAL_GET forced but data did not fit into single packet!");
-                    }
-
+                mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
+                while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
+                    more = sendRequest(ObexHelper.OBEX_OPCODE_GET);
+                }
+                // For GET we need to loop until all headers have been sent,
+                // And then we wait for the first continue package with the
+                // reply.
+                if (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
+                    mParent.sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL,
+                            null, mReplyHeader, mPrivateInput, mSrmActive);
+                }
+                if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
                     mOperationDone = true;
+                } else {
+                    checkForSrm();
                 }
             }
         } else {
-
+            // PUT operation
             if (!mOperationDone) {
                 mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
                 while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
-                    more = sendRequest(0x02);
-
+                    more = sendRequest(ObexHelper.OBEX_OPCODE_PUT);
                 }
             }
 
             if (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
-                mParent.sendRequest(0x82, null, mReplyHeader, mPrivateInput);
+                mParent.sendRequest(ObexHelper.OBEX_OPCODE_PUT_FINAL,
+                        null, mReplyHeader, mPrivateInput, mSrmActive);
             }
 
             if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
@@ -617,15 +680,21 @@
     public synchronized boolean continueOperation(boolean sendEmpty, boolean inStream)
             throws IOException {
 
+        // One path to the first put operation - the other one does not need to
+        // handle SRM, as all will fit into one packet.
+
         if (mGetOperation) {
             if ((inStream) && (!mOperationDone)) {
                 // to deal with inputstream in get operation
-                mParent.sendRequest(0x83, null, mReplyHeader, mPrivateInput);
+                mParent.sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL,
+                        null, mReplyHeader, mPrivateInput, mSrmActive);
                 /*
                   * Determine if that was not the last packet in the operation
                   */
                 if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
                     mOperationDone = true;
+                } else {
+                    checkForSrm();
                 }
 
                 return true;
@@ -636,16 +705,7 @@
                 if (mPrivateInput == null) {
                     mPrivateInput = new PrivateInputStream(this);
                 }
-
-                if (!mGetFinalFlag) {
-                    sendRequest(0x03);
-                } else {
-                    sendRequest(0x83);
-
-                    if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
-                        mOperationDone = true;
-                    }
-                }
+                sendRequest(ObexHelper.OBEX_OPCODE_GET);
                 return true;
 
             } else if (mOperationDone) {
@@ -653,12 +713,13 @@
             }
 
         } else {
+            // PUT operation
             if ((!inStream) && (!mOperationDone)) {
                 // to deal with outputstream in put operation
                 if (mReplyHeader.responseCode == -1) {
                     mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
                 }
-                sendRequest(0x02);
+                sendRequest(ObexHelper.OBEX_OPCODE_PUT);
                 return true;
             } else if ((inStream) && (!mOperationDone)) {
                 // How to deal with inputstream  in put operation ?
@@ -696,7 +757,7 @@
                 }
 
                 while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
-                    more = sendRequest(0x02);
+                    more = sendRequest(ObexHelper.OBEX_OPCODE_PUT);
                 }
 
                 /*
@@ -706,7 +767,7 @@
                  */
                 while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
 
-                    sendRequest(0x82);
+                    sendRequest(ObexHelper.OBEX_OPCODE_PUT_FINAL);
                 }
                 mOperationDone = true;
             } else if ((inStream) && (mOperationDone)) {
@@ -724,12 +785,14 @@
                 }
 
                 while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
-                    if (!sendRequest(0x83)) {
+                    if (!sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL)) {
                         break;
                     }
                 }
                 while (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE) {
-                    mParent.sendRequest(0x83, null, mReplyHeader, mPrivateInput);
+                    mParent.sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL, null,
+                            mReplyHeader, mPrivateInput, false);
+                    // Regardless of the SRM state, wait for the response.
                 }
                 mOperationDone = true;
             } else if ((!inStream) && (!mOperationDone)) {
@@ -752,9 +815,9 @@
 
                 mReplyHeader.responseCode = ResponseCodes.OBEX_HTTP_CONTINUE;
                 while ((more) && (mReplyHeader.responseCode == ResponseCodes.OBEX_HTTP_CONTINUE)) {
-                    more = sendRequest(0x03);
+                    more = sendRequest(ObexHelper.OBEX_OPCODE_GET);
                 }
-                sendRequest(0x83);
+                sendRequest(ObexHelper.OBEX_OPCODE_GET_FINAL);
                 //                parent.sendRequest(0x83, null, replyHeaders, privateInput);
                 if (mReplyHeader.responseCode != ResponseCodes.OBEX_HTTP_CONTINUE) {
                     mOperationDone = true;
@@ -764,5 +827,6 @@
     }
 
     public void noBodyHeader(){
+        mSendBodyHeader = false;
     }
 }
diff --git a/obex/javax/obex/ClientSession.java b/obex/javax/obex/ClientSession.java
index 27d8976..272a920 100644
--- a/obex/javax/obex/ClientSession.java
+++ b/obex/javax/obex/ClientSession.java
@@ -1,4 +1,6 @@
 /*
+ * Copyright (c) 2015 The Android Open Source Project
+ * Copyright (C) 2015 Samsung LSI
  * Copyright (c) 2008-2009, Motorola, Inc.
  *
  * All rights reserved.
@@ -37,12 +39,16 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 
+import android.util.Log;
+
 /**
  * This class in an implementation of the OBEX ClientSession.
  * @hide
  */
 public final class ClientSession extends ObexSession {
 
+    private static final String TAG = "ClientSession";
+
     private boolean mOpen;
 
     // Determines if an OBEX layer connection has been established
@@ -51,10 +57,10 @@
     private byte[] mConnectionId = null;
 
     /*
-     * The max Packet size must be at least 256 according to the OBEX
+     * The max Packet size must be at least 255 according to the OBEX
      * specification.
      */
-    private int maxPacketSize = 256;
+    private int mMaxTxPacketSize = ObexHelper.LOWER_LIMIT_MAX_PACKET_SIZE;
 
     private boolean mRequestActive;
 
@@ -62,11 +68,33 @@
 
     private final OutputStream mOutput;
 
+    private final boolean mLocalSrmSupported;
+
+    private final ObexTransport mTransport;
+
     public ClientSession(final ObexTransport trans) throws IOException {
         mInput = trans.openInputStream();
         mOutput = trans.openOutputStream();
         mOpen = true;
         mRequestActive = false;
+        mLocalSrmSupported = trans.isSrmSupported();
+        mTransport = trans;
+    }
+
+    /**
+     * Create a ClientSession
+     * @param trans The transport to use for OBEX transactions
+     * @param supportsSrm True if Single Response Mode should be used e.g. if the
+     *        supplied transport is a TCP or l2cap channel.
+     * @throws IOException if it occurs while opening the transport streams.
+     */
+    public ClientSession(final ObexTransport trans, final boolean supportsSrm) throws IOException {
+        mInput = trans.openInputStream();
+        mOutput = trans.openOutputStream();
+        mOpen = true;
+        mRequestActive = false;
+        mLocalSrmSupported = supportsSrm;
+        mTransport = trans;
     }
 
     public HeaderSet connect(final HeaderSet header) throws IOException {
@@ -98,23 +126,25 @@
         * Byte 7 to n: headers
         */
         byte[] requestPacket = new byte[totalLength];
+        int maxRxPacketSize = ObexHelper.getMaxRxPacketSize(mTransport);
         // We just need to start at  byte 3 since the sendRequest() method will
         // handle the length and 0x80.
         requestPacket[0] = (byte)0x10;
         requestPacket[1] = (byte)0x00;
-        requestPacket[2] = (byte)(ObexHelper.MAX_PACKET_SIZE_INT >> 8);
-        requestPacket[3] = (byte)(ObexHelper.MAX_PACKET_SIZE_INT & 0xFF);
+        requestPacket[2] = (byte)(maxRxPacketSize >> 8);
+        requestPacket[3] = (byte)(maxRxPacketSize & 0xFF);
         if (head != null) {
             System.arraycopy(head, 0, requestPacket, 4, head.length);
         }
 
-        // check with local max packet size
+        // Since we are not yet connected, the peer max packet size is unknown,
+        // hence we are only guaranteed the server will use the first 7 bytes.
         if ((requestPacket.length + 3) > ObexHelper.MAX_PACKET_SIZE_INT) {
-            throw new IOException("Packet size exceeds max packet size");
+            throw new IOException("Packet size exceeds max packet size for connect");
         }
 
         HeaderSet returnHeaderSet = new HeaderSet();
-        sendRequest(ObexHelper.OBEX_OPCODE_CONNECT, requestPacket, returnHeaderSet, null);
+        sendRequest(ObexHelper.OBEX_OPCODE_CONNECT, requestPacket, returnHeaderSet, null, false);
 
         /*
         * Read the response from the OBEX server.
@@ -158,7 +188,18 @@
             System.arraycopy(mConnectionId, 0, head.mConnectionID, 0, 4);
         }
 
-        return new ClientOperation(maxPacketSize, this, head, true);
+        if(mLocalSrmSupported) {
+            head.setHeader(HeaderSet.SINGLE_RESPONSE_MODE, ObexHelper.OBEX_SRM_ENABLE);
+            /* TODO: Consider creating an interface to get the wait state.
+             * On an android system, I cannot see when this is to be used.
+             * except perhaps if we are to wait for user accept on a push message.
+            if(getLocalWaitState()) {
+                head.setHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER, ObexHelper.OBEX_SRMP_WAIT);
+            }
+            */
+        }
+
+        return new ClientOperation(mMaxTxPacketSize, this, head, true);
     }
 
     /**
@@ -202,7 +243,7 @@
             }
             head = ObexHelper.createHeader(header, false);
 
-            if ((head.length + 3) > maxPacketSize) {
+            if ((head.length + 3) > mMaxTxPacketSize) {
                 throw new IOException("Packet size exceeds max packet size");
             }
         } else {
@@ -215,7 +256,7 @@
         }
 
         HeaderSet returnHeaderSet = new HeaderSet();
-        sendRequest(ObexHelper.OBEX_OPCODE_DISCONNECT, head, returnHeaderSet, null);
+        sendRequest(ObexHelper.OBEX_OPCODE_DISCONNECT, head, returnHeaderSet, null, false);
 
         /*
          * An OBEX DISCONNECT reply from the server:
@@ -269,7 +310,16 @@
             System.arraycopy(mConnectionId, 0, head.mConnectionID, 0, 4);
         }
 
-        return new ClientOperation(maxPacketSize, this, head, false);
+        if(mLocalSrmSupported) {
+            head.setHeader(HeaderSet.SINGLE_RESPONSE_MODE, ObexHelper.OBEX_SRM_ENABLE);
+            /* TODO: Consider creating an interface to get the wait state.
+             * On an android system, I cannot see when this is to be used.
+            if(getLocalWaitState()) {
+                head.setHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER, ObexHelper.OBEX_SRMP_WAIT);
+            }
+             */
+        }
+        return new ClientOperation(mMaxTxPacketSize, this, head, false);
     }
 
     public void setAuthenticator(Authenticator auth) throws IOException {
@@ -314,7 +364,7 @@
         head = ObexHelper.createHeader(headset, false);
         totalLength += head.length;
 
-        if (totalLength > maxPacketSize) {
+        if (totalLength > mMaxTxPacketSize) {
             throw new IOException("Packet size exceeds max packet size");
         }
 
@@ -348,7 +398,7 @@
         }
 
         HeaderSet returnHeaderSet = new HeaderSet();
-        sendRequest(ObexHelper.OBEX_OPCODE_SETPATH, packet, returnHeaderSet, null);
+        sendRequest(ObexHelper.OBEX_OPCODE_SETPATH, packet, returnHeaderSet, null, false);
 
         /*
          * An OBEX SETPATH reply from the server:
@@ -400,20 +450,40 @@
      * @param head the headers to send to the client
      * @param header the header object to update with the response
      * @param privateInput the input stream used by the Operation object; null
-     *        if this is called on a CONNECT, SETPATH or DISCONNECT return
+     *        if this is called on a CONNECT, SETPATH or DISCONNECT
+     * @return
      *        <code>true</code> if the operation completed successfully;
      *        <code>false</code> if an authentication response failed to pass
      * @throws IOException if an IO error occurs
      */
     public boolean sendRequest(int opCode, byte[] head, HeaderSet header,
-            PrivateInputStream privateInput) throws IOException {
+            PrivateInputStream privateInput, boolean srmActive) throws IOException {
         //check header length with local max size
         if (head != null) {
             if ((head.length + 3) > ObexHelper.MAX_PACKET_SIZE_INT) {
+                // TODO: This is an implementation limit - not a specification requirement.
                 throw new IOException("header too large ");
             }
         }
 
+        boolean skipSend = false;
+        boolean skipReceive = false;
+        if (srmActive == true) {
+            if (opCode == ObexHelper.OBEX_OPCODE_PUT) {
+                // we are in the middle of a SRM PUT operation, don't expect a continue.
+                skipReceive = true;
+            } else if (opCode == ObexHelper.OBEX_OPCODE_GET) {
+                // We are still sending the get request, send, but don't expect continue
+                // until the request is transfered (the final bit is set)
+                skipReceive = true;
+            } else if (opCode == ObexHelper.OBEX_OPCODE_GET_FINAL) {
+                // All done sending the request, expect data from the server, without
+                // sending continue.
+                skipSend = true;
+            }
+
+        }
+
         int bytesReceived;
         ByteArrayOutputStream out = new ByteArrayOutputStream();
         out.write((byte)opCode);
@@ -428,86 +498,105 @@
             out.write(head);
         }
 
-        // Write the request to the output stream and flush the stream
-        mOutput.write(out.toByteArray());
-        mOutput.flush();
-
-        header.responseCode = mInput.read();
-
-        int length = ((mInput.read() << 8) | (mInput.read()));
-
-        if (length > ObexHelper.MAX_PACKET_SIZE_INT) {
-            throw new IOException("Packet received exceeds packet size limit");
+        if (!skipSend) {
+            // Write the request to the output stream and flush the stream
+            mOutput.write(out.toByteArray());
+            // TODO: is this really needed? if this flush is implemented
+            //       correctly, we will get a gap between each obex packet.
+            //       which is kind of the idea behind SRM to avoid.
+            //  Consider offloading to another thread (async action)
+            mOutput.flush();
         }
-        if (length > ObexHelper.BASE_PACKET_LENGTH) {
-            byte[] data = null;
-            if (opCode == ObexHelper.OBEX_OPCODE_CONNECT) {
-                @SuppressWarnings("unused")
-                int version = mInput.read();
-                @SuppressWarnings("unused")
-                int flags = mInput.read();
-                maxPacketSize = (mInput.read() << 8) + mInput.read();
 
-                //check with local max size
-                if (maxPacketSize > ObexHelper.MAX_CLIENT_PACKET_SIZE) {
-                    maxPacketSize = ObexHelper.MAX_CLIENT_PACKET_SIZE;
-                }
+        if (!skipReceive) {
+            header.responseCode = mInput.read();
 
-                if (length > 7) {
-                    data = new byte[length - 7];
+            int length = ((mInput.read() << 8) | (mInput.read()));
 
-                    bytesReceived = mInput.read(data);
-                    while (bytesReceived != (length - 7)) {
-                        bytesReceived += mInput.read(data, bytesReceived, data.length
-                                - bytesReceived);
+            if (length > ObexHelper.getMaxRxPacketSize(mTransport)) {
+                throw new IOException("Packet received exceeds packet size limit");
+            }
+            if (length > ObexHelper.BASE_PACKET_LENGTH) {
+                byte[] data = null;
+                if (opCode == ObexHelper.OBEX_OPCODE_CONNECT) {
+                    @SuppressWarnings("unused")
+                    int version = mInput.read();
+                    @SuppressWarnings("unused")
+                    int flags = mInput.read();
+                    mMaxTxPacketSize = (mInput.read() << 8) + mInput.read();
+
+                    //check with local max size
+                    if (mMaxTxPacketSize > ObexHelper.MAX_CLIENT_PACKET_SIZE) {
+                        mMaxTxPacketSize = ObexHelper.MAX_CLIENT_PACKET_SIZE;
+                    }
+
+                    // check with transport maximum size
+                    if(mMaxTxPacketSize > ObexHelper.getMaxTxPacketSize(mTransport)) {
+                        // To increase this size, increase the buffer size in L2CAP layer
+                        // in Bluedroid.
+                        Log.w(TAG, "An OBEX packet size of " + mMaxTxPacketSize + "was"
+                                + " requested. Transport only allows: "
+                                + ObexHelper.getMaxTxPacketSize(mTransport)
+                                + " Lowering limit to this value.");
+                        mMaxTxPacketSize = ObexHelper.getMaxTxPacketSize(mTransport);
+                    }
+
+                    if (length > 7) {
+                        data = new byte[length - 7];
+
+                        bytesReceived = mInput.read(data);
+                        while (bytesReceived != (length - 7)) {
+                            bytesReceived += mInput.read(data, bytesReceived, data.length
+                                    - bytesReceived);
+                        }
+                    } else {
+                        return true;
                     }
                 } else {
-                    return true;
+                    data = new byte[length - 3];
+                    bytesReceived = mInput.read(data);
+
+                    while (bytesReceived != (length - 3)) {
+                        bytesReceived += mInput.read(data, bytesReceived, data.length - bytesReceived);
+                    }
+                    if (opCode == ObexHelper.OBEX_OPCODE_ABORT) {
+                        return true;
+                    }
                 }
-            } else {
-                data = new byte[length - 3];
-                bytesReceived = mInput.read(data);
 
-                while (bytesReceived != (length - 3)) {
-                    bytesReceived += mInput.read(data, bytesReceived, data.length - bytesReceived);
+                byte[] body = ObexHelper.updateHeaderSet(header, data);
+                if ((privateInput != null) && (body != null)) {
+                    privateInput.writeBytes(body, 1);
                 }
-                if (opCode == ObexHelper.OBEX_OPCODE_ABORT) {
-                    return true;
+
+                if (header.mConnectionID != null) {
+                    mConnectionId = new byte[4];
+                    System.arraycopy(header.mConnectionID, 0, mConnectionId, 0, 4);
                 }
-            }
 
-            byte[] body = ObexHelper.updateHeaderSet(header, data);
-            if ((privateInput != null) && (body != null)) {
-                privateInput.writeBytes(body, 1);
-            }
-
-            if (header.mConnectionID != null) {
-                mConnectionId = new byte[4];
-                System.arraycopy(header.mConnectionID, 0, mConnectionId, 0, 4);
-            }
-
-            if (header.mAuthResp != null) {
-                if (!handleAuthResp(header.mAuthResp)) {
-                    setRequestInactive();
-                    throw new IOException("Authentication Failed");
+                if (header.mAuthResp != null) {
+                    if (!handleAuthResp(header.mAuthResp)) {
+                        setRequestInactive();
+                        throw new IOException("Authentication Failed");
+                    }
                 }
-            }
 
-            if ((header.responseCode == ResponseCodes.OBEX_HTTP_UNAUTHORIZED)
-                    && (header.mAuthChall != null)) {
+                if ((header.responseCode == ResponseCodes.OBEX_HTTP_UNAUTHORIZED)
+                        && (header.mAuthChall != null)) {
 
-                if (handleAuthChall(header)) {
-                    out.write((byte)HeaderSet.AUTH_RESPONSE);
-                    out.write((byte)((header.mAuthResp.length + 3) >> 8));
-                    out.write((byte)(header.mAuthResp.length + 3));
-                    out.write(header.mAuthResp);
-                    header.mAuthChall = null;
-                    header.mAuthResp = null;
+                    if (handleAuthChall(header)) {
+                        out.write((byte)HeaderSet.AUTH_RESPONSE);
+                        out.write((byte)((header.mAuthResp.length + 3) >> 8));
+                        out.write((byte)(header.mAuthResp.length + 3));
+                        out.write(header.mAuthResp);
+                        header.mAuthChall = null;
+                        header.mAuthResp = null;
 
-                    byte[] sendHeaders = new byte[out.size() - 3];
-                    System.arraycopy(out.toByteArray(), 3, sendHeaders, 0, sendHeaders.length);
+                        byte[] sendHeaders = new byte[out.size() - 3];
+                        System.arraycopy(out.toByteArray(), 3, sendHeaders, 0, sendHeaders.length);
 
-                    return sendRequest(opCode, sendHeaders, header, privateInput);
+                        return sendRequest(opCode, sendHeaders, header, privateInput, false);
+                    }
                 }
             }
         }
@@ -520,4 +609,8 @@
         mInput.close();
         mOutput.close();
     }
+
+    public boolean isSrmSupported() {
+        return mLocalSrmSupported;
+    }
 }
diff --git a/obex/javax/obex/HeaderSet.java b/obex/javax/obex/HeaderSet.java
index 51b560a..35fe186 100644
--- a/obex/javax/obex/HeaderSet.java
+++ b/obex/javax/obex/HeaderSet.java
@@ -40,7 +40,7 @@
 
 /**
  * This class implements the javax.obex.HeaderSet interface for OBEX over
- * RFCOMM.
+ * RFCOMM or OBEX over l2cap.
  * @hide
  */
 public final class HeaderSet {
@@ -178,6 +178,22 @@
      */
     public static final int OBJECT_CLASS = 0x4F;
 
+    /**
+     * Represents the OBEX Single Response Mode (SRM). This header is used
+     * for Single response mode, introduced in OBEX 1.5.
+     * <P>
+     * The value of <code>SINGLE_RESPONSE_MODE</code> is 0x97 (151).
+     */
+    public static final int SINGLE_RESPONSE_MODE = 0x97;
+
+    /**
+     * Represents the OBEX Single Response Mode Parameters. This header is used
+     * for Single response mode, introduced in OBEX 1.5.
+     * <P>
+     * The value of <code>SINGLE_RESPONSE_MODE_PARAMETER</code> is 0x98 (152).
+     */
+    public static final int SINGLE_RESPONSE_MODE_PARAMETER = 0x98;
+
     private Long mCount; // 4 byte unsigned integer
 
     private String mName; // null terminated Unicode text string
@@ -204,7 +220,7 @@
 
     private byte[] mObjectClass; // byte sequence
 
-    private String[] mUnicodeUserDefined; //null terminated unicode string
+    private String[] mUnicodeUserDefined; // null terminated unicode string
 
     private byte[][] mSequenceUserDefined; // byte sequence user defined
 
@@ -212,7 +228,12 @@
 
     private Long[] mIntegerUserDefined; // 4 byte unsigned integer
 
-    private final SecureRandom mRandom;
+    private SecureRandom mRandom = null;
+
+    private Byte mSingleResponseMode; // byte to indicate enable/disable/support for SRM
+
+    private Byte mSrmParam; // byte representing the SRM parameters - only "wait"
+                            // is supported by Bluetooth
 
     /*package*/ byte[] nonce;
 
@@ -234,7 +255,6 @@
         mByteUserDefined = new Byte[16];
         mIntegerUserDefined = new Long[16];
         responseCode = -1;
-        mRandom = new SecureRandom();
     }
 
     /**
@@ -393,6 +413,30 @@
                     }
                 }
                 break;
+            case SINGLE_RESPONSE_MODE:
+                if (headerValue == null) {
+                    mSingleResponseMode = null;
+                } else {
+                    if (!(headerValue instanceof Byte)) {
+                        throw new IllegalArgumentException(
+                                "Single Response Mode must be a Byte");
+                    } else {
+                        mSingleResponseMode = (Byte)headerValue;
+                    }
+                }
+                break;
+            case SINGLE_RESPONSE_MODE_PARAMETER:
+                if (headerValue == null) {
+                    mSrmParam = null;
+                } else {
+                    if (!(headerValue instanceof Byte)) {
+                        throw new IllegalArgumentException(
+                                "Single Response Mode Parameter must be a Byte");
+                    } else {
+                        mSrmParam = (Byte)headerValue;
+                    }
+                }
+                break;
             default:
                 // Verify that it was not a Unicode String user Defined
                 if ((headerID >= 0x30) && (headerID <= 0x3F)) {
@@ -493,6 +537,10 @@
                 return mObjectClass;
             case APPLICATION_PARAMETER:
                 return mAppParam;
+            case SINGLE_RESPONSE_MODE:
+                return mSingleResponseMode;
+            case SINGLE_RESPONSE_MODE_PARAMETER:
+                return mSrmParam;
             default:
                 // Verify that it was not a Unicode String user Defined
                 if ((headerID >= 0x30) && (headerID <= 0x3F)) {
@@ -564,6 +612,12 @@
         if (mObjectClass != null) {
             out.write(OBJECT_CLASS);
         }
+        if(mSingleResponseMode != null) {
+            out.write(SINGLE_RESPONSE_MODE);
+        }
+        if(mSrmParam != null) {
+            out.write(SINGLE_RESPONSE_MODE_PARAMETER);
+        }
 
         for (int i = 0x30; i < 0x40; i++) {
             if (mUnicodeUserDefined[i - 0x30] != null) {
@@ -625,6 +679,9 @@
             throws IOException {
 
         nonce = new byte[16];
+        if(mRandom == null) {
+            mRandom = new SecureRandom();
+        }
         for (int i = 0; i < 16; i++) {
             nonce[i] = (byte)mRandom.nextInt();
         }
diff --git a/obex/javax/obex/ObexHelper.java b/obex/javax/obex/ObexHelper.java
index 0a06709..fa50943 100644
--- a/obex/javax/obex/ObexHelper.java
+++ b/obex/javax/obex/ObexHelper.java
@@ -1,5 +1,6 @@
 /*
- * Copyright (C) 2014 The Android Open Source Project
+ * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2015 Samsung LSI
  * Copyright (c) 2008-2009, Motorola, Inc.
  *
  * All rights reserved.
@@ -42,12 +43,16 @@
 import java.util.Date;
 import java.util.TimeZone;
 
+import android.util.Log;
+
 /**
  * This class defines a set of helper methods for the implementation of Obex.
  * @hide
  */
 public final class ObexHelper {
 
+    private static final String TAG = "ObexHelper";
+    public static final boolean VDBG = false;
     /**
      * Defines the basic packet length used by OBEX. Every OBEX packet has the
      * same basic format:<BR>
@@ -65,18 +70,24 @@
      * should be the Max incoming MTU minus TODO: L2CAP package headers and
      * RFCOMM package headers. TODO: Retrieve the max incoming MTU from TODO:
      * LocalDevice.getProperty().
+     * NOTE: This value must be larger than or equal to the L2CAP SDU
      */
     /*
      * android note set as 0xFFFE to match remote MPS
      */
     public static final int MAX_PACKET_SIZE_INT = 0xFFFE;
 
+    // The minimum allowed max packet size is 255 according to the OBEX specification
+    public static final int LOWER_LIMIT_MAX_PACKET_SIZE = 255;
+
     /**
      * Temporary workaround to be able to push files to Windows 7.
      * TODO: Should be removed as soon as Microsoft updates their driver.
      */
     public static final int MAX_CLIENT_PACKET_SIZE = 0xFC00;
 
+    public static final int OBEX_OPCODE_FINAL_BIT_MASK = 0x80;
+
     public static final int OBEX_OPCODE_CONNECT = 0x80;
 
     public static final int OBEX_OPCODE_DISCONNECT = 0x81;
@@ -119,6 +130,12 @@
 
     public static final int OBEX_AUTH_REALM_CHARSET_UNICODE = 0xFF;
 
+    public static final byte OBEX_SRM_ENABLE         = 0x01; // For BT we only need enable/disable
+    public static final byte OBEX_SRM_DISABLE        = 0x00;
+    public static final byte OBEX_SRM_SUPPORT        = 0x02; // Unused for now
+
+    public static final byte OBEX_SRMP_WAIT          = 0x01; // Only SRMP value used by BT
+
     /**
      * Updates the HeaderSet with the headers received in the byte array
      * provided. Invalid headers are ignored.
@@ -314,7 +331,7 @@
                             }
                         } catch (Exception e) {
                             // Not a valid header so ignore
-                            throw new IOException("Header was not formatted properly");
+                            throw new IOException("Header was not formatted properly", e);
                         }
                         index += 4;
                         break;
@@ -322,7 +339,7 @@
 
             }
         } catch (IOException e) {
-            throw new IOException("Header was not formatted properly");
+            throw new IOException("Header was not formatted properly", e);
         }
 
         return body;
@@ -672,6 +689,33 @@
                 }
             }
 
+            // TODO:
+            // If the SRM and SRMP header is in use, they must be send in the same OBEX packet
+            // But the current structure of the obex code cannot handle this, and therefore
+            // it makes sense to put them in the tail of the headers, since we then reduce the
+            // chance of enabling SRM to soon. The down side is that SRM cannot be used while
+            // transferring non-body headers
+
+            // Add the SRM header
+            byteHeader = (Byte)headImpl.getHeader(HeaderSet.SINGLE_RESPONSE_MODE);
+            if (byteHeader != null) {
+                out.write((byte)HeaderSet.SINGLE_RESPONSE_MODE);
+                out.write(byteHeader.byteValue());
+                if (nullOut) {
+                    headImpl.setHeader(HeaderSet.SINGLE_RESPONSE_MODE, null);
+                }
+            }
+
+            // Add the SRM parameter header
+            byteHeader = (Byte)headImpl.getHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER);
+            if (byteHeader != null) {
+                out.write((byte)HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER);
+                out.write(byteHeader.byteValue());
+                if (nullOut) {
+                    headImpl.setHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER, null);
+                }
+            }
+
         } catch (IOException e) {
         } finally {
             result = out.toByteArray();
@@ -702,6 +746,8 @@
         int index = start;
         int length = 0;
 
+        // TODO: Ensure SRM and SRMP headers are not split into two OBEX packets
+
         while ((fullLength < maxSize) && (index < headerArray.length)) {
             int headerID = (headerArray[index] < 0 ? headerArray[index] + 256 : headerArray[index]);
             lastLength = fullLength;
@@ -1008,4 +1054,39 @@
 
         return authChall;
     }
+
+    /**
+     * Return the maximum allowed OBEX packet to transmit.
+     * OBEX packets transmitted must be smaller than this value.
+     * @param transport Reference to the ObexTransport in use.
+     * @return the maximum allowed OBEX packet to transmit
+     */
+    public static int getMaxTxPacketSize(ObexTransport transport) {
+        int size = transport.getMaxTransmitPacketSize();
+        return validateMaxPacketSize(size);
+    }
+
+    /**
+     * Return the maximum allowed OBEX packet to receive - used in OBEX connect.
+     * @param transport
+     * @return he maximum allowed OBEX packet to receive
+     */
+    public static int getMaxRxPacketSize(ObexTransport transport) {
+        int size = transport.getMaxReceivePacketSize();
+        return validateMaxPacketSize(size);
+    }
+
+    private static int validateMaxPacketSize(int size) {
+        if(VDBG && (size > MAX_PACKET_SIZE_INT)) Log.w(TAG,
+                "The packet size supported for the connection (" + size + ") is larger"
+                + " than the configured OBEX packet size: " + MAX_PACKET_SIZE_INT);
+        if(size != -1) {
+            if(size < LOWER_LIMIT_MAX_PACKET_SIZE) {
+                throw new IllegalArgumentException(size + " is less that the lower limit: "
+                        + LOWER_LIMIT_MAX_PACKET_SIZE);
+            }
+            return size;
+        }
+        return MAX_PACKET_SIZE_INT;
+    }
 }
diff --git a/obex/javax/obex/ObexPacket.java b/obex/javax/obex/ObexPacket.java
new file mode 100644
index 0000000..bb6c96e
--- /dev/null
+++ b/obex/javax/obex/ObexPacket.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2015 The Android Open Source Project
+ * Copyright (c) 2015 Samsung LSI
+ *
+ * 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 javax.obex;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ObexPacket {
+    public int mHeaderId;
+    public int mLength;
+    public byte[] mPayload = null;
+
+    private ObexPacket(int headerId, int length) {
+        mHeaderId = headerId;
+        mLength = length;
+    }
+
+    /**
+     * Create a complete OBEX packet by reading data from an InputStream.
+     * @param is the input stream to read from.
+     * @return the OBEX packet read.
+     * @throws IOException if an IO exception occurs during read.
+     */
+    public static ObexPacket read(InputStream is) throws IOException {
+        int headerId = is.read();
+        return read(headerId, is);
+    }
+
+    /**
+     * Read the remainder of an OBEX packet, with a specified headerId.
+     * @param headerId the headerId already read from the stream.
+     * @param is the stream to read from, assuming 1 byte have already been read.
+     * @return the OBEX packet read.
+     * @throws IOException
+     */
+    public static ObexPacket read(int headerId, InputStream is) throws IOException {
+        // Read the 2 byte length field from the stream
+        int length = is.read();
+        length = (length << 8) + is.read();
+
+        ObexPacket newPacket = new ObexPacket(headerId, length);
+
+        int bytesReceived;
+        byte[] temp = null;
+        if (length > 3) {
+            // First three bytes already read, compensating for this
+            temp = new byte[length - 3];
+            bytesReceived = is.read(temp);
+            while (bytesReceived != temp.length) {
+                bytesReceived += is.read(temp, bytesReceived, temp.length - bytesReceived);
+            }
+        }
+        newPacket.mPayload = temp;
+        return newPacket;
+    }
+}
diff --git a/obex/javax/obex/ObexSession.java b/obex/javax/obex/ObexSession.java
index a7daeb5..542b9c8 100644
--- a/obex/javax/obex/ObexSession.java
+++ b/obex/javax/obex/ObexSession.java
@@ -34,6 +34,8 @@
 
 import java.io.IOException;
 
+import android.util.Log;
+
 /**
  * The <code>ObexSession</code> interface characterizes the term
  * "OBEX Connection" as defined in the IrDA Object Exchange Protocol v1.2, which
@@ -47,6 +49,9 @@
  */
 public class ObexSession {
 
+    private static final String TAG = "ObexSession";
+    private static final boolean V = ObexHelper.VDBG;
+
     protected Authenticator mAuthenticator;
 
     protected byte[] mChallengeDigest;
@@ -125,6 +130,7 @@
             result = mAuthenticator
                     .onAuthenticationChallenge(realm, isUserIDRequired, isFullAccess);
         } catch (Exception e) {
+            if (V) Log.d(TAG, "Exception occured - returning false", e);
             return false;
         }
 
diff --git a/obex/javax/obex/ObexTransport.java b/obex/javax/obex/ObexTransport.java
index 445e267..a5a75f5 100644
--- a/obex/javax/obex/ObexTransport.java
+++ b/obex/javax/obex/ObexTransport.java
@@ -73,4 +73,39 @@
 
     DataOutputStream openDataOutputStream() throws IOException;
 
+    /**
+     * Must return the maximum allowed OBEX packet that can be sent over
+     * the transport. For L2CAP this will be the Max SDU reported by the
+     * peer device.
+     * The returned value will be used to set the outgoing OBEX packet
+     * size. Therefore this value shall not change.
+     * For RFCOMM or other transport types where the OBEX packets size
+     * is unrelated to the transport packet size, return -1;
+     * @return the maximum allowed OBEX packet that can be send over
+     *         the transport. Or -1 in case of don't care.
+     */
+    int getMaxTransmitPacketSize();
+
+    /**
+     * Must return the maximum allowed OBEX packet that can be received over
+     * the transport. For L2CAP this will be the Max SDU configured for the
+     * L2CAP channel.
+     * The returned value will be used to validate the incoming packet size
+     * values.
+     * For RFCOMM or other transport types where the OBEX packets size
+     * is unrelated to the transport packet size, return -1;
+     * @return the maximum allowed OBEX packet that can be send over
+     *         the transport. Or -1 in case of don't care.
+     */
+    int getMaxReceivePacketSize();
+
+    /**
+     * Shall return true if the transport in use supports SRM.
+     * @return
+     *        <code>true</code> if SRM operation is supported, and is to be enabled.
+     *        <code>false</code> if SRM operations are not supported, or should not be used.
+     */
+    boolean isSrmSupported();
+
+
 }
diff --git a/obex/javax/obex/ServerOperation.java b/obex/javax/obex/ServerOperation.java
index fc441e0..56a675a 100644
--- a/obex/javax/obex/ServerOperation.java
+++ b/obex/javax/obex/ServerOperation.java
@@ -1,4 +1,5 @@
-/*
+/* Copyright (c) 2015 The Android Open Source Project
+ * Copyright (C) 2015 Samsung LSI
  * Copyright (c) 2008-2009, Motorola, Inc.
  *
  * All rights reserved.
@@ -39,6 +40,8 @@
 import java.io.DataOutputStream;
 import java.io.ByteArrayOutputStream;
 
+import android.util.Log;
+
 /**
  * This class implements the Operation interface for server side connections.
  * <P>
@@ -54,6 +57,10 @@
  */
 public final class ServerOperation implements Operation, BaseStream {
 
+    private static final String TAG = "ServerOperation";
+
+    private static final boolean V = ObexHelper.VDBG; // Verbose debugging
+
     public boolean isAborted;
 
     public HeaderSet requestHeader;
@@ -78,6 +85,8 @@
 
     private PrivateOutputStream mPrivateOutput;
 
+    private ObexTransport mTransport;
+
     private boolean mPrivateOutputOpen;
 
     private String mExceptionString;
@@ -89,6 +98,19 @@
     private boolean mHasBody;
 
     private boolean mSendBodyHeader = true;
+    // Assume SRM disabled - needs to be explicit
+    // enabled by client
+    private boolean mSrmEnabled = false;
+    // A latch - when triggered, there is not way back ;-)
+    private boolean mSrmActive = false;
+    // Set to true when a SRM enable response have been send
+    private boolean mSrmResponseSent = false;
+    // keep waiting until final-bit is received in request
+    // to handle the case where the SRM enable header is in
+    // a different OBEX packet than the SRMP header.
+    private boolean mSrmWaitingForRemote = true;
+    // Why should we wait? - currently not exposed to apps.
+    private boolean mSrmLocalWait = false;
 
     /**
      * Creates new ServerOperation
@@ -116,12 +138,14 @@
         mRequestFinished = false;
         mPrivateOutputOpen = false;
         mHasBody = false;
-        int bytesReceived;
+        ObexPacket packet;
+        mTransport = p.getTransport();
 
         /*
          * Determine if this is a PUT request
          */
-        if ((request == 0x02) || (request == 0x82)) {
+        if ((request == ObexHelper.OBEX_OPCODE_PUT) ||
+                (request == ObexHelper.OBEX_OPCODE_PUT_FINAL)) {
             /*
              * It is a PUT request.
              */
@@ -130,13 +154,14 @@
             /*
              * Determine if the final bit is set
              */
-            if ((request & 0x80) == 0) {
+            if ((request & ObexHelper.OBEX_OPCODE_FINAL_BIT_MASK) == 0) {
                 finalBitSet = false;
             } else {
                 finalBitSet = true;
                 mRequestFinished = true;
             }
-        } else if ((request == 0x03) || (request == 0x83)) {
+        } else if ((request == ObexHelper.OBEX_OPCODE_GET) ||
+                (request == ObexHelper.OBEX_OPCODE_GET_FINAL)) {
             /*
              * It is a GET request.
              */
@@ -145,71 +170,32 @@
             // For Get request, final bit set is decided by server side logic
             finalBitSet = false;
 
-            if (request == 0x83) {
+            if (request == ObexHelper.OBEX_OPCODE_GET_FINAL) {
                 mRequestFinished = true;
             }
         } else {
             throw new IOException("ServerOperation can not handle such request");
         }
 
-        int length = in.read();
-        length = (length << 8) + in.read();
+        packet = ObexPacket.read(request, mInput);
 
         /*
          * Determine if the packet length is larger than this device can receive
          */
-        if (length > ObexHelper.MAX_PACKET_SIZE_INT) {
+        if (packet.mLength > ObexHelper.getMaxRxPacketSize(mTransport)) {
             mParent.sendResponse(ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE, null);
-            throw new IOException("Packet received was too large");
+            throw new IOException("Packet received was too large. Length: "
+                    + packet.mLength + " maxLength: " + ObexHelper.getMaxRxPacketSize(mTransport));
         }
 
         /*
          * Determine if any headers were sent in the initial request
          */
-        if (length > 3) {
-            byte[] data = new byte[length - 3];
-            bytesReceived = in.read(data);
-
-            while (bytesReceived != data.length) {
-                bytesReceived += in.read(data, bytesReceived, data.length - bytesReceived);
+        if (packet.mLength > 3) {
+            if(!handleObexPacket(packet)) {
+                return;
             }
-
-            byte[] body = ObexHelper.updateHeaderSet(requestHeader, data);
-
-            if (body != null) {
-                mHasBody = true;
-            }
-
-            if (mListener.getConnectionId() != -1 && requestHeader.mConnectionID != null) {
-                mListener.setConnectionId(ObexHelper.convertToLong(requestHeader.mConnectionID));
-            } else {
-                mListener.setConnectionId(1);
-            }
-
-            if (requestHeader.mAuthResp != null) {
-                if (!mParent.handleAuthResp(requestHeader.mAuthResp)) {
-                    mExceptionString = "Authentication Failed";
-                    mParent.sendResponse(ResponseCodes.OBEX_HTTP_UNAUTHORIZED, null);
-                    mClosed = true;
-                    requestHeader.mAuthResp = null;
-                    return;
-                }
-            }
-
-            if (requestHeader.mAuthChall != null) {
-                mParent.handleAuthChall(requestHeader);
-                // send the  authResp to the client
-                replyHeader.mAuthResp = new byte[requestHeader.mAuthResp.length];
-                System.arraycopy(requestHeader.mAuthResp, 0, replyHeader.mAuthResp, 0,
-                        replyHeader.mAuthResp.length);
-                requestHeader.mAuthResp = null;
-                requestHeader.mAuthChall = null;
-
-            }
-
-            if (body != null) {
-                mPrivateInput.writeBytes(body, 1);
-            } else {
+            if (!mHasBody) {
                 while ((!mGetOperation) && (!finalBitSet)) {
                     sendReply(ResponseCodes.OBEX_HTTP_CONTINUE);
                     if (mPrivateInput.available() > 0) {
@@ -232,6 +218,100 @@
         }
     }
 
+    /**
+     * Parse headers and update member variables
+     * @param packet the received obex packet
+     * @return false for failing authentication - and a OBEX_HTTP_UNAUTHORIZED
+     * response have been send. Else true.
+     * @throws IOException
+     */
+    private boolean handleObexPacket(ObexPacket packet) throws IOException {
+        byte[] body = updateRequestHeaders(packet);
+
+        if (body != null) {
+            mHasBody = true;
+        }
+        if (mListener.getConnectionId() != -1 && requestHeader.mConnectionID != null) {
+            mListener.setConnectionId(ObexHelper
+                    .convertToLong(requestHeader.mConnectionID));
+        } else {
+            mListener.setConnectionId(1);
+        }
+
+        if (requestHeader.mAuthResp != null) {
+            if (!mParent.handleAuthResp(requestHeader.mAuthResp)) {
+                mExceptionString = "Authentication Failed";
+                mParent.sendResponse(ResponseCodes.OBEX_HTTP_UNAUTHORIZED, null);
+                mClosed = true;
+                requestHeader.mAuthResp = null;
+                return false;
+            }
+            requestHeader.mAuthResp = null;
+        }
+
+        if (requestHeader.mAuthChall != null) {
+            mParent.handleAuthChall(requestHeader);
+            // send the auhtResp to the client
+            replyHeader.mAuthResp = new byte[requestHeader.mAuthResp.length];
+            System.arraycopy(requestHeader.mAuthResp, 0, replyHeader.mAuthResp, 0,
+                    replyHeader.mAuthResp.length);
+            requestHeader.mAuthResp = null;
+            requestHeader.mAuthChall = null;
+        }
+
+        if (body != null) {
+            mPrivateInput.writeBytes(body, 1);
+        }
+        return true;
+    }
+
+    /**
+     * Update the request header set, and sniff on SRM headers to update local state.
+     * @param data the OBEX packet data
+     * @return any bytes in a body/end-of-body header returned by {@link ObexHelper.updateHeaderSet}
+     * @throws IOException
+     */
+    private byte[] updateRequestHeaders(ObexPacket packet) throws IOException {
+        byte[] body = null;
+        if (packet.mPayload != null) {
+            body = ObexHelper.updateHeaderSet(requestHeader, packet.mPayload);
+        }
+        Byte srmMode = (Byte)requestHeader.getHeader(HeaderSet.SINGLE_RESPONSE_MODE);
+        if(mTransport.isSrmSupported() && srmMode != null
+                && srmMode == ObexHelper.OBEX_SRM_ENABLE) {
+            mSrmEnabled = true;
+            if(V) Log.d(TAG,"SRM is now ENABLED (but not active) for this operation");
+        }
+        checkForSrmWait(packet.mHeaderId);
+        if((!mSrmWaitingForRemote) && (mSrmEnabled)) {
+            if(V) Log.d(TAG,"SRM is now ACTIVE for this operation");
+            mSrmActive = true;
+        }
+        return body;
+    }
+
+    /**
+     * Call this only when a complete request have been received.
+     * (This is not optimal, but the current design is not really suited to
+     * the way SRM is specified.)
+     */
+    private void checkForSrmWait(int headerId){
+        if (mSrmEnabled && (headerId == ObexHelper.OBEX_OPCODE_GET
+                || headerId == ObexHelper.OBEX_OPCODE_GET_FINAL
+                || headerId == ObexHelper.OBEX_OPCODE_PUT)) {
+            try {
+                mSrmWaitingForRemote = false;
+                Byte srmp = (Byte)requestHeader.getHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER);
+                if(srmp != null && srmp == ObexHelper.OBEX_SRMP_WAIT) {
+                    mSrmWaitingForRemote = true;
+                    // Clear the wait header, as the absents of the header when the final bit is set
+                    // indicates don't wait.
+                    requestHeader.setHeader(HeaderSet.SINGLE_RESPONSE_MODE_PARAMETER, null);
+                }
+            } catch (IOException e) {if(V){Log.w(TAG,"Exception while extracting header",e);}}
+        }
+    }
+
     public boolean isValidBody() {
         return mHasBody;
     }
@@ -274,17 +354,19 @@
 
     /**
      * Sends a reply to the client. If the reply is a OBEX_HTTP_CONTINUE, it
-     * will wait for a response from the client before ending.
+     * will wait for a response from the client before ending unless SRM is active.
      * @param type the response code to send back to the client
      * @return <code>true</code> if the final bit was not set on the reply;
      *         <code>false</code> if no reply was received because the operation
-     *         ended, an abort was received, or the final bit was set in the
-     *         reply
+     *         ended, an abort was received, the final bit was set in the
+     *         reply or SRM is active.
      * @throws IOException if an IO error occurs
      */
     public synchronized boolean sendReply(int type) throws IOException {
         ByteArrayOutputStream out = new ByteArrayOutputStream();
-        int bytesReceived;
+        boolean skipSend = false;
+        boolean skipReceive = false;
+        boolean srmRespSendPending = false;
 
         long id = mListener.getConnectionId();
         if (id == -1) {
@@ -293,7 +375,19 @@
             replyHeader.mConnectionID = ObexHelper.convertToByteArray(id);
         }
 
-        byte[] headerArray = ObexHelper.createHeader(replyHeader, true);
+        if(mSrmEnabled && !mSrmResponseSent) {
+            // As we are not ensured that the SRM enable is in the first OBEX packet
+            // We must check for each reply.
+            if(V)Log.v(TAG, "mSrmEnabled==true, sending SRM enable response.");
+            replyHeader.setHeader(HeaderSet.SINGLE_RESPONSE_MODE, (byte)ObexHelper.OBEX_SRM_ENABLE);
+            srmRespSendPending = true;
+        }
+
+        if(mSrmEnabled && !mGetOperation && mSrmLocalWait) {
+            replyHeader.setHeader(HeaderSet.SINGLE_RESPONSE_MODE, (byte)ObexHelper.OBEX_SRMP_WAIT);
+        }
+
+        byte[] headerArray = ObexHelper.createHeader(replyHeader, true); // This clears the headers
         int bodyLength = -1;
         int orginalBodyLength = -1;
 
@@ -347,6 +441,28 @@
             finalBitSet = true;
         }
 
+        if(mSrmActive) {
+            if(!mGetOperation && type == ResponseCodes.OBEX_HTTP_CONTINUE &&
+                    mSrmResponseSent == true) {
+                // we are in the middle of a SRM PUT operation, don't send a continue.
+                skipSend = true;
+            } else if(mGetOperation && mRequestFinished == false && mSrmResponseSent == true) {
+                // We are still receiving the get request, receive, but don't send continue.
+                skipSend = true;
+            } else if(mGetOperation && mRequestFinished == true) {
+                // All done receiving the GET request, send data to the client, without
+                // expecting a continue.
+                skipReceive = true;
+            }
+            if(V)Log.v(TAG, "type==" + type + " skipSend==" + skipSend
+                    + " skipReceive==" + skipReceive);
+        }
+        if(srmRespSendPending) {
+            if(V)Log.v(TAG,
+                    "SRM Enabled (srmRespSendPending == true)- sending SRM Enable response");
+            mSrmResponseSent = true;
+        }
+
         if ((finalBitSet) || (headerArray.length < (mMaxPacketLength - 20))) {
             if (bodyLength > 0) {
                 /*
@@ -387,7 +503,7 @@
         }
 
         if ((finalBitSet) && (type == ResponseCodes.OBEX_HTTP_OK) && (orginalBodyLength <= 0)) {
-            if(mSendBodyHeader == true) {
+            if(mSendBodyHeader) {
                 out.write(0x49);
                 orginalBodyLength = 3;
                 out.write((byte)(orginalBodyLength >> 8));
@@ -395,107 +511,66 @@
             }
         }
 
-        mResponseSize = 3;
-        mParent.sendResponse(type, out.toByteArray());
+        if(skipSend == false) {
+            mResponseSize = 3;
+            mParent.sendResponse(type, out.toByteArray());
+        }
 
         if (type == ResponseCodes.OBEX_HTTP_CONTINUE) {
-            int headerID = mInput.read();
-            int length = mInput.read();
-            length = (length << 8) + mInput.read();
-            if ((headerID != ObexHelper.OBEX_OPCODE_PUT)
-                    && (headerID != ObexHelper.OBEX_OPCODE_PUT_FINAL)
-                    && (headerID != ObexHelper.OBEX_OPCODE_GET)
-                    && (headerID != ObexHelper.OBEX_OPCODE_GET_FINAL)) {
 
-                if (length > 3) {
-                    byte[] temp = new byte[length - 3];
-                    // First three bytes already read, compensating for this
-                    bytesReceived = mInput.read(temp);
-
-                    while (bytesReceived != temp.length) {
-                        bytesReceived += mInput.read(temp, bytesReceived,
-                                temp.length - bytesReceived);
-                    }
-                }
-
-                /*
-                 * Determine if an ABORT was sent as the reply
-                 */
-                if (headerID == ObexHelper.OBEX_OPCODE_ABORT) {
-                    mParent.sendResponse(ResponseCodes.OBEX_HTTP_OK, null);
-                    mClosed = true;
-                    isAborted = true;
-                    mExceptionString = "Abort Received";
-                    throw new IOException("Abort Received");
-                } else {
-                    mParent.sendResponse(ResponseCodes.OBEX_HTTP_BAD_REQUEST, null);
-                    mClosed = true;
-                    mExceptionString = "Bad Request Received";
-                    throw new IOException("Bad Request Received");
-                }
+            if(mGetOperation && skipReceive) {
+                // Here we need to check for and handle abort (throw an exception).
+                // Any other signal received should be discarded silently (only on server side)
+                checkSrmRemoteAbort();
             } else {
+                // Receive and handle data (only send reply if !skipSend)
+                // Read a complete OBEX Packet
+                ObexPacket packet = ObexPacket.read(mInput);
 
-                if ((headerID == ObexHelper.OBEX_OPCODE_PUT_FINAL)) {
-                    finalBitSet = true;
-                } else if (headerID == ObexHelper.OBEX_OPCODE_GET_FINAL) {
-                    mRequestFinished = true;
-                }
+                int headerId = packet.mHeaderId;
+                if ((headerId != ObexHelper.OBEX_OPCODE_PUT)
+                        && (headerId != ObexHelper.OBEX_OPCODE_PUT_FINAL)
+                        && (headerId != ObexHelper.OBEX_OPCODE_GET)
+                        && (headerId != ObexHelper.OBEX_OPCODE_GET_FINAL)) {
 
-                /*
-                 * Determine if the packet length is larger then this device can receive
-                 */
-                if (length > ObexHelper.MAX_PACKET_SIZE_INT) {
-                    mParent.sendResponse(ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE, null);
-                    throw new IOException("Packet received was too large");
-                }
-
-                /*
-                 * Determine if any headers were sent in the initial request
-                 */
-                if (length > 3) {
-                    byte[] data = new byte[length - 3];
-                    bytesReceived = mInput.read(data);
-
-                    while (bytesReceived != data.length) {
-                        bytesReceived += mInput.read(data, bytesReceived, data.length
-                                - bytesReceived);
-                    }
-                    byte[] body = ObexHelper.updateHeaderSet(requestHeader, data);
-                    if (body != null) {
-                        mHasBody = true;
-                    }
-                    if (mListener.getConnectionId() != -1 && requestHeader.mConnectionID != null) {
-                        mListener.setConnectionId(ObexHelper
-                                .convertToLong(requestHeader.mConnectionID));
+                    /*
+                     * Determine if an ABORT was sent as the reply
+                     */
+                    if (headerId == ObexHelper.OBEX_OPCODE_ABORT) {
+                        handleRemoteAbort();
                     } else {
-                        mListener.setConnectionId(1);
+                        // TODO:shall we send this if it occurs during SRM? Errata on the subject
+                        mParent.sendResponse(ResponseCodes.OBEX_HTTP_BAD_REQUEST, null);
+                        mClosed = true;
+                        mExceptionString = "Bad Request Received";
+                        throw new IOException("Bad Request Received");
+                    }
+                } else {
+
+                    if ((headerId == ObexHelper.OBEX_OPCODE_PUT_FINAL)) {
+                        finalBitSet = true;
+                    } else if (headerId == ObexHelper.OBEX_OPCODE_GET_FINAL) {
+                        mRequestFinished = true;
                     }
 
-                    if (requestHeader.mAuthResp != null) {
-                        if (!mParent.handleAuthResp(requestHeader.mAuthResp)) {
-                            mExceptionString = "Authentication Failed";
-                            mParent.sendResponse(ResponseCodes.OBEX_HTTP_UNAUTHORIZED, null);
-                            mClosed = true;
-                            requestHeader.mAuthResp = null;
+                    /*
+                     * Determine if the packet length is larger than the negotiated packet size
+                     */
+                    if (packet.mLength > ObexHelper.getMaxRxPacketSize(mTransport)) {
+                        mParent.sendResponse(ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE, null);
+                        throw new IOException("Packet received was too large");
+                    }
+
+                    /*
+                     * Determine if any headers were sent in the initial request
+                     */
+                    if (packet.mLength > 3 || (mSrmEnabled && packet.mLength == 3)) {
+                        if(handleObexPacket(packet) == false) {
                             return false;
                         }
-                        requestHeader.mAuthResp = null;
-                    }
-
-                    if (requestHeader.mAuthChall != null) {
-                        mParent.handleAuthChall(requestHeader);
-                        // send the auhtResp to the client
-                        replyHeader.mAuthResp = new byte[requestHeader.mAuthResp.length];
-                        System.arraycopy(requestHeader.mAuthResp, 0, replyHeader.mAuthResp, 0,
-                                replyHeader.mAuthResp.length);
-                        requestHeader.mAuthResp = null;
-                        requestHeader.mAuthChall = null;
-                    }
-
-                    if (body != null) {
-                        mPrivateInput.writeBytes(body, 1);
                     }
                 }
+
             }
             return true;
         } else {
@@ -504,6 +579,53 @@
     }
 
     /**
+     * This method will look for an abort from the peer during a SRM transfer.
+     * The function will not block if no data has been received from the remote device.
+     * If data have been received, the function will block while reading the incoming
+     * OBEX package.
+     * An Abort request will be handled, and cause an IOException("Abort Received").
+     * Other messages will be discarded silently as per GOEP specification.
+     * @throws IOException if an abort request have been received.
+     * TODO: I think this is an error in the specification. If we discard other messages,
+     *       the peer device will most likely stall, as it will not receive the expected
+     *       response for the message...
+     *       I'm not sure how to understand "Receipt of invalid or unexpected SRM or SRMP
+     *       header values shall be ignored by the receiving device."
+     *       If any signal is received during an active SRM transfer it is unexpected regardless
+     *       whether or not it contains SRM/SRMP headers...
+     */
+    private void checkSrmRemoteAbort() throws IOException {
+        if(mInput.available() > 0) {
+            ObexPacket packet = ObexPacket.read(mInput);
+            /*
+             * Determine if an ABORT was sent as the reply
+             */
+            if (packet.mHeaderId == ObexHelper.OBEX_OPCODE_ABORT) {
+                handleRemoteAbort();
+            } else {
+                // TODO: should we throw an exception here anyway? - don't see how to
+                //       ignore SRM/SRMP headers without ignoring the complete signal
+                //       (in this particular case).
+                Log.w(TAG, "Received unexpected request from client - discarding...\n"
+                        + "   headerId: " + packet.mHeaderId + " length: " + packet.mLength);
+            }
+        }
+    }
+
+    private void handleRemoteAbort() throws IOException {
+        /* TODO: To increase the speed of the abort operation in SRM, we need
+         *       to be able to flush the L2CAP queue for the PSM in use.
+         *       This could be implemented by introducing a control
+         *       message to be send over the socket, that in the abort case
+         *       could carry a flush command. */
+        mParent.sendResponse(ResponseCodes.OBEX_HTTP_OK, null);
+        mClosed = true;
+        isAborted = true;
+        mExceptionString = "Abort Received";
+        throw new IOException("Abort Received");
+    }
+
+    /**
      * Sends an ABORT message to the server. By calling this method, the
      * corresponding input and output streams will be closed along with this
      * object.
diff --git a/obex/javax/obex/ServerRequestHandler.java b/obex/javax/obex/ServerRequestHandler.java
index 0882572..09cbc2c 100644
--- a/obex/javax/obex/ServerRequestHandler.java
+++ b/obex/javax/obex/ServerRequestHandler.java
@@ -275,4 +275,13 @@
      */
     public void onClose() {
     }
+
+    /**
+     * Override to add Single Response Mode support - e.g. if the supplied
+     * transport is l2cap.
+     * @return True if SRM is supported, else False
+     */
+    public boolean isSrmSupported() {
+        return false;
+    }
 }
diff --git a/obex/javax/obex/ServerSession.java b/obex/javax/obex/ServerSession.java
index f1b9a0d..acee5dd 100644
--- a/obex/javax/obex/ServerSession.java
+++ b/obex/javax/obex/ServerSession.java
@@ -1,4 +1,6 @@
 /*
+ * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (c) 2015 Samsung LSI
  * Copyright (c) 2008-2009, Motorola, Inc.
  *
  * All rights reserved.
@@ -45,6 +47,7 @@
 public final class ServerSession extends ObexSession implements Runnable {
 
     private static final String TAG = "Obex ServerSession";
+    private static final boolean V = ObexHelper.VDBG;
 
     private ObexTransport mTransport;
 
@@ -91,7 +94,9 @@
 
             boolean done = false;
             while (!done && !mClosed) {
+                if(V) Log.v(TAG, "Waiting for incoming request...");
                 int requestType = mInput.read();
+                if(V) Log.v(TAG, "Read request: " + requestType);
                 switch (requestType) {
                     case ObexHelper.OBEX_OPCODE_CONNECT:
                         handleConnectRequest();
@@ -140,9 +145,9 @@
             }
 
         } catch (NullPointerException e) {
-            Log.d(TAG, e.toString());
+            Log.d(TAG, "Exception occured - ignoring", e);
         } catch (Exception e) {
-            Log.d(TAG, e.toString());
+            Log.d(TAG, "Exception occured - ignoring", e);
         }
         close();
     }
@@ -163,7 +168,7 @@
 
         int length = mInput.read();
         length = (length << 8) + mInput.read();
-        if (length > ObexHelper.MAX_PACKET_SIZE_INT) {
+        if (length > ObexHelper.getMaxRxPacketSize(mTransport)) {
             code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
         } else {
             for (int i = 3; i < length; i++) {
@@ -215,6 +220,7 @@
              *internal error should not be sent because server has already replied with
              *OK response in "sendReply")
              */
+            if(V) Log.d(TAG,"Exception occured - sending OBEX_HTTP_INTERNAL_ERROR reply",e);
             if (!op.isAborted) {
                 sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
             }
@@ -243,6 +249,7 @@
                 op.sendReply(response);
             }
         } catch (Exception e) {
+            if(V) Log.d(TAG,"Exception occured - sending OBEX_HTTP_INTERNAL_ERROR reply",e);
             sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
         }
     }
@@ -275,7 +282,7 @@
             data[2] = (byte)totalLength;
         }
         op.write(data);
-        op.flush();
+        op.flush(); // TODO: Do we need to flush?
     }
 
     /**
@@ -304,7 +311,7 @@
         flags = mInput.read();
         constants = mInput.read();
 
-        if (length > ObexHelper.MAX_PACKET_SIZE_INT) {
+        if (length > ObexHelper.getMaxRxPacketSize(mTransport)) {
             code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
             totalLength = 3;
         } else {
@@ -358,6 +365,7 @@
                 try {
                     code = mListener.onSetPath(request, reply, backup, create);
                 } catch (Exception e) {
+                    if(V) Log.d(TAG,"Exception occured - sending OBEX_HTTP_INTERNAL_ERROR reply",e);
                     sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
                     return;
                 }
@@ -425,7 +433,7 @@
         length = mInput.read();
         length = (length << 8) + mInput.read();
 
-        if (length > ObexHelper.MAX_PACKET_SIZE_INT) {
+        if (length > ObexHelper.getMaxRxPacketSize(mTransport)) {
             code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
             totalLength = 3;
         } else {
@@ -466,6 +474,7 @@
                 try {
                     mListener.onDisconnect(request, reply);
                 } catch (Exception e) {
+                    if(V) Log.d(TAG,"Exception occured - sending OBEX_HTTP_INTERNAL_ERROR reply",e);
                     sendResponse(ResponseCodes.OBEX_HTTP_INTERNAL_ERROR, null);
                     return;
                 }
@@ -531,23 +540,38 @@
         HeaderSet reply = new HeaderSet();
         int bytesReceived;
 
+        if(V) Log.v(TAG,"handleConnectRequest()");
+
         /*
          * Read in the length of the OBEX packet, OBEX version, flags, and max
          * packet length
          */
         packetLength = mInput.read();
         packetLength = (packetLength << 8) + mInput.read();
+        if(V) Log.v(TAG,"handleConnectRequest() - packetLength: " + packetLength);
+
         version = mInput.read();
         flags = mInput.read();
         mMaxPacketLength = mInput.read();
         mMaxPacketLength = (mMaxPacketLength << 8) + mInput.read();
 
+        if(V) Log.v(TAG,"handleConnectRequest() - version: " + version
+                + " MaxLength: " + mMaxPacketLength + " flags: " + flags);
+
         // should we check it?
         if (mMaxPacketLength > ObexHelper.MAX_PACKET_SIZE_INT) {
             mMaxPacketLength = ObexHelper.MAX_PACKET_SIZE_INT;
         }
 
-        if (packetLength > ObexHelper.MAX_PACKET_SIZE_INT) {
+        if(mMaxPacketLength > ObexHelper.getMaxTxPacketSize(mTransport)) {
+            Log.w(TAG, "Requested MaxObexPacketSize " + mMaxPacketLength
+                    + " is larger than the max size supported by the transport: "
+                    + ObexHelper.getMaxTxPacketSize(mTransport)
+                    + " Reducing to this size.");
+            mMaxPacketLength = ObexHelper.getMaxTxPacketSize(mTransport);
+        }
+
+        if (packetLength > ObexHelper.getMaxRxPacketSize(mTransport)) {
             code = ResponseCodes.OBEX_HTTP_REQ_TOO_LARGE;
             totalLength = 7;
         } else {
@@ -614,7 +638,7 @@
                         code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
                     }
                 } catch (Exception e) {
-                    e.printStackTrace();
+                    if(V) Log.d(TAG,"Exception occured - sending OBEX_HTTP_INTERNAL_ERROR reply",e);
                     totalLength = 7;
                     head = null;
                     code = ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
@@ -633,13 +657,14 @@
          * Packet Length (Defined in MAX_PACKET_SIZE) Byte 7 to n: headers
          */
         byte[] sendData = new byte[totalLength];
+        int maxRxLength = ObexHelper.getMaxRxPacketSize(mTransport);
         sendData[0] = (byte)code;
         sendData[1] = length[2];
         sendData[2] = length[3];
         sendData[3] = (byte)0x10;
         sendData[4] = (byte)0x00;
-        sendData[5] = (byte)(ObexHelper.MAX_PACKET_SIZE_INT >> 8);
-        sendData[6] = (byte)(ObexHelper.MAX_PACKET_SIZE_INT & 0xFF);
+        sendData[5] = (byte)(maxRxLength >> 8);
+        sendData[6] = (byte)(maxRxLength & 0xFF);
 
         if (head != null) {
             System.arraycopy(head, 0, sendData, 7, head.length);
@@ -659,11 +684,16 @@
             mListener.onClose();
         }
         try {
-            mInput.close();
-            mOutput.close();
-            mTransport.close();
+            /* Set state to closed before interrupting the thread by closing the streams */
             mClosed = true;
+            if(mInput != null)
+                mInput.close();
+            if(mOutput != null)
+                mOutput.close();
+            if(mTransport != null)
+                mTransport.close();
         } catch (Exception e) {
+            if(V) Log.d(TAG,"Exception occured during close() - ignore",e);
         }
         mTransport = null;
         mInput = null;
@@ -702,4 +732,7 @@
         return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
     }
 
+    public ObexTransport getTransport() {
+        return mTransport;
+    }
 }
diff --git a/packages/CaptivePortalLogin/AndroidManifest.xml b/packages/CaptivePortalLogin/AndroidManifest.xml
index 2ec15be..aea8585 100644
--- a/packages/CaptivePortalLogin/AndroidManifest.xml
+++ b/packages/CaptivePortalLogin/AndroidManifest.xml
@@ -21,6 +21,7 @@
 
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
 
     <application android:label="@string/app_name" >
         <activity
@@ -28,9 +29,8 @@
             android:label="@string/action_bar_label"
             android:theme="@style/AppTheme" >
             <intent-filter>
-                <action android:name="android.intent.action.ACTION_SEND"/>
+                <action android:name="android.net.conn.CAPTIVE_PORTAL"/>
                 <category android:name="android.intent.category.DEFAULT"/>
-                <data android:mimeType="text/plain"/>
             </intent-filter>
         </activity>
     </application>
diff --git a/packages/CaptivePortalLogin/res/values-en-rGB/strings.xml b/packages/CaptivePortalLogin/res/values-en-rGB/strings.xml
index 687a14e..bfda753 100644
--- a/packages/CaptivePortalLogin/res/values-en-rGB/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-en-rGB/strings.xml
@@ -4,5 +4,5 @@
     <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
     <string name="action_use_network" msgid="6076184727448466030">"Use this network as is"</string>
     <string name="action_do_not_use_network" msgid="4577366536956516683">"Do not use this network"</string>
-    <string name="action_bar_label" msgid="2573986763322074279">"Sign-in to network"</string>
+    <string name="action_bar_label" msgid="2573986763322074279">"Sign in to network"</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values-en-rIN/strings.xml b/packages/CaptivePortalLogin/res/values-en-rIN/strings.xml
index 687a14e..bfda753 100644
--- a/packages/CaptivePortalLogin/res/values-en-rIN/strings.xml
+++ b/packages/CaptivePortalLogin/res/values-en-rIN/strings.xml
@@ -4,5 +4,5 @@
     <string name="app_name" msgid="5934709770924185752">"CaptivePortalLogin"</string>
     <string name="action_use_network" msgid="6076184727448466030">"Use this network as is"</string>
     <string name="action_do_not_use_network" msgid="4577366536956516683">"Do not use this network"</string>
-    <string name="action_bar_label" msgid="2573986763322074279">"Sign-in to network"</string>
+    <string name="action_bar_label" msgid="2573986763322074279">"Sign in to network"</string>
 </resources>
diff --git a/packages/CaptivePortalLogin/res/values/strings.xml b/packages/CaptivePortalLogin/res/values/strings.xml
index 1b0f0a4..8348be9 100644
--- a/packages/CaptivePortalLogin/res/values/strings.xml
+++ b/packages/CaptivePortalLogin/res/values/strings.xml
@@ -4,6 +4,6 @@
     <string name="app_name">CaptivePortalLogin</string>
     <string name="action_use_network">Use this network as is</string>
     <string name="action_do_not_use_network">Do not use this network</string>
-    <string name="action_bar_label">Sign-in to network</string>
+    <string name="action_bar_label">Sign in to network</string>
 
 </resources>
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index d898555..81ff2ab 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -58,24 +58,13 @@
     private static final String DEFAULT_SERVER = "connectivitycheck.android.com";
     private static final int SOCKET_TIMEOUT_MS = 10000;
 
-    // Keep this in sync with NetworkMonitor.
-    // Intent broadcast to ConnectivityService indicating sign-in is complete.
-    // Extras:
-    //     EXTRA_TEXT       = netId
-    //     LOGGED_IN_RESULT = one of the CAPTIVE_PORTAL_APP_RETURN_* values below.
-    //     RESPONSE_TOKEN   = data fragment from launching Intent
-    private static final String ACTION_CAPTIVE_PORTAL_LOGGED_IN =
-            "android.net.netmon.captive_portal_logged_in";
-    private static final String LOGGED_IN_RESULT = "result";
-    private static final int CAPTIVE_PORTAL_APP_RETURN_APPEASED = 0;
-    private static final int CAPTIVE_PORTAL_APP_RETURN_UNWANTED = 1;
-    private static final int CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS = 2;
-    private static final String RESPONSE_TOKEN = "response_token";
+    private enum Result { DISMISSED, UNWANTED, WANTED_AS_IS };
 
     private URL mURL;
-    private int mNetId;
+    private Network mNetwork;
     private String mResponseToken;
     private NetworkCallback mNetworkCallback;
+    private ConnectivityManager mCm;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -83,23 +72,19 @@
 
         String server = Settings.Global.getString(getContentResolver(), "captive_portal_server");
         if (server == null) server = DEFAULT_SERVER;
+        mCm = ConnectivityManager.from(this);
         try {
             mURL = new URL("http", server, "/generate_204");
-            final Uri dataUri = getIntent().getData();
-            if (!dataUri.getScheme().equals("netid")) {
-                throw new MalformedURLException();
-            }
-            mNetId = Integer.parseInt(dataUri.getSchemeSpecificPart());
-            mResponseToken = dataUri.getFragment();
-        } catch (MalformedURLException|NumberFormatException e) {
+        } catch (MalformedURLException e) {
             // System misconfigured, bail out in a way that at least provides network access.
-            done(CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS);
+            Log.e(TAG, "Invalid captive portal URL, server=" + server);
+            done(Result.WANTED_AS_IS);
         }
+        mNetwork = getIntent().getParcelableExtra(ConnectivityManager.EXTRA_NETWORK);
+        mResponseToken = getIntent().getStringExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_TOKEN);
 
-        final ConnectivityManager cm = ConnectivityManager.from(this);
-        final Network network = new Network(mNetId);
         // Also initializes proxy system properties.
-        cm.setProcessDefaultNetwork(network);
+        mCm.bindProcessToNetwork(mNetwork);
 
         // Proxy system properties must be initialized before setContentView is called because
         // setContentView initializes the WebView logic which in turn reads the system properties.
@@ -108,7 +93,7 @@
         getActionBar().setDisplayShowHomeEnabled(false);
 
         // Exit app if Network disappears.
-        final NetworkCapabilities networkCapabilities = cm.getNetworkCapabilities(network);
+        final NetworkCapabilities networkCapabilities = mCm.getNetworkCapabilities(mNetwork);
         if (networkCapabilities == null) {
             finish();
             return;
@@ -116,14 +101,14 @@
         mNetworkCallback = new NetworkCallback() {
             @Override
             public void onLost(Network lostNetwork) {
-                if (network.equals(lostNetwork)) done(CAPTIVE_PORTAL_APP_RETURN_UNWANTED);
+                if (mNetwork.equals(lostNetwork)) done(Result.UNWANTED);
             }
         };
         final NetworkRequest.Builder builder = new NetworkRequest.Builder();
         for (int transportType : networkCapabilities.getTransportTypes()) {
             builder.addTransportType(transportType);
         }
-        cm.registerNetworkCallback(builder.build(), mNetworkCallback);
+        mCm.registerNetworkCallback(builder.build(), mNetworkCallback);
 
         final WebView myWebView = (WebView) findViewById(R.id.webview);
         myWebView.clearCache(true);
@@ -160,15 +145,21 @@
         }
     }
 
-    private void done(int result) {
+    private void done(Result result) {
         if (mNetworkCallback != null) {
-            ConnectivityManager.from(this).unregisterNetworkCallback(mNetworkCallback);
+            mCm.unregisterNetworkCallback(mNetworkCallback);
         }
-        Intent intent = new Intent(ACTION_CAPTIVE_PORTAL_LOGGED_IN);
-        intent.putExtra(Intent.EXTRA_TEXT, String.valueOf(mNetId));
-        intent.putExtra(LOGGED_IN_RESULT, String.valueOf(result));
-        intent.putExtra(RESPONSE_TOKEN, mResponseToken);
-        sendBroadcast(intent);
+        switch (result) {
+            case DISMISSED:
+                mCm.reportCaptivePortalDismissed(mNetwork, mResponseToken);
+                break;
+            case UNWANTED:
+                mCm.ignoreNetworkWithCaptivePortal(mNetwork, mResponseToken);
+                break;
+            case WANTED_AS_IS:
+                mCm.useNetworkWithCaptivePortal(mNetwork, mResponseToken);
+                break;
+        }
         finish();
     }
 
@@ -192,11 +183,11 @@
     public boolean onOptionsItemSelected(MenuItem item) {
         int id = item.getItemId();
         if (id == R.id.action_use_network) {
-            done(CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS);
+            done(Result.WANTED_AS_IS);
             return true;
         }
         if (id == R.id.action_do_not_use_network) {
-            done(CAPTIVE_PORTAL_APP_RETURN_UNWANTED);
+            done(Result.UNWANTED);
             return true;
         }
         return super.onOptionsItemSelected(item);
@@ -225,7 +216,7 @@
                     if (urlConnection != null) urlConnection.disconnect();
                 }
                 if (httpResponseCode == 204) {
-                    done(CAPTIVE_PORTAL_APP_RETURN_APPEASED);
+                    done(Result.DISMISSED);
                 }
             }
         }).start();
diff --git a/services/Android.mk b/services/Android.mk
index 3c94f43..e4b0cbb 100644
--- a/services/Android.mk
+++ b/services/Android.mk
@@ -24,6 +24,7 @@
     appwidget \
     backup \
     devicepolicy \
+    net \
     print \
     restrictions \
     usage \
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 32a6a2f..46a4599 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -58,6 +58,7 @@
 import java.util.List;
 import java.util.Vector;
 
+import java.util.*;
 class BluetoothManagerService extends IBluetoothManager.Stub {
     private static final String TAG = "BluetoothManagerService";
     private static final boolean DBG = true;
@@ -114,6 +115,7 @@
     private static final int SERVICE_IBLUETOOTHGATT = 2;
 
     private final Context mContext;
+    private static int mBleAppCount = 0;
 
     // Locks are not provided for mName and mAddress.
     // They are accessed in handler or broadcast receiver, same thread context.
@@ -186,11 +188,40 @@
                             persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
                         }
                     }
+
+                    int st = BluetoothAdapter.STATE_OFF;
+                    if (mBluetooth != null) {
+                        try {
+                            st = mBluetooth.getState();
+                        } catch (RemoteException e) {
+                            Log.e(TAG,"Unable to call getState", e);
+                        }
+                    }
+                    Log.d(TAG, "state" + st);
+
                     if (isAirplaneModeOn()) {
-                        // disable without persisting the setting
-                        sendDisableMsg();
+                        // Clear registered LE apps to force shut-off
+                        synchronized (this) {
+                            mBleAppCount = 0;
+                        }
+                        if (st == BluetoothAdapter.STATE_BLE_ON) {
+                            //if state is BLE_ON make sure you trigger disableBLE part
+                            try {
+                                if (mBluetooth != null) {
+                                    mBluetooth.onBrEdrDown();
+                                    mEnableExternal = false;
+                                }
+                            } catch(RemoteException e) {
+                                Log.e(TAG,"Unable to call onBrEdrDown", e);
+                            }
+                        } else if (st == BluetoothAdapter.STATE_ON){
+                            // disable without persisting the setting
+                            Log.d(TAG, "Calling disable");
+                            sendDisableMsg();
+                        }
                     } else if (mEnableExternal) {
                         // enable without persisting the setting
+                        Log.d(TAG, "Calling enable");
                         sendEnableMsg(mQuietEnableExternal);
                     }
                 }
@@ -205,12 +236,6 @@
                         sendEnableMsg(mQuietEnableExternal);
                     }
                 }
-
-                if (!isNameAndAddressSet()) {
-                    //Sync the Bluetooth name and address from the Bluetooth Adapter
-                    if (DBG) Log.d(TAG,"Retrieving Bluetooth Adapter name and address...");
-                    getNameAndAddress();
-                }
             }
         }
     };
@@ -220,6 +245,7 @@
 
         mContext = context;
         mBluetooth = null;
+        mBluetoothGatt = null;
         mBinding = false;
         mUnbinding = false;
         mEnable = false;
@@ -398,6 +424,133 @@
         return false;
     }
 
+    class ClientDeathRecipient implements IBinder.DeathRecipient {
+        public void binderDied() {
+            if (DBG) Log.d(TAG, "Binder is dead -  unregister Ble App");
+            if (mBleAppCount > 0) --mBleAppCount;
+
+            if (mBleAppCount == 0) {
+                if (DBG) Log.d(TAG, "Disabling LE only mode after application crash");
+                try {
+                    if (mBluetooth != null) {
+                        mBluetooth.onBrEdrDown();
+                    }
+                } catch(RemoteException e) {
+                     Log.e(TAG,"Unable to call onBrEdrDown", e);
+                }
+            }
+        }
+    }
+
+    /** Internal death rec list */
+    Map<IBinder, ClientDeathRecipient> mBleApps = new HashMap<IBinder, ClientDeathRecipient>();
+
+    public int updateBleAppCount(IBinder token, boolean enable) {
+        if (enable) {
+            ClientDeathRecipient r = mBleApps.get(token);
+            if (r == null) {
+                ClientDeathRecipient deathRec = new ClientDeathRecipient();
+                try {
+                    token.linkToDeath(deathRec, 0);
+                } catch (RemoteException ex) {
+                    throw new IllegalArgumentException("Wake lock is already dead.");
+                }
+                mBleApps.put(token, deathRec);
+                synchronized (this) {
+                    ++mBleAppCount;
+                }
+                if (DBG) Log.d(TAG, "Registered for death Notification");
+            }
+
+        } else  {
+            ClientDeathRecipient r = mBleApps.get(token);
+            if (r != null) {
+                try {
+                    token.linkToDeath(r, 0);
+                } catch (RemoteException ex) {
+                    throw new IllegalArgumentException("Wake lock is already dead.");
+                }
+                mBleApps.remove(token);
+                synchronized (this) {
+                    if (mBleAppCount > 0) --mBleAppCount;
+                }
+                if (DBG) Log.d(TAG, "Unregistered for death Notification");
+            }
+        }
+        if (DBG) Log.d(TAG, "Updated BleAppCount" + mBleAppCount);
+        if (mBleAppCount == 0 && mEnable) {
+            try {
+                if (mBluetooth != null && (mBluetooth.getState() != BluetoothAdapter.STATE_ON)) {
+                    if (DBG) Log.d(TAG, "Reseting the mEnable flag for clean disable");
+                    mEnable = false;
+                }
+            } catch (RemoteException e) {
+                Log.e(TAG, "getState()", e);
+            }
+        }
+        return mBleAppCount;
+    }
+
+    /** @hide*/
+    public boolean isBleAppPresent() {
+        if (DBG) Log.d(TAG, "isBleAppPresent() count: " + mBleAppCount);
+        return (mBleAppCount > 0);
+    }
+
+    /**
+     * Action taken when GattService is turned off
+     */
+    private void onBluetoothGattServiceUp() {
+        if (DBG) Log.d(TAG,"BluetoothGatt Service is Up");
+        try{
+            if (isBleAppPresent() == false && mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) {
+                mBluetooth.onLeServiceUp();
+
+                // waive WRITE_SECURE_SETTINGS permission check
+                long callingIdentity = Binder.clearCallingIdentity();
+                persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
+                Binder.restoreCallingIdentity(callingIdentity);
+            }
+        } catch(RemoteException e) {
+                Log.e(TAG,"Unable to call onServiceUp", e);
+        }
+    }
+
+    /**
+     * Inform BluetoothAdapter instances that BREDR part is down
+     * and turn off all service and stack if no LE app needs it
+     */
+    private void sendBrEdrDownCallback() {
+        if (DBG) Log.d(TAG,"Calling sendBrEdrDownCallback callbacks");
+        int n = mCallbacks.beginBroadcast();
+
+        if (isBleAppPresent() == false) {
+            try {
+                mBluetooth.onBrEdrDown();
+            } catch(RemoteException e) {
+                Log.e(TAG,"Unable to call onBrEdrDown", e);
+            }
+        }
+        else{//need to stay at BLE ON. disconnect all Gatt connections
+            try{
+                mBluetoothGatt.unregAll();//disconnectAll();
+            } catch(RemoteException e) {
+                Log.e(TAG,"Unable to disconn all", e);
+            }
+        }
+
+        Log.d(TAG,"Broadcasting onBrEdrDown() to " + n + " receivers.");
+        for (int i=0; i <n; i++) {
+            try {
+                mCallbacks.getBroadcastItem(i).onBrEdrDown();
+            }  catch (RemoteException e) {
+                Log.e(TAG, "Unable to call sendBrEdrDownCallback() on callback #" + i, e);
+            }
+        }
+        mCallbacks.finishBroadcast();
+    }
+
+    /** @hide*/
     public void getNameAndAddress() {
         if (DBG) {
             Log.d(TAG,"getNameAndAddress(): mBluetooth = " + mBluetooth +
@@ -447,11 +600,9 @@
             mQuietEnableExternal = false;
             mEnableExternal = true;
             // waive WRITE_SECURE_SETTINGS permission check
-            long callingIdentity = Binder.clearCallingIdentity();
-            persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
-            Binder.restoreCallingIdentity(callingIdentity);
             sendEnableMsg(false);
         }
+        if (DBG) Log.d(TAG, "enable returning");
         return true;
     }
 
@@ -510,6 +661,7 @@
             } else {
                 mUnbinding=false;
             }
+            mBluetoothGatt = null;
         }
     }
 
@@ -1036,6 +1188,7 @@
                     synchronized(mConnection) {
                         if (msg.arg1 == SERVICE_IBLUETOOTHGATT) {
                             mBluetoothGatt = IBluetoothGatt.Stub.asInterface(service);
+                            onBluetoothGattServiceUp();
                             break;
                         } // else must be SERVICE_IBLUETOOTH
 
@@ -1113,12 +1266,18 @@
                     bluetoothStateChangeHandler(prevState, newState);
                     // handle error state transition case from TURNING_ON to OFF
                     // unbind and rebind bluetooth service and enable bluetooth
-                    if ((prevState == BluetoothAdapter.STATE_TURNING_ON) &&
+                    if ((prevState == BluetoothAdapter.STATE_BLE_TURNING_ON) &&
                         (newState == BluetoothAdapter.STATE_OFF) &&
                         (mBluetooth != null) && mEnable) {
                         recoverBluetoothServiceFromError();
                     }
-                    if (newState == BluetoothAdapter.STATE_ON) {
+                    if ((prevState == BluetoothAdapter.STATE_TURNING_ON) &&
+                        (newState == BluetoothAdapter.STATE_BLE_ON) &&
+                        (mBluetooth != null) && mEnable) {
+                        recoverBluetoothServiceFromError();
+                    }
+                    if (newState == BluetoothAdapter.STATE_ON ||
+                        newState == BluetoothAdapter.STATE_BLE_ON) {
                         // bluetooth is working, reset the counter
                         if (mErrorRecoveryRetryCounter != 0) {
                             Log.w(TAG, "bluetooth is recovered from error");
@@ -1378,39 +1537,90 @@
         return valid;
     }
 
+    private void sendBleStateChanged(int prevState, int newState) {
+        if (DBG) Log.d(TAG,"BLE State Change Intent: " + prevState + " -> " + newState);
+        // Send broadcast message to everyone else
+        Intent intent = new Intent(BluetoothAdapter.ACTION_BLE_STATE_CHANGED);
+        intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
+        intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
+        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+        mContext.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_PERM);
+    }
+
     private void bluetoothStateChangeHandler(int prevState, int newState) {
+        boolean isStandardBroadcast = true;
         if (prevState != newState) {
             //Notify all proxy objects first of adapter state change
-            if (newState == BluetoothAdapter.STATE_ON || newState == BluetoothAdapter.STATE_OFF) {
-                boolean isUp = (newState==BluetoothAdapter.STATE_ON);
-                sendBluetoothStateCallback(isUp);
+            if (newState == BluetoothAdapter.STATE_BLE_ON
+                   || newState == BluetoothAdapter.STATE_OFF) {
+                boolean intermediate_off = (prevState == BluetoothAdapter.STATE_TURNING_OFF
+                   && newState == BluetoothAdapter.STATE_BLE_ON);
 
-                if (isUp) {
-                    // connect to GattService
-                    if (mContext.getPackageManager().hasSystemFeature(
-                                                     PackageManager.FEATURE_BLUETOOTH_LE)) {
-                        Intent i = new Intent(IBluetoothGatt.class.getName());
-                        doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
-                                UserHandle.CURRENT);
-                    }
-                } else {
-                    //If Bluetooth is off, send service down event to proxy objects, and unbind
-                    if (!isUp && canUnbindBluetoothService()) {
-                        unbindAllBluetoothProfileServices();
+                if (newState == BluetoothAdapter.STATE_OFF) {
+                    // If Bluetooth is off, send service down event to proxy objects, and unbind
+                    if (DBG) Log.d(TAG, "Bluetooth is complete turn off");
+                    if (canUnbindBluetoothService()) {
+                        if (DBG) Log.d(TAG, "Good to unbind!");
                         sendBluetoothServiceDownCallback();
                         unbindAndFinish();
+                        sendBleStateChanged(prevState, newState);
+                        // Don't broadcast as it has already been broadcast before
+                        isStandardBroadcast = false;
                     }
+
+                } else if (!intermediate_off) {
+                    // connect to GattService
+                    if (DBG) Log.d(TAG, "Bluetooth is in LE only mode");
+                    if (mBluetoothGatt != null) {
+                        if (DBG) Log.d(TAG, "Calling BluetoothGattServiceUp");
+                        onBluetoothGattServiceUp();
+                    } else {
+                        if (DBG) Log.d(TAG, "Binding Bluetooth GATT service");
+                        if (mContext.getPackageManager().hasSystemFeature(
+                                                        PackageManager.FEATURE_BLUETOOTH_LE)) {
+                            Intent i = new Intent(IBluetoothGatt.class.getName());
+                            doBind(i, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.CURRENT);
+                        }
+                    }
+                    sendBleStateChanged(prevState, newState);
+                    //Don't broadcase this as std intent
+                    isStandardBroadcast = false;
+
+                } else if (intermediate_off){
+                    if (DBG) Log.d(TAG, "Intermediate off, back to LE only mode");
+                    // For LE only mode, broadcast as is
+                    sendBleStateChanged(prevState, newState);
+                    sendBluetoothStateCallback(false); // BT is OFF for general users
+                    // Broadcast as STATE_OFF
+                    newState = BluetoothAdapter.STATE_OFF;
+                    sendBrEdrDownCallback();
                 }
+            } else if (newState == BluetoothAdapter.STATE_ON) {
+                boolean isUp = (newState==BluetoothAdapter.STATE_ON);
+                sendBluetoothStateCallback(isUp);
+                sendBleStateChanged(prevState, newState);
+
+            } else if (newState == BluetoothAdapter.STATE_BLE_TURNING_ON
+                || newState == BluetoothAdapter.STATE_BLE_TURNING_OFF ) {
+                sendBleStateChanged(prevState, newState);
+                isStandardBroadcast = false;
+
+            } else if (newState == BluetoothAdapter.STATE_TURNING_ON
+                || newState == BluetoothAdapter.STATE_TURNING_OFF) {
+                sendBleStateChanged(prevState, newState);
             }
 
-            //Send broadcast message to everyone else
-            Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
-            intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
-            intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
-            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-            if (DBG) Log.d(TAG,"Bluetooth State Change Intent: " + prevState + " -> " + newState);
-            mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
-                    BLUETOOTH_PERM);
+            if (isStandardBroadcast) {
+                if (prevState == BluetoothAdapter.STATE_BLE_ON) {
+                    // Show prevState of BLE_ON as OFF to standard users
+                    prevState = BluetoothAdapter.STATE_OFF;
+                }
+                Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
+                intent.putExtra(BluetoothAdapter.EXTRA_PREVIOUS_STATE, prevState);
+                intent.putExtra(BluetoothAdapter.EXTRA_STATE, newState);
+                intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+                mContext.sendBroadcastAsUser(intent, UserHandle.ALL, BLUETOOTH_PERM);
+            }
         }
     }
 
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index b72b29d..ffda5a7 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -20,6 +20,7 @@
 import static android.Manifest.permission.RECEIVE_DATA_ACTIVITY_CHANGE;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
 import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE;
+import static android.net.ConnectivityManager.NETID_UNSET;
 import static android.net.ConnectivityManager.TYPE_NONE;
 import static android.net.ConnectivityManager.TYPE_VPN;
 import static android.net.ConnectivityManager.getNetworkTypeName;
@@ -48,7 +49,6 @@
 import android.net.INetworkStatsService;
 import android.net.LinkProperties;
 import android.net.LinkProperties.CompareResult;
-import android.net.MobileDataStateTracker;
 import android.net.Network;
 import android.net.NetworkAgent;
 import android.net.NetworkCapabilities;
@@ -59,12 +59,10 @@
 import android.net.NetworkQuotaInfo;
 import android.net.NetworkRequest;
 import android.net.NetworkState;
-import android.net.NetworkStateTracker;
 import android.net.NetworkUtils;
 import android.net.Proxy;
 import android.net.ProxyInfo;
 import android.net.RouteInfo;
-import android.net.SamplingDataTracker;
 import android.net.UidRange;
 import android.net.Uri;
 import android.os.Binder;
@@ -92,6 +90,7 @@
 import android.text.TextUtils;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
 import android.util.Xml;
 
@@ -153,9 +152,6 @@
     private static final boolean DBG = true;
     private static final boolean VDBG = false;
 
-    // network sampling debugging
-    private static final boolean SAMPLE_DBG = false;
-
     private static final boolean LOGD_RULES = false;
 
     // TODO: create better separation between radio types and network types
@@ -166,33 +162,10 @@
     private static final String NETWORK_RESTORE_DELAY_PROP_NAME =
             "android.telephony.apn-restore";
 
-    // Default value if FAIL_FAST_TIME_MS is not set
-    private static final int DEFAULT_FAIL_FAST_TIME_MS = 1 * 60 * 1000;
-    // system property that can override DEFAULT_FAIL_FAST_TIME_MS
-    private static final String FAIL_FAST_TIME_MS =
-            "persist.radio.fail_fast_time_ms";
-
-    private static final String ACTION_PKT_CNT_SAMPLE_INTERVAL_ELAPSED =
-            "android.net.ConnectivityService.action.PKT_CNT_SAMPLE_INTERVAL_ELAPSED";
-
-    private static final int SAMPLE_INTERVAL_ELAPSED_REQUEST_CODE = 0;
-
     // How long to delay to removal of a pending intent based request.
     // See Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS
     private final int mReleasePendingIntentDelayMs;
 
-    private PendingIntent mSampleIntervalElapsedIntent;
-
-    // Set network sampling interval at 12 minutes, this way, even if the timers get
-    // aggregated, it will fire at around 15 minutes, which should allow us to
-    // aggregate this timer with other timers (specially the socket keep alive timers)
-    private static final int DEFAULT_SAMPLING_INTERVAL_IN_SECONDS = (SAMPLE_DBG ? 30 : 12 * 60);
-
-    // start network sampling a minute after booting ...
-    private static final int DEFAULT_START_SAMPLING_INTERVAL_IN_SECONDS = (SAMPLE_DBG ? 30 : 60);
-
-    AlarmManager mAlarmManager;
-
     private Tethering mTethering;
 
     private final PermissionMonitor mPermissionMonitor;
@@ -212,13 +185,6 @@
     /** Set of ifaces that are costly. */
     private HashSet<String> mMeteredIfaces = Sets.newHashSet();
 
-    /**
-     * Sometimes we want to refer to the individual network state
-     * trackers separately, and sometimes we just want to treat them
-     * abstractly.
-     */
-    private NetworkStateTracker mNetTrackers[];
-
     private Context mContext;
     private int mNetworkPreference;
     // 0 is full bad, 100 is full good
@@ -278,28 +244,11 @@
     private static final int EVENT_APPLY_GLOBAL_HTTP_PROXY = 9;
 
     /**
-     * used internally to set external dependency met/unmet
-     * arg1 = ENABLED (met) or DISABLED (unmet)
-     * arg2 = NetworkType
-     */
-    private static final int EVENT_SET_DEPENDENCY_MET = 10;
-
-    /**
      * used internally to send a sticky broadcast delayed.
      */
     private static final int EVENT_SEND_STICKY_BROADCAST_INTENT = 11;
 
     /**
-     * Used internally to disable fail fast of mobile data
-     */
-    private static final int EVENT_ENABLE_FAIL_FAST_MOBILE_DATA = 14;
-
-    /**
-     * used internally to indicate that data sampling interval is up
-     */
-    private static final int EVENT_SAMPLE_INTERVAL_ELAPSED = 15;
-
-    /**
      * PAC manager has received new port.
      */
     private static final int EVENT_PROXY_HAS_CHANGED = 16;
@@ -419,8 +368,6 @@
 
     private DataConnectionStats mDataConnectionStats;
 
-    private AtomicInteger mEnableFailFastMobileDataTag = new AtomicInteger(0);
-
     TelephonyManager mTelephonyManager;
 
     // sequence number for Networks; keep in sync with system/netd/NetworkController.cpp
@@ -650,9 +597,6 @@
                 com.android.internal.R.integer.config_networkTransitionTimeout);
         mPendingIntentWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
 
-        mNetTrackers = new NetworkStateTracker[
-                ConnectivityManager.MAX_NETWORK_TYPE+1];
-
         mNetConfigs = new NetworkConfig[ConnectivityManager.MAX_NETWORK_TYPE+1];
 
         // TODO: What is the "correct" way to do determine if this is a wifi only device?
@@ -740,23 +684,6 @@
         mDataConnectionStats = new DataConnectionStats(mContext);
         mDataConnectionStats.startMonitoring();
 
-        mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
-
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(ACTION_PKT_CNT_SAMPLE_INTERVAL_ELAPSED);
-        mContext.registerReceiver(
-                new BroadcastReceiver() {
-                    @Override
-                    public void onReceive(Context context, Intent intent) {
-                        String action = intent.getAction();
-                        if (action.equals(ACTION_PKT_CNT_SAMPLE_INTERVAL_ELAPSED)) {
-                            mHandler.sendMessage(mHandler.obtainMessage
-                                    (EVENT_SAMPLE_INTERVAL_ELAPSED));
-                        }
-                    }
-                },
-                new IntentFilter(filter));
-
         mPacManager = new PacManager(mContext, mHandler, EVENT_PROXY_HAS_CHANGED);
 
         mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
@@ -766,31 +693,21 @@
         return mNextNetworkRequestId++;
     }
 
-    private void assignNextNetId(NetworkAgentInfo nai) {
+    private int reserveNetId() {
         synchronized (mNetworkForNetId) {
             for (int i = MIN_NET_ID; i <= MAX_NET_ID; i++) {
                 int netId = mNextNetId;
                 if (++mNextNetId > MAX_NET_ID) mNextNetId = MIN_NET_ID;
                 // Make sure NetID unused.  http://b/16815182
-                if (mNetworkForNetId.get(netId) == null) {
-                    nai.network = new Network(netId);
-                    mNetworkForNetId.put(netId, nai);
-                    return;
+                if (!mNetIdInUse.get(netId)) {
+                    mNetIdInUse.put(netId, true);
+                    return netId;
                 }
             }
         }
         throw new IllegalStateException("No free netIds");
     }
 
-    private boolean teardown(NetworkStateTracker netTracker) {
-        if (netTracker.teardown()) {
-            netTracker.setTeardownRequested(true);
-            return true;
-        } else {
-            return false;
-        }
-    }
-
     private NetworkState getFilteredNetworkState(int networkType, int uid) {
         NetworkInfo info = null;
         LinkProperties lp = null;
@@ -805,7 +722,9 @@
                     info = new NetworkInfo(nai.networkInfo);
                     lp = new LinkProperties(nai.linkProperties);
                     nc = new NetworkCapabilities(nai.networkCapabilities);
-                    network = new Network(nai.network);
+                    // Network objects are outwardly immutable so there is no point to duplicating.
+                    // Duplicating also precludes sharing socket factories and connection pools.
+                    network = nai.network;
                     subscriberId = (nai.networkMisc != null) ? nai.networkMisc.subscriberId : null;
                 }
                 info.setType(networkType);
@@ -873,7 +792,9 @@
                 info = new NetworkInfo(nai.networkInfo);
                 lp = new LinkProperties(nai.linkProperties);
                 nc = new NetworkCapabilities(nai.networkCapabilities);
-                network = new Network(nai.network);
+                // Network objects are outwardly immutable so there is no point to duplicating.
+                // Duplicating also precludes sharing socket factories and connection pools.
+                network = nai.network;
                 subscriberId = (nai.networkMisc != null) ? nai.networkMisc.subscriberId : null;
             }
         }
@@ -939,6 +860,28 @@
         return getFilteredNetworkInfo(state.networkInfo, state.linkProperties, uid);
     }
 
+    @Override
+    public Network getActiveNetwork() {
+        enforceAccessPermission();
+        final int uid = Binder.getCallingUid();
+        final int user = UserHandle.getUserId(uid);
+        int vpnNetId = NETID_UNSET;
+        synchronized (mVpns) {
+            final Vpn vpn = mVpns.get(user);
+            if (vpn != null && vpn.appliesToUid(uid)) vpnNetId = vpn.getNetId();
+        }
+        NetworkAgentInfo nai;
+        if (vpnNetId != NETID_UNSET) {
+            synchronized (mNetworkForNetId) {
+                nai = mNetworkForNetId.get(vpnNetId);
+            }
+            if (nai != null) return nai.network;
+        }
+        nai = getDefaultNetwork();
+        if (nai != null && isNetworkWithLinkPropertiesBlocked(nai.linkProperties, uid)) nai = null;
+        return nai != null ? nai.network : null;
+    }
+
     /**
      * Find the first Provisioning network.
      *
@@ -1051,13 +994,13 @@
     @Override
     public Network[] getAllNetworks() {
         enforceAccessPermission();
-        final ArrayList<Network> result = new ArrayList();
         synchronized (mNetworkForNetId) {
+            final Network[] result = new Network[mNetworkForNetId.size()];
             for (int i = 0; i < mNetworkForNetId.size(); i++) {
-                result.add(new Network(mNetworkForNetId.valueAt(i).network));
+                result[i] = mNetworkForNetId.valueAt(i).network;
             }
+            return result;
         }
-        return result.toArray(new Network[result.size()]);
     }
 
     private NetworkCapabilities getNetworkCapabilitiesAndValidation(NetworkAgentInfo nai) {
@@ -1350,22 +1293,6 @@
         return true;
     }
 
-    public void setDataDependency(int networkType, boolean met) {
-        enforceConnectivityInternalPermission();
-
-        mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_DEPENDENCY_MET,
-                (met ? ENABLED : DISABLED), networkType));
-    }
-
-    private void handleSetDependencyMet(int networkType, boolean met) {
-        if (mNetTrackers[networkType] != null) {
-            if (DBG) {
-                log("handleSetDependencyMet(" + networkType + ", " + met + ")");
-            }
-            mNetTrackers[networkType].setDependencyMet(met);
-        }
-    }
-
     private INetworkPolicyListener mPolicyListener = new INetworkPolicyListener.Stub() {
         @Override
         public void onUidRulesChanged(int uid, int uidRules) {
@@ -1406,21 +1333,6 @@
             if (LOGD_RULES) {
                 log("onRestrictBackgroundChanged(restrictBackground=" + restrictBackground + ")");
             }
-
-            // kick off connectivity change broadcast for active network, since
-            // global background policy change is radical.
-            // TODO: Dead code; remove.
-            //
-            // final int networkType = mActiveDefaultNetwork;
-            // if (isNetworkTypeValid(networkType)) {
-            //     final NetworkStateTracker tracker = mNetTrackers[networkType];
-            //     if (tracker != null) {
-            //         final NetworkInfo info = tracker.getNetworkInfo();
-            //         if (info != null && info.isConnected()) {
-            //             sendConnectedBroadcast(info);
-            //         }
-            //     }
-            // }
         }
     };
 
@@ -1536,14 +1448,6 @@
     }
 
     void systemReady() {
-        // start network sampling ..
-        Intent intent = new Intent(ACTION_PKT_CNT_SAMPLE_INTERVAL_ELAPSED);
-        intent.setPackage(mContext.getPackageName());
-
-        mSampleIntervalElapsedIntent = PendingIntent.getBroadcast(mContext,
-                SAMPLE_INTERVAL_ELAPSED_REQUEST_CODE, intent, 0);
-        setAlarm(DEFAULT_START_SAMPLING_INTERVAL_IN_SECONDS * 1000, mSampleIntervalElapsedIntent);
-
         loadGlobalProxy();
 
         synchronized(this) {
@@ -2015,66 +1919,6 @@
                     }
                     break;
                 }
-                case NetworkStateTracker.EVENT_STATE_CHANGED: {
-                    info = (NetworkInfo) msg.obj;
-                    NetworkInfo.State state = info.getState();
-
-                    if (VDBG || (state == NetworkInfo.State.CONNECTED) ||
-                            (state == NetworkInfo.State.DISCONNECTED) ||
-                            (state == NetworkInfo.State.SUSPENDED)) {
-                        log("ConnectivityChange for " +
-                            info.getTypeName() + ": " +
-                            state + "/" + info.getDetailedState());
-                    }
-
-                    EventLogTags.writeConnectivityStateChanged(
-                            info.getType(), info.getSubtype(), info.getDetailedState().ordinal());
-
-                    if (info.isConnectedToProvisioningNetwork()) {
-                        /**
-                         * TODO: Create ConnectivityManager.TYPE_MOBILE_PROVISIONING
-                         * for now its an in between network, its a network that
-                         * is actually a default network but we don't want it to be
-                         * announced as such to keep background applications from
-                         * trying to use it. It turns out that some still try so we
-                         * take the additional step of clearing any default routes
-                         * to the link that may have incorrectly setup by the lower
-                         * levels.
-                         */
-                        LinkProperties lp = getLinkPropertiesForType(info.getType());
-                        if (DBG) {
-                            log("EVENT_STATE_CHANGED: connected to provisioning network, lp=" + lp);
-                        }
-
-                        // Clear any default routes setup by the radio so
-                        // any activity by applications trying to use this
-                        // connection will fail until the provisioning network
-                        // is enabled.
-                        /*
-                        for (RouteInfo r : lp.getRoutes()) {
-                            removeRoute(lp, r, TO_DEFAULT_TABLE,
-                                        mNetTrackers[info.getType()].getNetwork().netId);
-                        }
-                        */
-                    } else if (state == NetworkInfo.State.DISCONNECTED) {
-                    } else if (state == NetworkInfo.State.SUSPENDED) {
-                    } else if (state == NetworkInfo.State.CONNECTED) {
-                    //    handleConnect(info);
-                    }
-                    notifyLockdownVpn(null);
-                    break;
-                }
-                case NetworkStateTracker.EVENT_CONFIGURATION_CHANGED: {
-                    info = (NetworkInfo) msg.obj;
-                    // TODO: Temporary allowing network configuration
-                    //       change not resetting sockets.
-                    //       @see bug/4455071
-                    /*
-                    handleConnectivityChange(info.getType(), mCurrentLinkProperties[info.getType()],
-                            false);
-                    */
-                    break;
-                }
             }
         }
     }
@@ -2121,6 +1965,7 @@
                 if (nai != null) {
                     synchronized (mNetworkForNetId) {
                         mNetworkForNetId.remove(nai.network.netId);
+                        mNetIdInUse.delete(nai.network.netId);
                     }
                     // Just in case.
                     mLegacyTypeTracker.remove(nai);
@@ -2164,6 +2009,7 @@
             mLegacyTypeTracker.remove(nai);
             synchronized (mNetworkForNetId) {
                 mNetworkForNetId.remove(nai.network.netId);
+                mNetIdInUse.delete(nai.network.netId);
             }
             // Since we've lost the network, go through all the requests that
             // it was satisfying and see if any other factory can satisfy them.
@@ -2439,34 +2285,11 @@
                     handleDeprecatedGlobalHttpProxy();
                     break;
                 }
-                case EVENT_SET_DEPENDENCY_MET: {
-                    boolean met = (msg.arg1 == ENABLED);
-                    handleSetDependencyMet(msg.arg2, met);
-                    break;
-                }
                 case EVENT_SEND_STICKY_BROADCAST_INTENT: {
                     Intent intent = (Intent)msg.obj;
                     sendStickyBroadcast(intent);
                     break;
                 }
-                case EVENT_ENABLE_FAIL_FAST_MOBILE_DATA: {
-                    int tag = mEnableFailFastMobileDataTag.get();
-                    if (msg.arg1 == tag) {
-                        MobileDataStateTracker mobileDst =
-                            (MobileDataStateTracker) mNetTrackers[ConnectivityManager.TYPE_MOBILE];
-                        if (mobileDst != null) {
-                            mobileDst.setEnableFailFastMobileData(msg.arg2);
-                        }
-                    } else {
-                        log("EVENT_ENABLE_FAIL_FAST_MOBILE_DATA: stale arg1:" + msg.arg1
-                                + " != tag:" + tag);
-                    }
-                    break;
-                }
-                case EVENT_SAMPLE_INTERVAL_ELAPSED: {
-                    handleNetworkSamplingTimeout();
-                    break;
-                }
                 case EVENT_PROXY_HAS_CHANGED: {
                     handleApplyDefaultProxy((ProxyInfo)msg.obj);
                     break;
@@ -2638,25 +2461,27 @@
     public void reportInetCondition(int networkType, int percentage) {
         NetworkAgentInfo nai = mLegacyTypeTracker.getNetworkForType(networkType);
         if (nai == null) return;
-        boolean isGood = percentage > 50;
-        // Revalidate if the app report does not match our current validated state.
-        if (isGood != nai.lastValidated) {
-            // Make the message logged by reportBadNetwork below less confusing.
-            if (DBG && isGood) log("reportInetCondition: type=" + networkType + " ok, revalidate");
-            reportBadNetwork(nai.network);
-        }
+        reportNetworkConnectivity(nai.network, percentage > 50);
     }
 
-    public void reportBadNetwork(Network network) {
+    public void reportNetworkConnectivity(Network network, boolean hasConnectivity) {
         enforceAccessPermission();
         enforceInternetPermission();
 
-        if (network == null) return;
-
-        final int uid = Binder.getCallingUid();
-        NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
+        NetworkAgentInfo nai;
+        if (network == null) {
+            nai = getDefaultNetwork();
+        } else {
+            nai = getNetworkAgentInfoForNetwork(network);
+        }
         if (nai == null) return;
-        if (DBG) log("reportBadNetwork(" + nai.name() + ") by " + uid);
+        // Revalidate if the app report does not match our current validated state.
+        if (hasConnectivity == nai.lastValidated) return;
+        final int uid = Binder.getCallingUid();
+        if (DBG) {
+            log("reportNetworkConnectivity(" + nai.network.netId + ", " + hasConnectivity +
+                    ") by " + uid);
+        }
         synchronized (nai) {
             // Validating an uncreated network could result in a call to rematchNetworkAndRequests()
             // which isn't meant to work on uncreated networks.
@@ -2668,6 +2493,16 @@
         }
     }
 
+    public void captivePortalAppResponse(Network network, int response, String actionToken) {
+        if (response == ConnectivityManager.CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS) {
+            enforceConnectivityInternalPermission();
+        }
+        final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(network);
+        if (nai == null) return;
+        nai.networkMonitor.sendMessage(NetworkMonitor.CMD_CAPTIVE_PORTAL_APP_FINISHED, response, 0,
+                actionToken);
+    }
+
     public ProxyInfo getDefaultProxy() {
         // this information is already available as a world read/writable jvm property
         // so this API change wouldn't have a benifit.  It also breaks the passing
@@ -3066,48 +2901,6 @@
         }
     }
 
-    public void supplyMessenger(int networkType, Messenger messenger) {
-        enforceConnectivityInternalPermission();
-
-        if (isNetworkTypeValid(networkType) && mNetTrackers[networkType] != null) {
-            mNetTrackers[networkType].supplyMessenger(messenger);
-        }
-    }
-
-    public int findConnectionTypeForIface(String iface) {
-        enforceConnectivityInternalPermission();
-
-        if (TextUtils.isEmpty(iface)) return ConnectivityManager.TYPE_NONE;
-
-        synchronized(mNetworkForNetId) {
-            for (int i = 0; i < mNetworkForNetId.size(); i++) {
-                NetworkAgentInfo nai = mNetworkForNetId.valueAt(i);
-                LinkProperties lp = nai.linkProperties;
-                if (lp != null && iface.equals(lp.getInterfaceName()) && nai.networkInfo != null) {
-                    return nai.networkInfo.getType();
-                }
-            }
-        }
-        return ConnectivityManager.TYPE_NONE;
-    }
-
-    /**
-     * Have mobile data fail fast if enabled.
-     *
-     * @param enabled DctConstants.ENABLED/DISABLED
-     */
-    private void setEnableFailFastMobileData(int enabled) {
-        int tag;
-
-        if (enabled == DctConstants.ENABLED) {
-            tag = mEnableFailFastMobileDataTag.incrementAndGet();
-        } else {
-            tag = mEnableFailFastMobileDataTag.get();
-        }
-        mHandler.sendMessage(mHandler.obtainMessage(EVENT_ENABLE_FAIL_FAST_MOBILE_DATA, tag,
-                         enabled));
-    }
-
     @Override
     public int checkMobileProvisioning(int suggestedTimeOutMs) {
         // TODO: Remove?  Any reason to trigger a provisioning check?
@@ -3361,7 +3154,7 @@
                 loge("Starting user already has a VPN");
                 return;
             }
-            userVpn = new Vpn(mHandler.getLooper(), mContext, mNetd, this, userId);
+            userVpn = new Vpn(mHandler.getLooper(), mContext, mNetd, userId);
             mVpns.put(userId, userVpn);
         }
     }
@@ -3392,69 +3185,6 @@
         }
     };
 
-    /* Infrastructure for network sampling */
-
-    private void handleNetworkSamplingTimeout() {
-
-        if (SAMPLE_DBG) log("Sampling interval elapsed, updating statistics ..");
-
-        // initialize list of interfaces ..
-        Map<String, SamplingDataTracker.SamplingSnapshot> mapIfaceToSample =
-                new HashMap<String, SamplingDataTracker.SamplingSnapshot>();
-        for (NetworkStateTracker tracker : mNetTrackers) {
-            if (tracker != null) {
-                String ifaceName = tracker.getNetworkInterfaceName();
-                if (ifaceName != null) {
-                    mapIfaceToSample.put(ifaceName, null);
-                }
-            }
-        }
-
-        // Read samples for all interfaces
-        SamplingDataTracker.getSamplingSnapshots(mapIfaceToSample);
-
-        // process samples for all networks
-        for (NetworkStateTracker tracker : mNetTrackers) {
-            if (tracker != null) {
-                String ifaceName = tracker.getNetworkInterfaceName();
-                SamplingDataTracker.SamplingSnapshot ss = mapIfaceToSample.get(ifaceName);
-                if (ss != null) {
-                    // end the previous sampling cycle
-                    tracker.stopSampling(ss);
-                    // start a new sampling cycle ..
-                    tracker.startSampling(ss);
-                }
-            }
-        }
-
-        if (SAMPLE_DBG) log("Done.");
-
-        int samplingIntervalInSeconds = Settings.Global.getInt(mContext.getContentResolver(),
-                Settings.Global.CONNECTIVITY_SAMPLING_INTERVAL_IN_SECONDS,
-                DEFAULT_SAMPLING_INTERVAL_IN_SECONDS);
-
-        if (SAMPLE_DBG) {
-            log("Setting timer for " + String.valueOf(samplingIntervalInSeconds) + "seconds");
-        }
-
-        setAlarm(samplingIntervalInSeconds * 1000, mSampleIntervalElapsedIntent);
-    }
-
-    /**
-     * Sets a network sampling alarm.
-     */
-    void setAlarm(int timeoutInMilliseconds, PendingIntent intent) {
-        long wakeupTime = SystemClock.elapsedRealtime() + timeoutInMilliseconds;
-        int alarmType;
-        if (Resources.getSystem().getBoolean(
-                R.bool.config_networkSamplingWakesDevice)) {
-            alarmType = AlarmManager.ELAPSED_REALTIME_WAKEUP;
-        } else {
-            alarmType = AlarmManager.ELAPSED_REALTIME;
-        }
-        mAlarmManager.set(alarmType, wakeupTime, intent);
-    }
-
     private final HashMap<Messenger, NetworkFactoryInfo> mNetworkFactoryInfos =
             new HashMap<Messenger, NetworkFactoryInfo>();
     private final HashMap<NetworkRequest, NetworkRequestInfo> mNetworkRequests =
@@ -3569,6 +3299,24 @@
         }
     }
 
+    @Override
+    public boolean requestBwUpdate(Network network) {
+        enforceAccessPermission();
+        NetworkAgentInfo nai = null;
+        if (network == null) {
+            return false;
+        }
+        synchronized (mNetworkForNetId) {
+            nai = mNetworkForNetId.get(network.netId);
+        }
+        if (nai != null) {
+            nai.asyncChannel.sendMessage(android.net.NetworkAgent.CMD_REQUEST_BANDWIDTH_UPDATE);
+            return true;
+        }
+        return false;
+    }
+
+
     private void enforceMeteredApnPolicy(NetworkCapabilities networkCapabilities) {
         // if UID is restricted, don't allow them to bring up metered APNs
         if (networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
@@ -3676,14 +3424,23 @@
      * and the are the highest scored network available.
      * the are keyed off the Requests requestId.
      */
+    // TODO: Yikes, this is accessed on multiple threads: add synchronization.
     private final SparseArray<NetworkAgentInfo> mNetworkForRequestId =
             new SparseArray<NetworkAgentInfo>();
 
+    // NOTE: Accessed on multiple threads, must be synchronized on itself.
+    @GuardedBy("mNetworkForNetId")
     private final SparseArray<NetworkAgentInfo> mNetworkForNetId =
             new SparseArray<NetworkAgentInfo>();
+    // NOTE: Accessed on multiple threads, synchronized with mNetworkForNetId.
+    // An entry is first added to mNetIdInUse, prior to mNetworkForNetId, so
+    // there may not be a strict 1:1 correlation between the two.
+    @GuardedBy("mNetworkForNetId")
+    private final SparseBooleanArray mNetIdInUse = new SparseBooleanArray();
 
     // NetworkAgentInfo keyed off its connecting messenger
     // TODO - eval if we can reduce the number of lists/hashmaps/sparsearrays
+    // NOTE: Only should be accessed on ConnectivityServiceThread, except dump().
     private final HashMap<Messenger, NetworkAgentInfo> mNetworkAgentInfos =
             new HashMap<Messenger, NetworkAgentInfo>();
 
@@ -3698,7 +3455,7 @@
         return nai == getDefaultNetwork();
     }
 
-    public void registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
+    public int registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
             LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
             int currentScore, NetworkMisc networkMisc) {
         enforceConnectivityInternalPermission();
@@ -3706,20 +3463,23 @@
         // TODO: Instead of passing mDefaultRequest, provide an API to determine whether a Network
         // satisfies mDefaultRequest.
         NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),
-            new NetworkInfo(networkInfo), new LinkProperties(linkProperties),
-            new NetworkCapabilities(networkCapabilities), currentScore, mContext, mTrackerHandler,
-            new NetworkMisc(networkMisc), mDefaultRequest);
+                new Network(reserveNetId()), new NetworkInfo(networkInfo), new LinkProperties(
+                linkProperties), new NetworkCapabilities(networkCapabilities), currentScore,
+                mContext, mTrackerHandler, new NetworkMisc(networkMisc), mDefaultRequest);
         synchronized (this) {
             nai.networkMonitor.systemReady = mSystemReady;
         }
         if (DBG) log("registerNetworkAgent " + nai);
         mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_AGENT, nai));
+        return nai.network.netId;
     }
 
     private void handleRegisterNetworkAgent(NetworkAgentInfo na) {
         if (VDBG) log("Got NetworkAgent Messenger");
         mNetworkAgentInfos.put(na.messenger, na);
-        assignNextNetId(na);
+        synchronized (mNetworkForNetId) {
+            mNetworkForNetId.put(na.network.netId, na);
+        }
         na.asyncChannel.connect(mContext, mTrackerHandler, na.messenger);
         NetworkInfo networkInfo = na.networkInfo;
         na.networkInfo = null;
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 8c56c8c..0437a2a 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -1374,7 +1374,8 @@
                 mConnector.execute("softap", "set", wlanIface);
             } else {
                 mConnector.execute("softap", "set", wlanIface, wifiConfig.SSID,
-                                   "broadcast", "6", getSecurityType(wifiConfig),
+                                   "broadcast", Integer.toString(wifiConfig.apChannel),
+                                   getSecurityType(wifiConfig),
                                    new SensitiveArg(wifiConfig.preSharedKey));
             }
             mConnector.execute("softap", "startap");
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index f3e0bbc..2d1f939 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -40,7 +40,10 @@
  */
 public class NetworkAgentInfo {
     public NetworkInfo networkInfo;
-    public Network network;
+    // This Network object should always be used if possible, so as to encourage reuse of the
+    // enclosed socket factory and connection pool.  Avoid creating other Network objects.
+    // This Network object is always valid.
+    public final Network network;
     public LinkProperties linkProperties;
     public NetworkCapabilities networkCapabilities;
     public final NetworkMonitor networkMonitor;
@@ -83,12 +86,12 @@
     // Used by ConnectivityService to keep track of 464xlat.
     public Nat464Xlat clatd;
 
-    public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, NetworkInfo info,
+    public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info,
             LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler,
             NetworkMisc misc, NetworkRequest defaultRequest) {
         this.messenger = messenger;
         asyncChannel = ac;
-        network = null;
+        network = net;
         networkInfo = info;
         linkProperties = lp;
         networkCapabilities = nc;
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index 87f78c1..3dc5426 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -87,17 +87,6 @@
     private static final String PERMISSION_ACCESS_NETWORK_CONDITIONS =
             "android.permission.ACCESS_NETWORK_CONDITIONS";
 
-    // Keep these in sync with CaptivePortalLoginActivity.java.
-    // Intent broadcast from CaptivePortalLogin indicating sign-in is complete.
-    // Extras:
-    //     EXTRA_TEXT       = netId
-    //     LOGGED_IN_RESULT = one of the CAPTIVE_PORTAL_APP_RETURN_* values below.
-    //     RESPONSE_TOKEN   = data fragment from launching Intent
-    private static final String ACTION_CAPTIVE_PORTAL_LOGGED_IN =
-            "android.net.netmon.captive_portal_logged_in";
-    private static final String LOGGED_IN_RESULT = "result";
-    private static final String RESPONSE_TOKEN = "response_token";
-
     // After a network has been tested this result can be sent with EVENT_NETWORK_TESTED.
     // The network should be used as a default internet connection.  It was found to be:
     // 1. a functioning network providing internet access, or
@@ -170,11 +159,12 @@
 
     /**
      * Message to self indicating captive portal app finished.
-     * arg1 = one of: CAPTIVE_PORTAL_APP_RETURN_APPEASED,
+     * arg1 = one of: CAPTIVE_PORTAL_APP_RETURN_DISMISSED,
      *                CAPTIVE_PORTAL_APP_RETURN_UNWANTED,
      *                CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS
+     * obj = mCaptivePortalLoggedInResponseToken as String
      */
-    private static final int CMD_CAPTIVE_PORTAL_APP_FINISHED = BASE + 9;
+    public static final int CMD_CAPTIVE_PORTAL_APP_FINISHED = BASE + 9;
 
     /**
      * Request ConnectivityService display provisioning notification.
@@ -185,26 +175,11 @@
     public static final int EVENT_PROVISIONING_NOTIFICATION = BASE + 10;
 
     /**
-     * Message to self indicating sign-in app bypassed captive portal.
+     * Message to self indicating sign-in app should be launched.
+     * Sent by mLaunchCaptivePortalAppBroadcastReceiver when the
+     * user touches the sign in notification.
      */
-    private static final int EVENT_APP_BYPASSED_CAPTIVE_PORTAL = BASE + 11;
-
-    /**
-     * Message to self indicating no sign-in app responded.
-     */
-    private static final int EVENT_NO_APP_RESPONSE = BASE + 12;
-
-    /**
-     * Message to self indicating sign-in app indicates sign-in is not possible.
-     */
-    private static final int EVENT_APP_INDICATES_SIGN_IN_IMPOSSIBLE = BASE + 13;
-
-    /**
-     * Return codes from captive portal sign-in app.
-     */
-    public static final int CAPTIVE_PORTAL_APP_RETURN_APPEASED = 0;
-    public static final int CAPTIVE_PORTAL_APP_RETURN_UNWANTED = 1;
-    public static final int CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS = 2;
+    private static final int CMD_LAUNCH_CAPTIVE_PORTAL_APP = BASE + 11;
 
     private static final String LINGER_DELAY_PROPERTY = "persist.netmon.linger";
     // Default to 30s linger time-out.
@@ -220,7 +195,7 @@
     // If a network is not validated, make one attempt every 10 mins to see if it starts working.
     private static final int REEVALUATE_PAUSE_MS = 10*60*1000;
     private static final int PERIODIC_ATTEMPTS = 1;
-    // When an application calls reportBadNetwork, only make one attempt.
+    // When an application calls reportNetworkConnectivity, only make one attempt.
     private static final int REEVALUATE_ATTEMPTS = 1;
     private final int mReevaluateDelayMs;
     private int mReevaluateToken = 0;
@@ -259,7 +234,7 @@
     private final State mCaptivePortalState = new CaptivePortalState();
     private final State mLingeringState = new LingeringState();
 
-    private CaptivePortalLoggedInBroadcastReceiver mCaptivePortalLoggedInBroadcastReceiver = null;
+    private CustomIntentReceiver mLaunchCaptivePortalAppBroadcastReceiver = null;
     private String mCaptivePortalLoggedInResponseToken = null;
 
     public NetworkMonitor(Context context, Handler handler, NetworkAgentInfo networkAgentInfo,
@@ -323,9 +298,9 @@
                     return HANDLED;
                 case CMD_NETWORK_DISCONNECTED:
                     if (DBG) log("Disconnected - quitting");
-                    if (mCaptivePortalLoggedInBroadcastReceiver != null) {
-                        mContext.unregisterReceiver(mCaptivePortalLoggedInBroadcastReceiver);
-                        mCaptivePortalLoggedInBroadcastReceiver = null;
+                    if (mLaunchCaptivePortalAppBroadcastReceiver != null) {
+                        mContext.unregisterReceiver(mLaunchCaptivePortalAppBroadcastReceiver);
+                        mLaunchCaptivePortalAppBroadcastReceiver = null;
                     }
                     quit();
                     return HANDLED;
@@ -336,14 +311,21 @@
                     transitionTo(mEvaluatingState);
                     return HANDLED;
                 case CMD_CAPTIVE_PORTAL_APP_FINISHED:
-                    // Previous token was broadcast, come up with a new one.
+                    if (!mCaptivePortalLoggedInResponseToken.equals((String)message.obj))
+                        return HANDLED;
+                    // Previous token was sent out, come up with a new one.
                     mCaptivePortalLoggedInResponseToken = String.valueOf(new Random().nextLong());
                     switch (message.arg1) {
-                        case CAPTIVE_PORTAL_APP_RETURN_APPEASED:
-                        case CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS:
+                        case ConnectivityManager.CAPTIVE_PORTAL_APP_RETURN_DISMISSED:
+                            sendMessage(CMD_FORCE_REEVALUATION, 0 /* no UID */,
+                                    0 /* INITIAL_ATTEMPTS */);
+                            break;
+                        case ConnectivityManager.CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS:
+                            // TODO: Distinguish this from a network that actually validates.
+                            // Displaying the "!" on the system UI icon may still be a good idea.
                             transitionTo(mValidatedState);
                             break;
-                        case CAPTIVE_PORTAL_APP_RETURN_UNWANTED:
+                        case ConnectivityManager.CAPTIVE_PORTAL_APP_RETURN_UNWANTED:
                             mUserDoesNotWant = true;
                             // TODO: Should teardown network.
                             transitionTo(mOfflineState);
@@ -421,6 +403,25 @@
     // is required.  This State takes care to clear the notification upon exit from the State.
     private class MaybeNotifyState extends State {
         @Override
+        public boolean processMessage(Message message) {
+            if (DBG) log(getName() + message.toString());
+            switch (message.what) {
+                case CMD_LAUNCH_CAPTIVE_PORTAL_APP:
+                    final Intent intent = new Intent(
+                            ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN);
+                    intent.putExtra(ConnectivityManager.EXTRA_NETWORK, mNetworkAgentInfo.network);
+                    intent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_TOKEN,
+                            mCaptivePortalLoggedInResponseToken);
+                    intent.setFlags(
+                            Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
+                    mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+                    return HANDLED;
+                default:
+                    return NOT_HANDLED;
+            }
+        }
+
+        @Override
         public void exit() {
             Message message = obtainMessage(EVENT_PROVISIONING_NOTIFICATION, 0,
                     mNetworkAgentInfo.network.netId, null);
@@ -516,7 +517,9 @@
             mContext.registerReceiver(this, new IntentFilter(mAction));
         }
         public PendingIntent getPendingIntent() {
-            return PendingIntent.getBroadcast(mContext, 0, new Intent(mAction), 0);
+            final Intent intent = new Intent(mAction);
+            intent.setPackage(mContext.getPackageName());
+            return PendingIntent.getBroadcast(mContext, 0, intent, 0);
         }
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -524,48 +527,29 @@
         }
     }
 
-    private class CaptivePortalLoggedInBroadcastReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (Integer.parseInt(intent.getStringExtra(Intent.EXTRA_TEXT)) ==
-                    mNetworkAgentInfo.network.netId &&
-                    mCaptivePortalLoggedInResponseToken.equals(
-                            intent.getStringExtra(RESPONSE_TOKEN))) {
-                sendMessage(obtainMessage(CMD_CAPTIVE_PORTAL_APP_FINISHED,
-                        Integer.parseInt(intent.getStringExtra(LOGGED_IN_RESULT)), 0));
-            }
-        }
-    }
-
     // Being in the CaptivePortalState State indicates a captive portal was detected and the user
     // has been shown a notification to sign-in.
     private class CaptivePortalState extends State {
+        private static final String ACTION_LAUNCH_CAPTIVE_PORTAL_APP =
+                "android.net.netmon.launchCaptivePortalApp";
+
         @Override
         public void enter() {
             mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
                     NETWORK_TEST_RESULT_INVALID, 0, mNetworkAgentInfo));
-
-            // Assemble Intent to launch captive portal sign-in app.
-            final Intent intent = new Intent(Intent.ACTION_SEND);
-            // Intent cannot use extras because PendingIntent.getActivity will merge matching
-            // Intents erasing extras.  Use data instead of extras to encode NetID.
-            intent.setData(Uri.fromParts("netid", Integer.toString(mNetworkAgentInfo.network.netId),
-                    mCaptivePortalLoggedInResponseToken));
-            intent.setComponent(new ComponentName("com.android.captiveportallogin",
-                    "com.android.captiveportallogin.CaptivePortalLoginActivity"));
-            intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
-
-            if (mCaptivePortalLoggedInBroadcastReceiver == null) {
+            // Create a CustomIntentReceiver that sends us a
+            // CMD_LAUNCH_CAPTIVE_PORTAL_APP message when the user
+            // touches the notification.
+            if (mLaunchCaptivePortalAppBroadcastReceiver == null) {
                 // Wait for result.
-                mCaptivePortalLoggedInBroadcastReceiver =
-                        new CaptivePortalLoggedInBroadcastReceiver();
-                final IntentFilter filter = new IntentFilter(ACTION_CAPTIVE_PORTAL_LOGGED_IN);
-                mContext.registerReceiver(mCaptivePortalLoggedInBroadcastReceiver, filter);
+                mLaunchCaptivePortalAppBroadcastReceiver = new CustomIntentReceiver(
+                        ACTION_LAUNCH_CAPTIVE_PORTAL_APP, new Random().nextInt(),
+                        CMD_LAUNCH_CAPTIVE_PORTAL_APP);
             }
-            // Initiate notification to sign-in.
+            // Display the sign in notification.
             Message message = obtainMessage(EVENT_PROVISIONING_NOTIFICATION, 1,
                     mNetworkAgentInfo.network.netId,
-                    PendingIntent.getActivity(mContext, 0, intent, 0));
+                    mLaunchCaptivePortalAppBroadcastReceiver.getPendingIntent());
             mConnectivityServiceHandler.sendMessage(message);
         }
 
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 8533f69..d9c96f2 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -17,9 +17,10 @@
 package com.android.server.connectivity;
 
 import static android.Manifest.permission.BIND_VPN_SERVICE;
-import static android.os.UserHandle.PER_USER_RANGE;
+import static android.net.ConnectivityManager.NETID_UNSET;
 import static android.net.RouteInfo.RTN_THROW;
 import static android.net.RouteInfo.RTN_UNREACHABLE;
+import static android.os.UserHandle.PER_USER_RANGE;
 import static android.system.OsConstants.AF_INET;
 import static android.system.OsConstants.AF_INET6;
 
@@ -69,6 +70,7 @@
 import android.os.UserManager;
 import android.security.Credentials;
 import android.security.KeyStore;
+import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
@@ -114,7 +116,6 @@
     private LegacyVpnRunner mLegacyVpnRunner;
     private PendingIntent mStatusIntent;
     private volatile boolean mEnableTeardown = true;
-    private final IConnectivityManager mConnService;
     private final INetworkManagementService mNetd;
     private VpnConfig mConfig;
     private NetworkAgent mNetworkAgent;
@@ -130,10 +131,9 @@
     private final int mUserHandle;
 
     public Vpn(Looper looper, Context context, INetworkManagementService netService,
-            IConnectivityManager connService, int userHandle) {
+            int userHandle) {
         mContext = context;
         mNetd = netService;
-        mConnService = connService;
         mUserHandle = userHandle;
         mLooper = looper;
 
@@ -340,6 +340,10 @@
         return mNetworkInfo;
     }
 
+    public int getNetId() {
+        return mNetworkAgent != null ? mNetworkAgent.netId : NETID_UNSET;
+    }
+
     private LinkProperties makeLinkProperties() {
         boolean allowIPv4 = mConfig.allowIPv4;
         boolean allowIPv6 = mConfig.allowIPv6;
@@ -1086,11 +1090,15 @@
             // registering
             mOuterInterface = mConfig.interfaze;
 
-            try {
-                mOuterConnection.set(
-                        mConnService.findConnectionTypeForIface(mOuterInterface));
-            } catch (Exception e) {
-                mOuterConnection.set(ConnectivityManager.TYPE_NONE);
+            if (!TextUtils.isEmpty(mOuterInterface)) {
+                final ConnectivityManager cm = ConnectivityManager.from(mContext);
+                for (Network network : cm.getAllNetworks()) {
+                    final LinkProperties lp = cm.getLinkProperties(network);
+                    if (lp != null && mOuterInterface.equals(lp.getInterfaceName())) {
+                        final NetworkInfo networkInfo = cm.getNetworkInfo(network);
+                        if (networkInfo != null) mOuterConnection.set(networkInfo.getType());
+                    }
+                }
             }
 
             IntentFilter filter = new IntentFilter();
diff --git a/services/core/java/com/android/server/telecom/TelecomLoaderService.java b/services/core/java/com/android/server/telecom/TelecomLoaderService.java
index 64a67fc..22fee22 100644
--- a/services/core/java/com/android/server/telecom/TelecomLoaderService.java
+++ b/services/core/java/com/android/server/telecom/TelecomLoaderService.java
@@ -63,7 +63,7 @@
 
     private static final ComponentName SERVICE_COMPONENT = new ComponentName(
             "com.android.server.telecom",
-            "com.android.server.telecom.TelecomService");
+            "com.android.server.telecom.components.TelecomService");
 
     private static final String SERVICE_ACTION = "com.android.ITelecomService";
 
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 334cdf6..0705fbd 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -677,7 +677,8 @@
 
                 mSystemServiceManager.startService("com.android.server.wifi.RttService");
 
-                if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_ETHERNET)) {
+                if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_ETHERNET) ||
+                    mPackageManager.hasSystemFeature(PackageManager.FEATURE_USB_HOST)) {
                     mSystemServiceManager.startService(ETHERNET_SERVICE_CLASS);
                 }
 
diff --git a/services/net/Android.mk b/services/net/Android.mk
new file mode 100644
index 0000000..336bc45
--- /dev/null
+++ b/services/net/Android.mk
@@ -0,0 +1,10 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := services.net
+
+LOCAL_SRC_FILES += \
+    $(call all-java-files-under,java)
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/core/java/android/net/dhcp/DhcpAckPacket.java b/services/net/java/android/net/dhcp/DhcpAckPacket.java
similarity index 77%
rename from core/java/android/net/dhcp/DhcpAckPacket.java
rename to services/net/java/android/net/dhcp/DhcpAckPacket.java
index 7b8be9c..25b8093 100644
--- a/core/java/android/net/dhcp/DhcpAckPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpAckPacket.java
@@ -16,7 +16,6 @@
 
 package android.net.dhcp;
 
-import java.net.InetAddress;
 import java.net.Inet4Address;
 import java.nio.ByteBuffer;
 
@@ -28,12 +27,11 @@
     /**
      * The address of the server which sent this packet.
      */
-    private final InetAddress mSrcIp;
+    private final Inet4Address mSrcIp;
 
-    DhcpAckPacket(int transId, boolean broadcast, InetAddress serverAddress,
-                  InetAddress clientIp, byte[] clientMac) {
-        super(transId, Inet4Address.ANY, clientIp, serverAddress,
-            Inet4Address.ANY, clientMac, broadcast);
+    DhcpAckPacket(int transId, boolean broadcast, Inet4Address serverAddress,
+                  Inet4Address clientIp, byte[] clientMac) {
+        super(transId, INADDR_ANY, clientIp, serverAddress, INADDR_ANY, clientMac, broadcast);
         mBroadcast = broadcast;
         mSrcIp = serverAddress;
     }
@@ -42,7 +40,7 @@
         String s = super.toString();
         String dnsServers = " DNS servers: ";
 
-        for (InetAddress dnsServer: mDnsServers) {
+        for (Inet4Address dnsServer: mDnsServers) {
             dnsServers += dnsServer.toString() + " ";
         }
 
@@ -57,8 +55,8 @@
      */
     public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
         ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
-        InetAddress destIp = mBroadcast ? Inet4Address.ALL : mYourIp;
-        InetAddress srcIp = mBroadcast ? Inet4Address.ANY : mSrcIp;
+        Inet4Address destIp = mBroadcast ? INADDR_BROADCAST : mYourIp;
+        Inet4Address srcIp = mBroadcast ? INADDR_ANY : mSrcIp;
 
         fillInPacket(encap, destIp, srcIp, destUdp, srcUdp, result,
             DHCP_BOOTREPLY, mBroadcast);
@@ -98,12 +96,4 @@
             return v.intValue();
         }
     }
-
-    /**
-     * Notifies the specified state machine of the ACK packet parameters.
-     */
-    public void doNextOp(DhcpStateMachine machine) {
-        machine.onAckReceived(mYourIp, mSubnetMask, mGateway, mDnsServers,
-            mServerIdentifier, getInt(mLeaseTime));
-    }
 }
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
new file mode 100644
index 0000000..e9203a4
--- /dev/null
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -0,0 +1,809 @@
+/*
+ * Copyright (C) 2015 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.net.dhcp;
+
+import com.android.internal.util.HexDump;
+import com.android.internal.util.Protocol;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.DhcpResults;
+import android.net.BaseDhcpStateMachine;
+import android.net.DhcpStateMachine;
+import android.net.InterfaceConfiguration;
+import android.net.LinkAddress;
+import android.net.NetworkUtils;
+import android.os.IBinder;
+import android.os.INetworkManagementService;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.PacketSocketAddress;
+import android.util.Log;
+import android.util.TimeUtils;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.lang.Thread;
+import java.net.Inet4Address;
+import java.net.InetSocketAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Random;
+
+import libcore.io.IoUtils;
+
+import static android.system.OsConstants.*;
+import static android.net.dhcp.DhcpPacket.*;
+
+/**
+ * A DHCPv4 client.
+ *
+ * Written to behave similarly to the DhcpStateMachine + dhcpcd 5.5.6 combination used in Android
+ * 5.1 and below, as configured on Nexus 6. The interface is the same as DhcpStateMachine.
+ *
+ * TODO:
+ *
+ * - Exponential backoff when receiving NAKs (not specified by the RFC, but current behaviour).
+ * - Support persisting lease state and support INIT-REBOOT. Android 5.1 does this, but it does not
+ *   do so correctly: instead of requesting the lease last obtained on a particular network (e.g., a
+ *   given SSID), it requests the last-leased IP address on the same interface, causing a delay if
+ *   the server NAKs or a timeout if it doesn't.
+ *
+ * Known differences from current behaviour:
+ *
+ * - Does not request the "static routes" option.
+ * - Does not support BOOTP servers. DHCP has been around since 1993, should be everywhere now.
+ * - Requests the "broadcast" option, but does nothing with it.
+ * - Rejects invalid subnet masks such as 255.255.255.1 (current code treats that as 255.255.255.0).
+ *
+ * @hide
+ */
+public class DhcpClient extends BaseDhcpStateMachine {
+
+    private static final String TAG = "DhcpClient";
+    private static final boolean DBG = true;
+    private static final boolean STATE_DBG = false;
+    private static final boolean MSG_DBG = false;
+
+    // Timers and timeouts.
+    private static final int SECONDS = 1000;
+    private static final int FIRST_TIMEOUT_MS   =   2 * SECONDS;
+    private static final int MAX_TIMEOUT_MS     = 128 * SECONDS;
+
+    // This is not strictly needed, since the client is asynchronous and implements exponential
+    // backoff. It's maintained for backwards compatibility with the previous DHCP code, which was
+    // a blocking operation with a 30-second timeout. We pick 36 seconds so we can send packets at
+    // t=0, t=2, t=6, t=14, t=30, allowing for 10% jitter.
+    private static final int DHCP_TIMEOUT_MS    =  36 * SECONDS;
+
+    // Messages.
+    private static final int BASE                 = Protocol.BASE_DHCP + 100;
+    private static final int CMD_KICK             = BASE + 1;
+    private static final int CMD_RECEIVED_PACKET  = BASE + 2;
+    private static final int CMD_TIMEOUT          = BASE + 3;
+
+    // DHCP parameters that we request.
+    private static final byte[] REQUESTED_PARAMS = new byte[] {
+        DHCP_SUBNET_MASK,
+        DHCP_ROUTER,
+        DHCP_DNS_SERVER,
+        DHCP_DOMAIN_NAME,
+        DHCP_MTU,
+        DHCP_BROADCAST_ADDRESS,  // TODO: currently ignored.
+        DHCP_LEASE_TIME,
+        DHCP_RENEWAL_TIME,
+        DHCP_REBINDING_TIME,
+    };
+
+    // DHCP flag that means "yes, we support unicast."
+    private static final boolean DO_UNICAST   = false;
+
+    // System services / libraries we use.
+    private final Context mContext;
+    private final AlarmManager mAlarmManager;
+    private final Random mRandom;
+    private final INetworkManagementService mNMService;
+
+    // Sockets.
+    // - We use a packet socket to receive, because servers send us packets bound for IP addresses
+    //   which we have not yet configured, and the kernel protocol stack drops these.
+    // - We use a UDP socket to send, so the kernel handles ARP and routing for us (DHCP servers can
+    //   be off-link as well as on-link).
+    private FileDescriptor mPacketSock;
+    private FileDescriptor mUdpSock;
+    private ReceiveThread mReceiveThread;
+
+    // State variables.
+    private final StateMachine mController;
+    private final PendingIntent mKickIntent;
+    private final PendingIntent mTimeoutIntent;
+    private final PendingIntent mRenewIntent;
+    private final String mIfaceName;
+
+    private boolean mRegisteredForPreDhcpNotification;
+    private NetworkInterface mIface;
+    private byte[] mHwAddr;
+    private PacketSocketAddress mInterfaceBroadcastAddr;
+    private int mTransactionId;
+    private DhcpResults mDhcpLease;
+    private long mDhcpLeaseExpiry;
+    private DhcpResults mOffer;
+
+    // States.
+    private State mStoppedState = new StoppedState();
+    private State mDhcpState = new DhcpState();
+    private State mDhcpInitState = new DhcpInitState();
+    private State mDhcpSelectingState = new DhcpSelectingState();
+    private State mDhcpRequestingState = new DhcpRequestingState();
+    private State mDhcpBoundState = new DhcpBoundState();
+    private State mDhcpRenewingState = new DhcpRenewingState();
+    private State mDhcpRebindingState = new DhcpRebindingState();
+    private State mDhcpInitRebootState = new DhcpInitRebootState();
+    private State mDhcpRebootingState = new DhcpRebootingState();
+    private State mWaitBeforeStartState = new WaitBeforeStartState(mDhcpInitState);
+    private State mWaitBeforeRenewalState = new WaitBeforeRenewalState(mDhcpRenewingState);
+
+    private DhcpClient(Context context, StateMachine controller, String iface) {
+        super(TAG);
+
+        mContext = context;
+        mController = controller;
+        mIfaceName = iface;
+
+        addState(mStoppedState);
+        addState(mDhcpState);
+            addState(mDhcpInitState, mDhcpState);
+            addState(mWaitBeforeStartState, mDhcpState);
+            addState(mDhcpSelectingState, mDhcpState);
+            addState(mDhcpRequestingState, mDhcpState);
+            addState(mDhcpBoundState, mDhcpState);
+            addState(mWaitBeforeRenewalState, mDhcpState);
+            addState(mDhcpRenewingState, mDhcpState);
+            addState(mDhcpRebindingState, mDhcpState);
+            addState(mDhcpInitRebootState, mDhcpState);
+            addState(mDhcpRebootingState, mDhcpState);
+
+        setInitialState(mStoppedState);
+
+        mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+        IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+        mNMService = INetworkManagementService.Stub.asInterface(b);
+
+        mRandom = new Random();
+
+        mKickIntent = createStateMachineCommandIntent("KICK", CMD_KICK);
+        mTimeoutIntent = createStateMachineCommandIntent("TIMEOUT", CMD_TIMEOUT);
+        mRenewIntent = createStateMachineCommandIntent("RENEW", DhcpStateMachine.CMD_RENEW_DHCP);
+    }
+
+    @Override
+    public void registerForPreDhcpNotification() {
+        mRegisteredForPreDhcpNotification = true;
+    }
+
+    public static BaseDhcpStateMachine makeDhcpStateMachine(
+            Context context, StateMachine controller, String intf) {
+        DhcpClient client = new DhcpClient(context, controller, intf);
+        client.start();
+        return client;
+    }
+
+    /**
+     * Constructs a PendingIntent that sends the specified command to the state machine. This is
+     * implemented by creating an Intent with the specified parameters, and creating and registering
+     * a BroadcastReceiver for it. The broadcast must be sent by a process that holds the
+     * {@code CONNECTIVITY_INTERNAL} permission.
+     *
+     * @param cmdName the name of the command. The intent's action will be
+     *         {@code android.net.dhcp.DhcpClient.<cmdName>}
+     * @param cmd the command to send to the state machine when the PendingIntent is triggered.
+     * @return the PendingIntent
+     */
+    private PendingIntent createStateMachineCommandIntent(final String cmdName, final int cmd) {
+        String action = DhcpClient.class.getName() + "." + cmdName;
+
+        // TODO: figure out what values to pass to intent.setPackage() and intent.setClass() that
+        // result in the Intent being received by this class and nowhere else, and use them.
+        Intent intent = new Intent(action, null)
+                .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+        PendingIntent pendingIntent =  PendingIntent.getBroadcast(mContext, cmd, intent, 0);
+
+        mContext.registerReceiver(
+            new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    sendMessage(cmd);
+                }
+            },
+            new IntentFilter(action),
+            android.Manifest.permission.CONNECTIVITY_INTERNAL,
+            null);
+
+        return pendingIntent;
+    }
+
+    private boolean initInterface() {
+        try {
+            mIface = NetworkInterface.getByName(mIfaceName);
+            mHwAddr = mIface.getHardwareAddress();
+            mInterfaceBroadcastAddr = new PacketSocketAddress(mIface.getIndex(),
+                    DhcpPacket.ETHER_BROADCAST);
+            return true;
+        } catch(SocketException e) {
+            Log.wtf(TAG, "Can't determine ifindex or MAC address for " + mIfaceName);
+            return false;
+        }
+    }
+
+    private void initTransactionId() {
+        mTransactionId = mRandom.nextInt();
+    }
+
+    private boolean initSockets() {
+        try {
+            mPacketSock = Os.socket(AF_PACKET, SOCK_RAW, ETH_P_IP);
+            PacketSocketAddress addr = new PacketSocketAddress((short) ETH_P_IP, mIface.getIndex());
+            Os.bind(mPacketSock, addr);
+            NetworkUtils.attachDhcpFilter(mPacketSock);
+        } catch(SocketException|ErrnoException e) {
+            Log.e(TAG, "Error creating packet socket", e);
+            return false;
+        }
+        try {
+            mUdpSock = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+            Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_REUSEADDR, 1);
+            Os.setsockoptIfreq(mUdpSock, SOL_SOCKET, SO_BINDTODEVICE, mIfaceName);
+            Os.setsockoptInt(mUdpSock, SOL_SOCKET, SO_BROADCAST, 1);
+            Os.bind(mUdpSock, Inet4Address.ANY, DhcpPacket.DHCP_CLIENT);
+            NetworkUtils.protectFromVpn(mUdpSock);
+        } catch(SocketException|ErrnoException e) {
+            Log.e(TAG, "Error creating UDP socket", e);
+            return false;
+        }
+        return true;
+    }
+
+    private void closeSockets() {
+        IoUtils.closeQuietly(mUdpSock);
+        IoUtils.closeQuietly(mPacketSock);
+    }
+
+    private boolean setIpAddress(LinkAddress address) {
+        InterfaceConfiguration ifcg = new InterfaceConfiguration();
+        ifcg.setLinkAddress(address);
+        try {
+            mNMService.setInterfaceConfig(mIfaceName, ifcg);
+        } catch (RemoteException|IllegalStateException e) {
+            Log.e(TAG, "Error configuring IP address : " + e);
+            return false;
+        }
+        return true;
+    }
+
+    class ReceiveThread extends Thread {
+
+        private final byte[] mPacket = new byte[DhcpPacket.MAX_LENGTH];
+        private boolean stopped = false;
+
+        public void halt() {
+            stopped = true;
+            closeSockets();  // Interrupts the read() call the thread is blocked in.
+        }
+
+        @Override
+        public void run() {
+            maybeLog("Starting receive thread");
+            while (!stopped) {
+                try {
+                    int length = Os.read(mPacketSock, mPacket, 0, mPacket.length);
+                    DhcpPacket packet = null;
+                    packet = DhcpPacket.decodeFullPacket(mPacket, length, DhcpPacket.ENCAP_L2);
+                    if (packet != null) {
+                        maybeLog("Received packet: " + packet);
+                        sendMessage(CMD_RECEIVED_PACKET, packet);
+                    }
+                } catch (IOException|ErrnoException e) {
+                    if (!stopped) {
+                        Log.e(TAG, "Read error", e);
+                    }
+                }
+            }
+            maybeLog("Stopping receive thread");
+        }
+    }
+
+    private boolean transmitPacket(ByteBuffer buf, String description, Inet4Address to) {
+        try {
+            if (to.equals(INADDR_BROADCAST)) {
+                maybeLog("Broadcasting " + description);
+                Os.sendto(mPacketSock, buf.array(), 0, buf.limit(), 0, mInterfaceBroadcastAddr);
+            } else {
+                maybeLog("Unicasting " + description + " to " + to.getHostAddress());
+                Os.sendto(mUdpSock, buf, 0, to, DhcpPacket.DHCP_SERVER);
+            }
+        } catch(ErrnoException|IOException e) {
+            Log.e(TAG, "Can't send packet: ", e);
+            return false;
+        }
+        return true;
+    }
+
+    private boolean sendDiscoverPacket() {
+        ByteBuffer packet = DhcpPacket.buildDiscoverPacket(
+                DhcpPacket.ENCAP_L2, mTransactionId, mHwAddr, DO_UNICAST, REQUESTED_PARAMS);
+        return transmitPacket(packet, "DHCPDISCOVER", INADDR_BROADCAST);
+    }
+
+    private boolean sendRequestPacket(
+            Inet4Address clientAddress, Inet4Address requestedAddress,
+            Inet4Address serverAddress, Inet4Address to) {
+        // TODO: should we use the transaction ID from the server?
+        int encap = to.equals(INADDR_BROADCAST) ? DhcpPacket.ENCAP_L2 : DhcpPacket.ENCAP_BOOTP;
+
+        ByteBuffer packet = DhcpPacket.buildRequestPacket(
+                encap, mTransactionId, clientAddress,
+                DO_UNICAST, mHwAddr, requestedAddress,
+                serverAddress, REQUESTED_PARAMS, null);
+        String description = "DHCPREQUEST ciaddr=" + clientAddress.getHostAddress() +
+                             " request=" + requestedAddress.getHostAddress() +
+                             " to=" + serverAddress.getHostAddress();
+        return transmitPacket(packet, description, to);
+    }
+
+    private void scheduleRenew() {
+        long now = SystemClock.elapsedRealtime();
+        long alarmTime = (now + mDhcpLeaseExpiry) / 2;
+        mAlarmManager.cancel(mRenewIntent);
+        mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, alarmTime, mRenewIntent);
+        Log.d(TAG, "Scheduling renewal in " + ((alarmTime - now) / 1000) + "s");
+    }
+
+    private void notifyLease() {
+        mController.sendMessage(DhcpStateMachine.CMD_POST_DHCP_ACTION,
+                DhcpStateMachine.DHCP_SUCCESS, 0, mDhcpLease);
+    }
+
+    private void notifyFailure() {
+        mController.sendMessage(DhcpStateMachine.CMD_POST_DHCP_ACTION,
+                DhcpStateMachine.DHCP_FAILURE, 0, null);
+    }
+
+    private void clearDhcpState() {
+        mDhcpLease = null;
+        mDhcpLeaseExpiry = 0;
+        mOffer = null;
+    }
+
+    /**
+     * Quit the DhcpStateMachine.
+     *
+     * @hide
+     */
+    @Override
+    public void doQuit() {
+        Log.d(TAG, "doQuit");
+        quit();
+    }
+
+    protected void onQuitting() {
+        Log.d(TAG, "onQuitting");
+        mController.sendMessage(DhcpStateMachine.CMD_ON_QUIT);
+    }
+
+    private void maybeLog(String msg) {
+        if (DBG) Log.d(TAG, msg);
+    }
+
+    abstract class LoggingState extends State {
+        public void enter() {
+            if (STATE_DBG) Log.d(TAG, "Entering state " + getName());
+        }
+
+        private String messageName(int what) {
+            switch (what) {
+                case DhcpStateMachine.CMD_START_DHCP:
+                    return "CMD_START_DHCP";
+                case DhcpStateMachine.CMD_STOP_DHCP:
+                    return "CMD_STOP_DHCP";
+                case DhcpStateMachine.CMD_RENEW_DHCP:
+                    return "CMD_RENEW_DHCP";
+                case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
+                    return "CMD_PRE_DHCP_ACTION";
+                case DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE:
+                    return "CMD_PRE_DHCP_ACTION_COMPLETE";
+                case DhcpStateMachine.CMD_POST_DHCP_ACTION:
+                    return "CMD_POST_DHCP_ACTION";
+                case CMD_KICK:
+                    return "CMD_KICK";
+                case CMD_RECEIVED_PACKET:
+                    return "CMD_RECEIVED_PACKET";
+                default:
+                    return Integer.toString(what);
+            }
+        }
+
+        private String messageToString(Message message) {
+            long now = SystemClock.uptimeMillis();
+            StringBuilder b = new StringBuilder(" ");
+            TimeUtils.formatDuration(message.getWhen() - now, b);
+            b.append(" ").append(messageName(message.what))
+                    .append(" ").append(message.arg1)
+                    .append(" ").append(message.arg2)
+                    .append(" ").append(message.obj);
+            return b.toString();
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            if (MSG_DBG) {
+                Log.d(TAG, getName() + messageToString(message));
+            }
+            return NOT_HANDLED;
+        }
+    }
+
+    // Sends CMD_PRE_DHCP_ACTION to the controller, waits for the controller to respond with
+    // CMD_PRE_DHCP_ACTION_COMPLETE, and then transitions to mOtherState.
+    abstract class WaitBeforeOtherState extends LoggingState {
+        protected State mOtherState;
+
+        @Override
+        public void enter() {
+            super.enter();
+            mController.sendMessage(DhcpStateMachine.CMD_PRE_DHCP_ACTION);
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            super.processMessage(message);
+            switch (message.what) {
+                case DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE:
+                    transitionTo(mOtherState);
+                    return HANDLED;
+                default:
+                    return NOT_HANDLED;
+            }
+        }
+    }
+
+    class StoppedState extends LoggingState {
+        @Override
+        public boolean processMessage(Message message) {
+            super.processMessage(message);
+            switch (message.what) {
+                case DhcpStateMachine.CMD_START_DHCP:
+                    if (mRegisteredForPreDhcpNotification) {
+                        transitionTo(mWaitBeforeStartState);
+                    } else {
+                        transitionTo(mDhcpInitState);
+                    }
+                    return HANDLED;
+                default:
+                    return NOT_HANDLED;
+            }
+        }
+    }
+
+    class WaitBeforeStartState extends WaitBeforeOtherState {
+        public WaitBeforeStartState(State otherState) {
+            super();
+            mOtherState = otherState;
+        }
+    }
+
+    class WaitBeforeRenewalState extends WaitBeforeOtherState {
+        public WaitBeforeRenewalState(State otherState) {
+            super();
+            mOtherState = otherState;
+        }
+    }
+
+    class DhcpState extends LoggingState {
+        @Override
+        public void enter() {
+            super.enter();
+            clearDhcpState();
+            if (initInterface() && initSockets()) {
+                mReceiveThread = new ReceiveThread();
+                mReceiveThread.start();
+            } else {
+                notifyFailure();
+                transitionTo(mStoppedState);
+            }
+        }
+
+        @Override
+        public void exit() {
+            mReceiveThread.halt();  // Also closes sockets.
+            clearDhcpState();
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            super.processMessage(message);
+            switch (message.what) {
+                case DhcpStateMachine.CMD_STOP_DHCP:
+                    transitionTo(mStoppedState);
+                    return HANDLED;
+                default:
+                    return NOT_HANDLED;
+            }
+        }
+    }
+
+    public boolean isValidPacket(DhcpPacket packet) {
+        // TODO: check checksum.
+        int xid = packet.getTransactionId();
+        if (xid != mTransactionId) {
+            Log.d(TAG, "Unexpected transaction ID " + xid + ", expected " + mTransactionId);
+            return false;
+        }
+        if (!Arrays.equals(packet.getClientMac(), mHwAddr)) {
+            Log.d(TAG, "MAC addr mismatch: got " +
+                    HexDump.toHexString(packet.getClientMac()) + ", expected " +
+                    HexDump.toHexString(packet.getClientMac()));
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Retransmits packets using jittered exponential backoff with an optional timeout. Packet
+     * transmission is triggered by CMD_KICK, which is sent by an AlarmManager alarm.
+     *
+     * Concrete subclasses must implement sendPacket, which is called when the alarm fires and a
+     * packet needs to be transmitted, and receivePacket, which is triggered by CMD_RECEIVED_PACKET
+     * sent by the receive thread. They may implement timeout, which is called when the timeout
+     * fires.
+     */
+    abstract class PacketRetransmittingState extends LoggingState {
+
+        private int mTimer;
+        protected int mTimeout = 0;
+
+        @Override
+        public void enter() {
+            super.enter();
+            initTimer();
+            maybeInitTimeout();
+            sendMessage(CMD_KICK);
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            super.processMessage(message);
+            switch (message.what) {
+                case CMD_KICK:
+                    sendPacket();
+                    scheduleKick();
+                    return HANDLED;
+                case CMD_RECEIVED_PACKET:
+                    receivePacket((DhcpPacket) message.obj);
+                    return HANDLED;
+                case CMD_TIMEOUT:
+                    timeout();
+                    return HANDLED;
+                default:
+                    return NOT_HANDLED;
+            }
+        }
+
+        public void exit() {
+            mAlarmManager.cancel(mKickIntent);
+            mAlarmManager.cancel(mTimeoutIntent);
+        }
+
+        abstract protected boolean sendPacket();
+        abstract protected void receivePacket(DhcpPacket packet);
+        protected void timeout() {}
+
+        protected void initTimer() {
+            mTimer = FIRST_TIMEOUT_MS;
+        }
+
+        protected int jitterTimer(int baseTimer) {
+            int maxJitter = baseTimer / 10;
+            int jitter = mRandom.nextInt(2 * maxJitter) - maxJitter;
+            return baseTimer + jitter;
+        }
+
+        protected void scheduleKick() {
+            long now = SystemClock.elapsedRealtime();
+            long timeout = jitterTimer(mTimer);
+            long alarmTime = now + timeout;
+            mAlarmManager.cancel(mKickIntent);
+            mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, alarmTime, mKickIntent);
+            mTimer *= 2;
+            if (mTimer > MAX_TIMEOUT_MS) {
+                mTimer = MAX_TIMEOUT_MS;
+            }
+        }
+
+        protected void maybeInitTimeout() {
+            if (mTimeout > 0) {
+                long alarmTime = SystemClock.elapsedRealtime() + mTimeout;
+                mAlarmManager.setExact(
+                        AlarmManager.ELAPSED_REALTIME_WAKEUP, alarmTime, mTimeoutIntent);
+            }
+        }
+    }
+
+    class DhcpInitState extends PacketRetransmittingState {
+        public DhcpInitState() {
+            super();
+            mTimeout = DHCP_TIMEOUT_MS;
+        }
+
+        @Override
+        public void enter() {
+            super.enter();
+            initTransactionId();
+        }
+
+        protected boolean sendPacket() {
+            return sendDiscoverPacket();
+        }
+
+        protected void timeout() {
+            maybeLog("Timeout");
+            notifyFailure();
+        }
+
+        protected void receivePacket(DhcpPacket packet) {
+            if (!isValidPacket(packet)) return;
+            if (!(packet instanceof DhcpOfferPacket)) return;
+            mOffer = packet.toDhcpResults();
+            if (mOffer != null) {
+                Log.d(TAG, "Got pending lease: " + mOffer);
+                transitionTo(mDhcpRequestingState);
+            }
+        }
+    }
+
+    // Not implemented. We request the first offer we receive.
+    class DhcpSelectingState extends LoggingState {
+    }
+
+    class DhcpRequestingState extends PacketRetransmittingState {
+        public DhcpRequestingState() {
+            super();
+            mTimeout = DHCP_TIMEOUT_MS / 2;
+        }
+
+        protected boolean sendPacket() {
+            return sendRequestPacket(
+                    INADDR_ANY,                                    // ciaddr
+                    (Inet4Address) mOffer.ipAddress.getAddress(),  // DHCP_REQUESTED_IP
+                    (Inet4Address) mOffer.serverAddress,           // DHCP_SERVER_IDENTIFIER
+                    INADDR_BROADCAST);                             // packet destination address
+        }
+
+        protected void receivePacket(DhcpPacket packet) {
+            if (!isValidPacket(packet)) return;
+            if ((packet instanceof DhcpAckPacket)) {
+                DhcpResults results = packet.toDhcpResults();
+                if (results != null) {
+                    mDhcpLease = results;
+                    Log.d(TAG, "Confirmed lease: " + mDhcpLease);
+                    mDhcpLeaseExpiry = SystemClock.elapsedRealtime() +
+                            mDhcpLease.leaseDuration * 1000;
+                    mOffer = null;
+                    transitionTo(mDhcpBoundState);
+                }
+            } else if (packet instanceof DhcpNakPacket) {
+                Log.d(TAG, "Received NAK, returning to INIT");
+                mOffer = null;
+                transitionTo(mDhcpInitState);
+            }
+        }
+
+        protected void timeout() {
+            notifyFailure();
+            transitionTo(mDhcpInitState);
+        }
+    }
+
+    class DhcpBoundState extends LoggingState {
+        @Override
+        public void enter() {
+            super.enter();
+            if (!setIpAddress(mDhcpLease.ipAddress)) {
+                notifyFailure();
+                transitionTo(mStoppedState);
+            }
+            notifyLease();
+            // TODO: DhcpStateMachine only supports renewing at 50% of the lease time, and does not
+            // support rebinding. Fix this.
+            scheduleRenew();
+        }
+
+        @Override
+        public boolean processMessage(Message message) {
+            super.processMessage(message);
+            switch (message.what) {
+                case DhcpStateMachine.CMD_RENEW_DHCP:
+                    if (mRegisteredForPreDhcpNotification) {
+                        transitionTo(mWaitBeforeRenewalState);
+                    } else {
+                        transitionTo(mDhcpRenewingState);
+                    }
+                    return HANDLED;
+                default:
+                    return NOT_HANDLED;
+            }
+        }
+    }
+
+    // TODO: timeout.
+    class DhcpRenewingState extends PacketRetransmittingState {
+        public DhcpRenewingState() {
+            super();
+            mTimeout = DHCP_TIMEOUT_MS;
+        }
+
+        @Override
+        public void enter() {
+            super.enter();
+            initTransactionId();
+        }
+
+        protected boolean sendPacket() {
+            return sendRequestPacket(
+                    (Inet4Address) mDhcpLease.ipAddress.getAddress(),  // ciaddr
+                    INADDR_ANY,                                        // DHCP_REQUESTED_IP
+                    INADDR_ANY,                                        // DHCP_SERVER_IDENTIFIER
+                    (Inet4Address) mDhcpLease.serverAddress);          // packet destination address
+        }
+
+        protected void receivePacket(DhcpPacket packet) {
+            if (!isValidPacket(packet)) return;
+            if ((packet instanceof DhcpAckPacket)) {
+                DhcpResults results = packet.toDhcpResults();
+                mDhcpLease.leaseDuration = results.leaseDuration;
+                mDhcpLeaseExpiry = SystemClock.elapsedRealtime() +
+                        mDhcpLease.leaseDuration * 1000;
+                transitionTo(mDhcpBoundState);
+            } else if (packet instanceof DhcpNakPacket) {
+                transitionTo(mDhcpInitState);
+            }
+        }
+    }
+
+    // Not implemented. DhcpStateMachine does not implement it either.
+    class DhcpRebindingState extends LoggingState {
+    }
+
+    class DhcpInitRebootState extends LoggingState {
+    }
+
+    class DhcpRebootingState extends LoggingState {
+    }
+}
diff --git a/core/java/android/net/dhcp/DhcpDeclinePacket.java b/services/net/java/android/net/dhcp/DhcpDeclinePacket.java
similarity index 81%
rename from core/java/android/net/dhcp/DhcpDeclinePacket.java
rename to services/net/java/android/net/dhcp/DhcpDeclinePacket.java
index 7646eb4..9d985ac 100644
--- a/core/java/android/net/dhcp/DhcpDeclinePacket.java
+++ b/services/net/java/android/net/dhcp/DhcpDeclinePacket.java
@@ -16,7 +16,7 @@
 
 package android.net.dhcp;
 
-import java.net.InetAddress;
+import java.net.Inet4Address;
 import java.nio.ByteBuffer;
 
 /**
@@ -26,8 +26,8 @@
     /**
      * Generates a DECLINE packet with the specified parameters.
      */
-    DhcpDeclinePacket(int transId, InetAddress clientIp, InetAddress yourIp,
-                      InetAddress nextIp, InetAddress relayIp,
+    DhcpDeclinePacket(int transId, Inet4Address clientIp, Inet4Address yourIp,
+                      Inet4Address nextIp, Inet4Address relayIp,
                       byte[] clientMac) {
         super(transId, clientIp, yourIp, nextIp, relayIp, clientMac, false);
     }
@@ -55,11 +55,4 @@
     void finishPacket(ByteBuffer buffer) {
         // None needed
     }
-
-    /**
-     * Informs the state machine of the arrival of a DECLINE packet.
-     */
-    public void doNextOp(DhcpStateMachine machine) {
-        machine.onDeclineReceived(mClientMac, mRequestedIp);
-    }
 }
diff --git a/core/java/android/net/dhcp/DhcpDiscoverPacket.java b/services/net/java/android/net/dhcp/DhcpDiscoverPacket.java
similarity index 73%
rename from core/java/android/net/dhcp/DhcpDiscoverPacket.java
rename to services/net/java/android/net/dhcp/DhcpDiscoverPacket.java
index 0e2d39b..a031080 100644
--- a/core/java/android/net/dhcp/DhcpDiscoverPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpDiscoverPacket.java
@@ -16,7 +16,6 @@
 
 package android.net.dhcp;
 
-import java.net.InetAddress;
 import java.net.Inet4Address;
 import java.nio.ByteBuffer;
 
@@ -28,8 +27,7 @@
      * Generates a DISCOVER packet with the specified parameters.
      */
     DhcpDiscoverPacket(int transId, byte[] clientMac, boolean broadcast) {
-        super(transId, Inet4Address.ANY, Inet4Address.ANY, Inet4Address.ANY,
-            Inet4Address.ANY, clientMac, broadcast);
+        super(transId, INADDR_ANY, INADDR_ANY, INADDR_ANY, INADDR_ANY, clientMac, broadcast);
     }
 
     public String toString() {
@@ -43,10 +41,8 @@
      */
     public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
         ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
-        InetAddress destIp = Inet4Address.ALL;
-
-        fillInPacket(encap, Inet4Address.ALL, Inet4Address.ANY, destUdp, srcUdp,
-            result, DHCP_BOOTREQUEST, true);
+        fillInPacket(encap, INADDR_BROADCAST, INADDR_ANY, destUdp,
+                srcUdp, result, DHCP_BOOTREQUEST, mBroadcast);
         result.flip();
         return result;
     }
@@ -56,16 +52,8 @@
      */
     void finishPacket(ByteBuffer buffer) {
         addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_DISCOVER);
+        addCommonClientTlvs(buffer);
         addTlv(buffer, DHCP_PARAMETER_LIST, mRequestedParams);
         addTlvEnd(buffer);
     }
-
-    /**
-     * Informs the state machine of the arrival of a DISCOVER packet.
-     */
-    public void doNextOp(DhcpStateMachine machine) {
-        // currently omitted: host name
-        machine.onDiscoverReceived(mBroadcast, mTransId, mClientMac,
-            mRequestedParams);
-    }
 }
diff --git a/core/java/android/net/dhcp/DhcpInformPacket.java b/services/net/java/android/net/dhcp/DhcpInformPacket.java
similarity index 77%
rename from core/java/android/net/dhcp/DhcpInformPacket.java
rename to services/net/java/android/net/dhcp/DhcpInformPacket.java
index da73216..8bc7cdd 100644
--- a/core/java/android/net/dhcp/DhcpInformPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpInformPacket.java
@@ -16,7 +16,7 @@
 
 package android.net.dhcp;
 
-import java.net.InetAddress;
+import java.net.Inet4Address;
 import java.nio.ByteBuffer;
 
 /**
@@ -26,8 +26,8 @@
     /**
      * Generates an INFORM packet with the specified parameters.
      */
-    DhcpInformPacket(int transId, InetAddress clientIp, InetAddress yourIp,
-                     InetAddress nextIp, InetAddress relayIp,
+    DhcpInformPacket(int transId, Inet4Address clientIp, Inet4Address yourIp,
+                     Inet4Address nextIp, Inet4Address relayIp,
                      byte[] clientMac) {
         super(transId, clientIp, yourIp, nextIp, relayIp, clientMac, false);
     }
@@ -62,15 +62,4 @@
         addTlv(buffer, DHCP_PARAMETER_LIST, mRequestedParams);
         addTlvEnd(buffer);
     }
-
-    /**
-     * Informs the state machine of the arrival of an INFORM packet.  Not
-     * used currently.
-     */
-    public void doNextOp(DhcpStateMachine machine) {
-        InetAddress clientRequest =
-            mRequestedIp == null ? mClientIp : mRequestedIp;
-        machine.onInformReceived(mTransId, mClientMac, clientRequest,
-            mRequestedParams);
-    }
 }
diff --git a/core/java/android/net/dhcp/DhcpNakPacket.java b/services/net/java/android/net/dhcp/DhcpNakPacket.java
similarity index 78%
rename from core/java/android/net/dhcp/DhcpNakPacket.java
rename to services/net/java/android/net/dhcp/DhcpNakPacket.java
index 1f340ad..1390ea7 100644
--- a/core/java/android/net/dhcp/DhcpNakPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpNakPacket.java
@@ -16,7 +16,6 @@
 
 package android.net.dhcp;
 
-import java.net.InetAddress;
 import java.net.Inet4Address;
 import java.nio.ByteBuffer;
 
@@ -27,10 +26,10 @@
     /**
      * Generates a NAK packet with the specified parameters.
      */
-    DhcpNakPacket(int transId, InetAddress clientIp, InetAddress yourIp,
-                  InetAddress nextIp, InetAddress relayIp,
+    DhcpNakPacket(int transId, Inet4Address clientIp, Inet4Address yourIp,
+                  Inet4Address nextIp, Inet4Address relayIp,
                   byte[] clientMac) {
-        super(transId, Inet4Address.ANY, Inet4Address.ANY, nextIp, relayIp,
+        super(transId, INADDR_ANY, INADDR_ANY, nextIp, relayIp,
             clientMac, false);
     }
 
@@ -44,8 +43,8 @@
      */
     public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
         ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
-        InetAddress destIp = mClientIp;
-        InetAddress srcIp = mYourIp;
+        Inet4Address destIp = mClientIp;
+        Inet4Address srcIp = mYourIp;
 
         fillInPacket(encap, destIp, srcIp, destUdp, srcUdp, result,
             DHCP_BOOTREPLY, mBroadcast);
@@ -62,11 +61,4 @@
         addTlv(buffer, DHCP_MESSAGE, mMessage);
         addTlvEnd(buffer);
     }
-
-    /**
-     * Notifies the specified state machine of the newly-arrived NAK packet.
-     */
-    public void doNextOp(DhcpStateMachine machine) {
-        machine.onNakReceived();
-    }
 }
diff --git a/core/java/android/net/dhcp/DhcpOfferPacket.java b/services/net/java/android/net/dhcp/DhcpOfferPacket.java
similarity index 77%
rename from core/java/android/net/dhcp/DhcpOfferPacket.java
rename to services/net/java/android/net/dhcp/DhcpOfferPacket.java
index f1c30e1..b1f3bbd 100644
--- a/core/java/android/net/dhcp/DhcpOfferPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpOfferPacket.java
@@ -16,7 +16,6 @@
 
 package android.net.dhcp;
 
-import java.net.InetAddress;
 import java.net.Inet4Address;
 import java.nio.ByteBuffer;
 
@@ -27,15 +26,14 @@
     /**
      * The IP address of the server which sent this packet.
      */
-    private final InetAddress mSrcIp;
+    private final Inet4Address mSrcIp;
 
     /**
      * Generates a OFFER packet with the specified parameters.
      */
-    DhcpOfferPacket(int transId, boolean broadcast, InetAddress serverAddress,
-                    InetAddress clientIp, byte[] clientMac) {
-        super(transId, Inet4Address.ANY, clientIp, Inet4Address.ANY,
-            Inet4Address.ANY, clientMac, broadcast);
+    DhcpOfferPacket(int transId, boolean broadcast, Inet4Address serverAddress,
+                    Inet4Address clientIp, byte[] clientMac) {
+        super(transId, INADDR_ANY, clientIp, INADDR_ANY, INADDR_ANY, clientMac, broadcast);
         mSrcIp = serverAddress;
     }
 
@@ -44,7 +42,7 @@
         String dnsServers = ", DNS servers: ";
 
         if (mDnsServers != null) {
-            for (InetAddress dnsServer: mDnsServers) {
+            for (Inet4Address dnsServer: mDnsServers) {
                 dnsServers += dnsServer + " ";
             }
         }
@@ -59,8 +57,8 @@
      */
     public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
         ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
-        InetAddress destIp = mBroadcast ? Inet4Address.ALL : mYourIp;
-        InetAddress srcIp = mBroadcast ? Inet4Address.ANY : mSrcIp;
+        Inet4Address destIp = mBroadcast ? INADDR_BROADCAST : mYourIp;
+        Inet4Address srcIp = mBroadcast ? INADDR_ANY : mSrcIp;
 
         fillInPacket(encap, destIp, srcIp, destUdp, srcUdp, result,
             DHCP_BOOTREPLY, mBroadcast);
@@ -89,12 +87,4 @@
         addTlv(buffer, DHCP_DNS_SERVER, mDnsServers);
         addTlvEnd(buffer);
     }
-
-    /**
-     * Notifies the state machine of the OFFER packet parameters.
-     */
-    public void doNextOp(DhcpStateMachine machine) {
-        machine.onOfferReceived(mBroadcast, mTransId, mClientMac, mYourIp,
-            mServerIdentifier);
-    }
 }
diff --git a/services/net/java/android/net/dhcp/DhcpPacket.java b/services/net/java/android/net/dhcp/DhcpPacket.java
new file mode 100644
index 0000000..d41629d
--- /dev/null
+++ b/services/net/java/android/net/dhcp/DhcpPacket.java
@@ -0,0 +1,1077 @@
+package android.net.dhcp;
+
+import android.net.DhcpResults;
+import android.net.LinkAddress;
+import android.net.NetworkUtils;
+import android.os.Build;
+import android.os.SystemProperties;
+import android.system.OsConstants;
+
+import java.io.UnsupportedEncodingException;
+import java.net.Inet4Address;
+import java.net.UnknownHostException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.charset.StandardCharsets;
+import java.nio.ShortBuffer;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Defines basic data and operations needed to build and use packets for the
+ * DHCP protocol.  Subclasses create the specific packets used at each
+ * stage of the negotiation.
+ */
+abstract class DhcpPacket {
+    protected static final String TAG = "DhcpPacket";
+
+    public static final Inet4Address INADDR_ANY = (Inet4Address) Inet4Address.ANY;
+    public static final Inet4Address INADDR_BROADCAST = (Inet4Address) Inet4Address.ALL;
+    public static final byte[] ETHER_BROADCAST = new byte[] {
+            (byte) 0xff, (byte) 0xff, (byte) 0xff,
+            (byte) 0xff, (byte) 0xff, (byte) 0xff,
+    };
+
+    /**
+     * Packet encapsulations.
+     */
+    public static final int ENCAP_L2 = 0;    // EthernetII header included
+    public static final int ENCAP_L3 = 1;    // IP/UDP header included
+    public static final int ENCAP_BOOTP = 2; // BOOTP contents only
+
+    /**
+     * Minimum length of a DHCP packet, excluding options, in the above encapsulations.
+     */
+    public static final int MIN_PACKET_LENGTH_BOOTP = 236;  // See diagram in RFC 2131, section 2.
+    public static final int MIN_PACKET_LENGTH_L3 = MIN_PACKET_LENGTH_BOOTP + 20 + 8;
+    public static final int MIN_PACKET_LENGTH_L2 = MIN_PACKET_LENGTH_L3 + 14;
+
+    public static final int MAX_OPTION_LEN = 255;
+    /**
+     * IP layer definitions.
+     */
+    private static final byte IP_TYPE_UDP = (byte) 0x11;
+
+    /**
+     * IP: Version 4, Header Length 20 bytes
+     */
+    private static final byte IP_VERSION_HEADER_LEN = (byte) 0x45;
+
+    /**
+     * IP: Flags 0, Fragment Offset 0, Don't Fragment
+     */
+    private static final short IP_FLAGS_OFFSET = (short) 0x4000;
+
+    /**
+     * IP: TOS
+     */
+    private static final byte IP_TOS_LOWDELAY = (byte) 0x10;
+
+    /**
+     * IP: TTL -- use default 64 from RFC1340
+     */
+    private static final byte IP_TTL = (byte) 0x40;
+
+    /**
+     * The client DHCP port.
+     */
+    static final short DHCP_CLIENT = (short) 68;
+
+    /**
+     * The server DHCP port.
+     */
+    static final short DHCP_SERVER = (short) 67;
+
+    /**
+     * The message op code indicating a request from a client.
+     */
+    protected static final byte DHCP_BOOTREQUEST = (byte) 1;
+
+    /**
+     * The message op code indicating a response from the server.
+     */
+    protected static final byte DHCP_BOOTREPLY = (byte) 2;
+
+    /**
+     * The code type used to identify an Ethernet MAC address in the
+     * Client-ID field.
+     */
+    protected static final byte CLIENT_ID_ETHER = (byte) 1;
+
+    /**
+     * The maximum length of a packet that can be constructed.
+     */
+    protected static final int MAX_LENGTH = 1500;
+
+    /**
+     * DHCP Optional Type: DHCP Subnet Mask
+     */
+    protected static final byte DHCP_SUBNET_MASK = 1;
+    protected Inet4Address mSubnetMask;
+
+    /**
+     * DHCP Optional Type: DHCP Router
+     */
+    protected static final byte DHCP_ROUTER = 3;
+    protected Inet4Address mGateway;
+
+    /**
+     * DHCP Optional Type: DHCP DNS Server
+     */
+    protected static final byte DHCP_DNS_SERVER = 6;
+    protected List<Inet4Address> mDnsServers;
+
+    /**
+     * DHCP Optional Type: DHCP Host Name
+     */
+    protected static final byte DHCP_HOST_NAME = 12;
+    protected String mHostName;
+
+    /**
+     * DHCP Optional Type: DHCP DOMAIN NAME
+     */
+    protected static final byte DHCP_DOMAIN_NAME = 15;
+    protected String mDomainName;
+
+    /**
+     * DHCP Optional Type: DHCP Interface MTU
+     */
+    protected static final byte DHCP_MTU = 26;
+    protected Short mMtu;
+
+    /**
+     * DHCP Optional Type: DHCP BROADCAST ADDRESS
+     */
+    protected static final byte DHCP_BROADCAST_ADDRESS = 28;
+    protected Inet4Address mBroadcastAddress;
+
+    /**
+     * DHCP Optional Type: DHCP Requested IP Address
+     */
+    protected static final byte DHCP_REQUESTED_IP = 50;
+    protected Inet4Address mRequestedIp;
+
+    /**
+     * DHCP Optional Type: DHCP Lease Time
+     */
+    protected static final byte DHCP_LEASE_TIME = 51;
+    protected Integer mLeaseTime;
+
+    /**
+     * DHCP Optional Type: DHCP Message Type
+     */
+    protected static final byte DHCP_MESSAGE_TYPE = 53;
+    // the actual type values
+    protected static final byte DHCP_MESSAGE_TYPE_DISCOVER = 1;
+    protected static final byte DHCP_MESSAGE_TYPE_OFFER = 2;
+    protected static final byte DHCP_MESSAGE_TYPE_REQUEST = 3;
+    protected static final byte DHCP_MESSAGE_TYPE_DECLINE = 4;
+    protected static final byte DHCP_MESSAGE_TYPE_ACK = 5;
+    protected static final byte DHCP_MESSAGE_TYPE_NAK = 6;
+    protected static final byte DHCP_MESSAGE_TYPE_INFORM = 8;
+
+    /**
+     * DHCP Optional Type: DHCP Server Identifier
+     */
+    protected static final byte DHCP_SERVER_IDENTIFIER = 54;
+    protected Inet4Address mServerIdentifier;
+
+    /**
+     * DHCP Optional Type: DHCP Parameter List
+     */
+    protected static final byte DHCP_PARAMETER_LIST = 55;
+    protected byte[] mRequestedParams;
+
+    /**
+     * DHCP Optional Type: DHCP MESSAGE
+     */
+    protected static final byte DHCP_MESSAGE = 56;
+    protected String mMessage;
+
+    /**
+     * DHCP Optional Type: Maximum DHCP Message Size
+     */
+    protected static final byte DHCP_MAX_MESSAGE_SIZE = 57;
+    protected Short mMaxMessageSize;
+
+    /**
+     * DHCP Optional Type: DHCP Renewal Time Value
+     */
+    protected static final byte DHCP_RENEWAL_TIME = 58;
+    protected Integer mT1;
+
+    /**
+     * DHCP Optional Type: Rebinding Time Value
+     */
+    protected static final byte DHCP_REBINDING_TIME = 59;
+    protected Integer mT2;
+
+    /**
+     * DHCP Optional Type: Vendor Class Identifier
+     */
+    protected static final byte DHCP_VENDOR_CLASS_ID = 60;
+    protected String mVendorId;
+
+    /**
+     * DHCP Optional Type: DHCP Client Identifier
+     */
+    protected static final byte DHCP_CLIENT_IDENTIFIER = 61;
+
+    /**
+     * The transaction identifier used in this particular DHCP negotiation
+     */
+    protected final int mTransId;
+
+    /**
+     * The IP address of the client host.  This address is typically
+     * proposed by the client (from an earlier DHCP negotiation) or
+     * supplied by the server.
+     */
+    protected final Inet4Address mClientIp;
+    protected final Inet4Address mYourIp;
+    private final Inet4Address mNextIp;
+    private final Inet4Address mRelayIp;
+
+    /**
+     * Does the client request a broadcast response?
+     */
+    protected boolean mBroadcast;
+
+    /**
+     * The six-octet MAC of the client.
+     */
+    protected final byte[] mClientMac;
+
+    /**
+     * Asks the packet object to create a ByteBuffer serialization of
+     * the packet for transmission.
+     */
+    public abstract ByteBuffer buildPacket(int encap, short destUdp,
+        short srcUdp);
+
+    /**
+     * Allows the concrete class to fill in packet-type-specific details,
+     * typically optional parameters at the end of the packet.
+     */
+    abstract void finishPacket(ByteBuffer buffer);
+
+    protected DhcpPacket(int transId, Inet4Address clientIp, Inet4Address yourIp,
+                         Inet4Address nextIp, Inet4Address relayIp,
+                         byte[] clientMac, boolean broadcast) {
+        mTransId = transId;
+        mClientIp = clientIp;
+        mYourIp = yourIp;
+        mNextIp = nextIp;
+        mRelayIp = relayIp;
+        mClientMac = clientMac;
+        mBroadcast = broadcast;
+    }
+
+    /**
+     * Returns the transaction ID.
+     */
+    public int getTransactionId() {
+        return mTransId;
+    }
+
+    /**
+     * Returns the client MAC.
+     */
+    public byte[] getClientMac() {
+        return mClientMac;
+    }
+
+    /**
+     * Creates a new L3 packet (including IP header) containing the
+     * DHCP udp packet.  This method relies upon the delegated method
+     * finishPacket() to insert the per-packet contents.
+     */
+    protected void fillInPacket(int encap, Inet4Address destIp,
+        Inet4Address srcIp, short destUdp, short srcUdp, ByteBuffer buf,
+        byte requestCode, boolean broadcast) {
+        byte[] destIpArray = destIp.getAddress();
+        byte[] srcIpArray = srcIp.getAddress();
+        int ipHeaderOffset = 0;
+        int ipLengthOffset = 0;
+        int ipChecksumOffset = 0;
+        int endIpHeader = 0;
+        int udpHeaderOffset = 0;
+        int udpLengthOffset = 0;
+        int udpChecksumOffset = 0;
+
+        buf.clear();
+        buf.order(ByteOrder.BIG_ENDIAN);
+
+        if (encap == ENCAP_L2) {
+            buf.put(ETHER_BROADCAST);
+            buf.put(mClientMac);
+            buf.putShort((short) OsConstants.ETH_P_IP);
+        }
+
+        // if a full IP packet needs to be generated, put the IP & UDP
+        // headers in place, and pre-populate with artificial values
+        // needed to seed the IP checksum.
+        if (encap <= ENCAP_L3) {
+            ipHeaderOffset = buf.position();
+            buf.put(IP_VERSION_HEADER_LEN);
+            buf.put(IP_TOS_LOWDELAY);    // tos: IPTOS_LOWDELAY
+            ipLengthOffset = buf.position();
+            buf.putShort((short)0);  // length
+            buf.putShort((short)0);  // id
+            buf.putShort(IP_FLAGS_OFFSET); // ip offset: don't fragment
+            buf.put(IP_TTL);    // TTL: use default 64 from RFC1340
+            buf.put(IP_TYPE_UDP);
+            ipChecksumOffset = buf.position();
+            buf.putShort((short) 0); // checksum
+
+            buf.put(srcIpArray);
+            buf.put(destIpArray);
+            endIpHeader = buf.position();
+
+            // UDP header
+            udpHeaderOffset = buf.position();
+            buf.putShort(srcUdp);
+            buf.putShort(destUdp);
+            udpLengthOffset = buf.position();
+            buf.putShort((short) 0); // length
+            udpChecksumOffset = buf.position();
+            buf.putShort((short) 0); // UDP checksum -- initially zero
+        }
+
+        // DHCP payload
+        buf.put(requestCode);
+        buf.put((byte) 1); // Hardware Type: Ethernet
+        buf.put((byte) mClientMac.length); // Hardware Address Length
+        buf.put((byte) 0); // Hop Count
+        buf.putInt(mTransId);  // Transaction ID
+        buf.putShort((short) 0); // Elapsed Seconds
+
+        if (broadcast) {
+            buf.putShort((short) 0x8000); // Flags
+        } else {
+            buf.putShort((short) 0x0000); // Flags
+        }
+
+        buf.put(mClientIp.getAddress());
+        buf.put(mYourIp.getAddress());
+        buf.put(mNextIp.getAddress());
+        buf.put(mRelayIp.getAddress());
+        buf.put(mClientMac);
+        buf.position(buf.position() +
+                     (16 - mClientMac.length) // pad addr to 16 bytes
+                     + 64     // empty server host name (64 bytes)
+                     + 128);  // empty boot file name (128 bytes)
+        buf.putInt(0x63825363); // magic number
+        finishPacket(buf);
+
+        // round up to an even number of octets
+        if ((buf.position() & 1) == 1) {
+            buf.put((byte) 0);
+        }
+
+        // If an IP packet is being built, the IP & UDP checksums must be
+        // computed.
+        if (encap <= ENCAP_L3) {
+            // fix UDP header: insert length
+            short udpLen = (short)(buf.position() - udpHeaderOffset);
+            buf.putShort(udpLengthOffset, udpLen);
+            // fix UDP header: checksum
+            // checksum for UDP at udpChecksumOffset
+            int udpSeed = 0;
+
+            // apply IPv4 pseudo-header.  Read IP address src and destination
+            // values from the IP header and accumulate checksum.
+            udpSeed += intAbs(buf.getShort(ipChecksumOffset + 2));
+            udpSeed += intAbs(buf.getShort(ipChecksumOffset + 4));
+            udpSeed += intAbs(buf.getShort(ipChecksumOffset + 6));
+            udpSeed += intAbs(buf.getShort(ipChecksumOffset + 8));
+
+            // accumulate extra data for the pseudo-header
+            udpSeed += IP_TYPE_UDP;
+            udpSeed += udpLen;
+            // and compute UDP checksum
+            buf.putShort(udpChecksumOffset, (short) checksum(buf, udpSeed,
+                                                             udpHeaderOffset,
+                                                             buf.position()));
+            // fix IP header: insert length
+            buf.putShort(ipLengthOffset, (short)(buf.position() - ipHeaderOffset));
+            // fixup IP-header checksum
+            buf.putShort(ipChecksumOffset,
+                         (short) checksum(buf, 0, ipHeaderOffset, endIpHeader));
+        }
+    }
+
+    /**
+     * Converts a signed short value to an unsigned int value.  Needed
+     * because Java does not have unsigned types.
+     */
+    private static int intAbs(short v) {
+        return v & 0xFFFF;
+    }
+
+    /**
+     * Performs an IP checksum (used in IP header and across UDP
+     * payload) on the specified portion of a ByteBuffer.  The seed
+     * allows the checksum to commence with a specified value.
+     */
+    private int checksum(ByteBuffer buf, int seed, int start, int end) {
+        int sum = seed;
+        int bufPosition = buf.position();
+
+        // set position of original ByteBuffer, so that the ShortBuffer
+        // will be correctly initialized
+        buf.position(start);
+        ShortBuffer shortBuf = buf.asShortBuffer();
+
+        // re-set ByteBuffer position
+        buf.position(bufPosition);
+
+        short[] shortArray = new short[(end - start) / 2];
+        shortBuf.get(shortArray);
+
+        for (short s : shortArray) {
+            sum += intAbs(s);
+        }
+
+        start += shortArray.length * 2;
+
+        // see if a singleton byte remains
+        if (end != start) {
+            short b = buf.get(start);
+
+            // make it unsigned
+            if (b < 0) {
+                b += 256;
+            }
+
+            sum += b * 256;
+        }
+
+        sum = ((sum >> 16) & 0xFFFF) + (sum & 0xFFFF);
+        sum = ((sum + ((sum >> 16) & 0xFFFF)) & 0xFFFF);
+        int negated = ~sum;
+        return intAbs((short) negated);
+    }
+
+    /**
+     * Adds an optional parameter containing a single byte value.
+     */
+    protected static void addTlv(ByteBuffer buf, byte type, byte value) {
+        buf.put(type);
+        buf.put((byte) 1);
+        buf.put(value);
+    }
+
+    /**
+     * Adds an optional parameter containing an array of bytes.
+     */
+    protected static void addTlv(ByteBuffer buf, byte type, byte[] payload) {
+        if (payload != null) {
+            if (payload.length > MAX_OPTION_LEN) {
+                throw new IllegalArgumentException("DHCP option too long: "
+                        + payload.length + " vs. " + MAX_OPTION_LEN);
+            }
+            buf.put(type);
+            buf.put((byte) payload.length);
+            buf.put(payload);
+        }
+    }
+
+    /**
+     * Adds an optional parameter containing an IP address.
+     */
+    protected static void addTlv(ByteBuffer buf, byte type, Inet4Address addr) {
+        if (addr != null) {
+            addTlv(buf, type, addr.getAddress());
+        }
+    }
+
+    /**
+     * Adds an optional parameter containing a list of IP addresses.
+     */
+    protected static void addTlv(ByteBuffer buf, byte type, List<Inet4Address> addrs) {
+        if (addrs == null || addrs.size() == 0) return;
+
+        int optionLen = 4 * addrs.size();
+        if (optionLen > MAX_OPTION_LEN) {
+            throw new IllegalArgumentException("DHCP option too long: "
+                    + optionLen + " vs. " + MAX_OPTION_LEN);
+        }
+
+        buf.put(type);
+        buf.put((byte)(optionLen));
+
+        for (Inet4Address addr : addrs) {
+            buf.put(addr.getAddress());
+        }
+    }
+
+    /**
+     * Adds an optional parameter containing a short integer
+     */
+    protected static void addTlv(ByteBuffer buf, byte type, Short value) {
+        if (value != null) {
+            buf.put(type);
+            buf.put((byte) 2);
+            buf.putShort(value.shortValue());
+        }
+    }
+
+    /**
+     * Adds an optional parameter containing a simple integer
+     */
+    protected static void addTlv(ByteBuffer buf, byte type, Integer value) {
+        if (value != null) {
+            buf.put(type);
+            buf.put((byte) 4);
+            buf.putInt(value.intValue());
+        }
+    }
+
+    /**
+     * Adds an optional parameter containing an ASCII string.
+     */
+    protected static void addTlv(ByteBuffer buf, byte type, String str) {
+        try {
+            addTlv(buf, type, str.getBytes("US-ASCII"));
+        } catch (UnsupportedEncodingException e) {
+           throw new IllegalArgumentException("String is not US-ASCII: " + str);
+        }
+    }
+
+    /**
+     * Adds the special end-of-optional-parameters indicator.
+     */
+    protected static void addTlvEnd(ByteBuffer buf) {
+        buf.put((byte) 0xFF);
+    }
+
+    /**
+     * Adds common client TLVs.
+     *
+     * TODO: Does this belong here? The alternative would be to modify all the buildXyzPacket
+     * methods to take them.
+     */
+    protected void addCommonClientTlvs(ByteBuffer buf) {
+        addTlv(buf, DHCP_MAX_MESSAGE_SIZE, (short) MAX_LENGTH);
+        addTlv(buf, DHCP_VENDOR_CLASS_ID, "android-dhcp-" + Build.VERSION.RELEASE);
+        addTlv(buf, DHCP_HOST_NAME, SystemProperties.get("net.hostname"));
+    }
+
+    /**
+     * Converts a MAC from an array of octets to an ASCII string.
+     */
+    public static String macToString(byte[] mac) {
+        String macAddr = "";
+
+        for (int i = 0; i < mac.length; i++) {
+            String hexString = "0" + Integer.toHexString(mac[i]);
+
+            // substring operation grabs the last 2 digits: this
+            // allows signed bytes to be converted correctly.
+            macAddr += hexString.substring(hexString.length() - 2);
+
+            if (i != (mac.length - 1)) {
+                macAddr += ":";
+            }
+        }
+
+        return macAddr;
+    }
+
+    public String toString() {
+        String macAddr = macToString(mClientMac);
+
+        return macAddr;
+    }
+
+    /**
+     * Reads a four-octet value from a ByteBuffer and construct
+     * an IPv4 address from that value.
+     */
+    private static Inet4Address readIpAddress(ByteBuffer packet) {
+        Inet4Address result = null;
+        byte[] ipAddr = new byte[4];
+        packet.get(ipAddr);
+
+        try {
+            result = (Inet4Address) Inet4Address.getByAddress(ipAddr);
+        } catch (UnknownHostException ex) {
+            // ipAddr is numeric, so this should not be
+            // triggered.  However, if it is, just nullify
+            result = null;
+        }
+
+        return result;
+    }
+
+    /**
+     * Reads a string of specified length from the buffer.
+     */
+    private static String readAsciiString(ByteBuffer buf, int byteCount, boolean nullOk) {
+        byte[] bytes = new byte[byteCount];
+        buf.get(bytes);
+        int length = bytes.length;
+        if (!nullOk) {
+            // Stop at the first null byte. This is because some DHCP options (e.g., the domain
+            // name) are passed to netd via FrameworkListener, which refuses arguments containing
+            // null bytes. We don't do this by default because vendorInfo is an opaque string which
+            // could in theory contain null bytes.
+            for (length = 0; length < bytes.length; length++) {
+                if (bytes[length] == 0) {
+                    break;
+                }
+            }
+        }
+        return new String(bytes, 0, length, StandardCharsets.US_ASCII);
+    }
+
+    /**
+     * Creates a concrete DhcpPacket from the supplied ByteBuffer.  The
+     * buffer may have an L2 encapsulation (which is the full EthernetII
+     * format starting with the source-address MAC) or an L3 encapsulation
+     * (which starts with the IP header).
+     * <br>
+     * A subset of the optional parameters are parsed and are stored
+     * in object fields.
+     */
+    public static DhcpPacket decodeFullPacket(ByteBuffer packet, int pktType)
+    {
+        // bootp parameters
+        int transactionId;
+        Inet4Address clientIp;
+        Inet4Address yourIp;
+        Inet4Address nextIp;
+        Inet4Address relayIp;
+        byte[] clientMac;
+        List<Inet4Address> dnsServers = new ArrayList<Inet4Address>();
+        Inet4Address gateway = null; // aka router
+        Inet4Address serverIdentifier = null;
+        Inet4Address netMask = null;
+        String message = null;
+        String vendorId = null;
+        byte[] expectedParams = null;
+        String hostName = null;
+        String domainName = null;
+        Inet4Address ipSrc = null;
+        Inet4Address ipDst = null;
+        Inet4Address bcAddr = null;
+        Inet4Address requestedIp = null;
+
+        // The following are all unsigned integers. Internally we store them as signed integers of
+        // the same length because that way we're guaranteed that they can't be out of the range of
+        // the unsigned field in the packet. Callers wanting to pass in an unsigned value will need
+        // to cast it.
+        Short mtu = null;
+        Short maxMessageSize = null;
+        Integer leaseTime = null;
+        Integer T1 = null;
+        Integer T2 = null;
+
+        // dhcp options
+        byte dhcpType = (byte) 0xFF;
+
+        packet.order(ByteOrder.BIG_ENDIAN);
+
+        // check to see if we need to parse L2, IP, and UDP encaps
+        if (pktType == ENCAP_L2) {
+            if (packet.remaining() < MIN_PACKET_LENGTH_L2) {
+                return null;
+            }
+
+            byte[] l2dst = new byte[6];
+            byte[] l2src = new byte[6];
+
+            packet.get(l2dst);
+            packet.get(l2src);
+
+            short l2type = packet.getShort();
+
+            if (l2type != OsConstants.ETH_P_IP)
+                return null;
+        }
+
+        if (pktType <= ENCAP_L3) {
+            if (packet.remaining() < MIN_PACKET_LENGTH_L3) {
+                return null;
+            }
+
+            byte ipTypeAndLength = packet.get();
+            int ipVersion = (ipTypeAndLength & 0xf0) >> 4;
+            if (ipVersion != 4) {
+                return null;
+            }
+
+            // System.out.println("ipType is " + ipType);
+            byte ipDiffServicesField = packet.get();
+            short ipTotalLength = packet.getShort();
+            short ipIdentification = packet.getShort();
+            byte ipFlags = packet.get();
+            byte ipFragOffset = packet.get();
+            byte ipTTL = packet.get();
+            byte ipProto = packet.get();
+            short ipChksm = packet.getShort();
+
+            ipSrc = readIpAddress(packet);
+            ipDst = readIpAddress(packet);
+
+            if (ipProto != IP_TYPE_UDP) // UDP
+                return null;
+
+            // Skip options. This cannot cause us to read beyond the end of the buffer because the
+            // IPv4 header cannot be more than (0x0f * 4) = 60 bytes long, and that is less than
+            // MIN_PACKET_LENGTH_L3.
+            int optionWords = ((ipTypeAndLength & 0x0f) - 5);
+            for (int i = 0; i < optionWords; i++) {
+                packet.getInt();
+            }
+
+            // assume UDP
+            short udpSrcPort = packet.getShort();
+            short udpDstPort = packet.getShort();
+            short udpLen = packet.getShort();
+            short udpChkSum = packet.getShort();
+
+            if ((udpSrcPort != DHCP_SERVER) && (udpSrcPort != DHCP_CLIENT))
+                return null;
+        }
+
+        // We need to check the length even for ENCAP_L3 because the IPv4 header is variable-length.
+        if (pktType > ENCAP_BOOTP || packet.remaining() < MIN_PACKET_LENGTH_BOOTP) {
+            return null;
+        }
+
+        byte type = packet.get();
+        byte hwType = packet.get();
+        byte addrLen = packet.get();
+        byte hops = packet.get();
+        transactionId = packet.getInt();
+        short elapsed = packet.getShort();
+        short bootpFlags = packet.getShort();
+        boolean broadcast = (bootpFlags & 0x8000) != 0;
+        byte[] ipv4addr = new byte[4];
+
+        try {
+            packet.get(ipv4addr);
+            clientIp = (Inet4Address) Inet4Address.getByAddress(ipv4addr);
+            packet.get(ipv4addr);
+            yourIp = (Inet4Address) Inet4Address.getByAddress(ipv4addr);
+            packet.get(ipv4addr);
+            nextIp = (Inet4Address) Inet4Address.getByAddress(ipv4addr);
+            packet.get(ipv4addr);
+            relayIp = (Inet4Address) Inet4Address.getByAddress(ipv4addr);
+        } catch (UnknownHostException ex) {
+            return null;
+        }
+
+        clientMac = new byte[addrLen];
+        packet.get(clientMac);
+
+        // skip over address padding (16 octets allocated)
+        packet.position(packet.position() + (16 - addrLen)
+                        + 64    // skip server host name (64 chars)
+                        + 128); // skip boot file name (128 chars)
+
+        int dhcpMagicCookie = packet.getInt();
+
+        if (dhcpMagicCookie !=  0x63825363)
+            return null;
+
+        // parse options
+        boolean notFinishedOptions = true;
+
+        while ((packet.position() < packet.limit()) && notFinishedOptions) {
+            try {
+                byte optionType = packet.get();
+
+                if (optionType == (byte) 0xFF) {
+                    notFinishedOptions = false;
+                } else {
+                    int optionLen = packet.get() & 0xFF;
+                    int expectedLen = 0;
+
+                    switch(optionType) {
+                        case DHCP_SUBNET_MASK:
+                            netMask = readIpAddress(packet);
+                            expectedLen = 4;
+                            break;
+                        case DHCP_ROUTER:
+                            gateway = readIpAddress(packet);
+                            expectedLen = 4;
+                            break;
+                        case DHCP_DNS_SERVER:
+                            for (expectedLen = 0; expectedLen < optionLen; expectedLen += 4) {
+                                dnsServers.add(readIpAddress(packet));
+                            }
+                            break;
+                        case DHCP_HOST_NAME:
+                            expectedLen = optionLen;
+                            hostName = readAsciiString(packet, optionLen, false);
+                            break;
+                        case DHCP_MTU:
+                            expectedLen = 2;
+                            mtu = Short.valueOf(packet.getShort());
+                            break;
+                        case DHCP_DOMAIN_NAME:
+                            expectedLen = optionLen;
+                            domainName = readAsciiString(packet, optionLen, false);
+                            break;
+                        case DHCP_BROADCAST_ADDRESS:
+                            bcAddr = readIpAddress(packet);
+                            expectedLen = 4;
+                            break;
+                        case DHCP_REQUESTED_IP:
+                            requestedIp = readIpAddress(packet);
+                            expectedLen = 4;
+                            break;
+                        case DHCP_LEASE_TIME:
+                            leaseTime = Integer.valueOf(packet.getInt());
+                            expectedLen = 4;
+                            break;
+                        case DHCP_MESSAGE_TYPE:
+                            dhcpType = packet.get();
+                            expectedLen = 1;
+                            break;
+                        case DHCP_SERVER_IDENTIFIER:
+                            serverIdentifier = readIpAddress(packet);
+                            expectedLen = 4;
+                            break;
+                        case DHCP_PARAMETER_LIST:
+                            expectedParams = new byte[optionLen];
+                            packet.get(expectedParams);
+                            expectedLen = optionLen;
+                            break;
+                        case DHCP_MESSAGE:
+                            expectedLen = optionLen;
+                            message = readAsciiString(packet, optionLen, false);
+                            break;
+                        case DHCP_MAX_MESSAGE_SIZE:
+                            expectedLen = 2;
+                            maxMessageSize = Short.valueOf(packet.getShort());
+                            break;
+                        case DHCP_RENEWAL_TIME:
+                            expectedLen = 4;
+                            T1 = Integer.valueOf(packet.getInt());
+                            break;
+                        case DHCP_REBINDING_TIME:
+                            expectedLen = 4;
+                            T2 = Integer.valueOf(packet.getInt());
+                            break;
+                        case DHCP_VENDOR_CLASS_ID:
+                            expectedLen = optionLen;
+                            vendorId = readAsciiString(packet, optionLen, true);
+                            break;
+                        case DHCP_CLIENT_IDENTIFIER: { // Client identifier
+                            byte[] id = new byte[optionLen];
+                            packet.get(id);
+                            expectedLen = optionLen;
+                        } break;
+                        default:
+                            // ignore any other parameters
+                            for (int i = 0; i < optionLen; i++) {
+                                expectedLen++;
+                                byte throwaway = packet.get();
+                            }
+                    }
+
+                    if (expectedLen != optionLen) {
+                        return null;
+                    }
+                }
+            } catch (BufferUnderflowException e) {
+                return null;
+            }
+        }
+
+        DhcpPacket newPacket;
+
+        switch(dhcpType) {
+            case -1: return null;
+            case DHCP_MESSAGE_TYPE_DISCOVER:
+                newPacket = new DhcpDiscoverPacket(
+                    transactionId, clientMac, broadcast);
+                break;
+            case DHCP_MESSAGE_TYPE_OFFER:
+                newPacket = new DhcpOfferPacket(
+                    transactionId, broadcast, ipSrc, yourIp, clientMac);
+                break;
+            case DHCP_MESSAGE_TYPE_REQUEST:
+                newPacket = new DhcpRequestPacket(
+                    transactionId, clientIp, clientMac, broadcast);
+                break;
+            case DHCP_MESSAGE_TYPE_DECLINE:
+                newPacket = new DhcpDeclinePacket(
+                    transactionId, clientIp, yourIp, nextIp, relayIp,
+                    clientMac);
+                break;
+            case DHCP_MESSAGE_TYPE_ACK:
+                newPacket = new DhcpAckPacket(
+                    transactionId, broadcast, ipSrc, yourIp, clientMac);
+                break;
+            case DHCP_MESSAGE_TYPE_NAK:
+                newPacket = new DhcpNakPacket(
+                    transactionId, clientIp, yourIp, nextIp, relayIp,
+                    clientMac);
+                break;
+            case DHCP_MESSAGE_TYPE_INFORM:
+                newPacket = new DhcpInformPacket(
+                    transactionId, clientIp, yourIp, nextIp, relayIp,
+                    clientMac);
+                break;
+            default:
+                System.out.println("Unimplemented type: " + dhcpType);
+                return null;
+        }
+
+        newPacket.mBroadcastAddress = bcAddr;
+        newPacket.mDnsServers = dnsServers;
+        newPacket.mDomainName = domainName;
+        newPacket.mGateway = gateway;
+        newPacket.mHostName = hostName;
+        newPacket.mLeaseTime = leaseTime;
+        newPacket.mMessage = message;
+        newPacket.mMtu = mtu;
+        newPacket.mRequestedIp = requestedIp;
+        newPacket.mRequestedParams = expectedParams;
+        newPacket.mServerIdentifier = serverIdentifier;
+        newPacket.mSubnetMask = netMask;
+        newPacket.mMaxMessageSize = maxMessageSize;
+        newPacket.mT1 = T1;
+        newPacket.mT2 = T2;
+        newPacket.mVendorId = vendorId;
+        return newPacket;
+    }
+
+    /**
+     * Parse a packet from an array of bytes, stopping at the given length.
+     */
+    public static DhcpPacket decodeFullPacket(byte[] packet, int length, int pktType)
+    {
+        ByteBuffer buffer = ByteBuffer.wrap(packet, 0, length).order(ByteOrder.BIG_ENDIAN);
+        return decodeFullPacket(buffer, pktType);
+    }
+
+    /**
+     *  Construct a DhcpResults object from a DHCP reply packet.
+     */
+    public DhcpResults toDhcpResults() {
+        Inet4Address ipAddress = mYourIp;
+        if (ipAddress == Inet4Address.ANY) {
+            ipAddress = mClientIp;
+            if (ipAddress == Inet4Address.ANY) {
+                return null;
+            }
+        }
+
+        int prefixLength;
+        if (mSubnetMask != null) {
+            try {
+                prefixLength = NetworkUtils.netmaskToPrefixLength(mSubnetMask);
+            } catch (IllegalArgumentException e) {
+                // Non-contiguous netmask.
+                return null;
+            }
+        } else {
+            prefixLength = NetworkUtils.getImplicitNetmask(ipAddress);
+        }
+
+        DhcpResults results = new DhcpResults();
+        try {
+            results.ipAddress = new LinkAddress(ipAddress, prefixLength);
+        } catch (IllegalArgumentException e) {
+            return null;
+        }
+        results.gateway = mGateway;
+        results.dnsServers.addAll(mDnsServers);
+        results.domains = mDomainName;
+        results.serverAddress = mServerIdentifier;
+        results.vendorInfo = mVendorId;
+        results.leaseDuration = mLeaseTime;
+        return results;
+    }
+
+    /**
+     * Builds a DHCP-DISCOVER packet from the required specified
+     * parameters.
+     */
+    public static ByteBuffer buildDiscoverPacket(int encap, int transactionId,
+        byte[] clientMac, boolean broadcast, byte[] expectedParams) {
+        DhcpPacket pkt = new DhcpDiscoverPacket(
+            transactionId, clientMac, broadcast);
+        pkt.mRequestedParams = expectedParams;
+        return pkt.buildPacket(encap, DHCP_SERVER, DHCP_CLIENT);
+    }
+
+    /**
+     * Builds a DHCP-OFFER packet from the required specified
+     * parameters.
+     */
+    public static ByteBuffer buildOfferPacket(int encap, int transactionId,
+        boolean broadcast, Inet4Address serverIpAddr, Inet4Address clientIpAddr,
+        byte[] mac, Integer timeout, Inet4Address netMask, Inet4Address bcAddr,
+        Inet4Address gateway, List<Inet4Address> dnsServers,
+        Inet4Address dhcpServerIdentifier, String domainName) {
+        DhcpPacket pkt = new DhcpOfferPacket(
+            transactionId, broadcast, serverIpAddr, clientIpAddr, mac);
+        pkt.mGateway = gateway;
+        pkt.mDnsServers = dnsServers;
+        pkt.mLeaseTime = timeout;
+        pkt.mDomainName = domainName;
+        pkt.mServerIdentifier = dhcpServerIdentifier;
+        pkt.mSubnetMask = netMask;
+        pkt.mBroadcastAddress = bcAddr;
+        return pkt.buildPacket(encap, DHCP_CLIENT, DHCP_SERVER);
+    }
+
+    /**
+     * Builds a DHCP-ACK packet from the required specified parameters.
+     */
+    public static ByteBuffer buildAckPacket(int encap, int transactionId,
+        boolean broadcast, Inet4Address serverIpAddr, Inet4Address clientIpAddr,
+        byte[] mac, Integer timeout, Inet4Address netMask, Inet4Address bcAddr,
+        Inet4Address gateway, List<Inet4Address> dnsServers,
+        Inet4Address dhcpServerIdentifier, String domainName) {
+        DhcpPacket pkt = new DhcpAckPacket(
+            transactionId, broadcast, serverIpAddr, clientIpAddr, mac);
+        pkt.mGateway = gateway;
+        pkt.mDnsServers = dnsServers;
+        pkt.mLeaseTime = timeout;
+        pkt.mDomainName = domainName;
+        pkt.mSubnetMask = netMask;
+        pkt.mServerIdentifier = dhcpServerIdentifier;
+        pkt.mBroadcastAddress = bcAddr;
+        return pkt.buildPacket(encap, DHCP_CLIENT, DHCP_SERVER);
+    }
+
+    /**
+     * Builds a DHCP-NAK packet from the required specified parameters.
+     */
+    public static ByteBuffer buildNakPacket(int encap, int transactionId,
+        Inet4Address serverIpAddr, Inet4Address clientIpAddr, byte[] mac) {
+        DhcpPacket pkt = new DhcpNakPacket(transactionId, clientIpAddr,
+            serverIpAddr, serverIpAddr, serverIpAddr, mac);
+        pkt.mMessage = "requested address not available";
+        pkt.mRequestedIp = clientIpAddr;
+        return pkt.buildPacket(encap, DHCP_CLIENT, DHCP_SERVER);
+    }
+
+    /**
+     * Builds a DHCP-REQUEST packet from the required specified parameters.
+     */
+    public static ByteBuffer buildRequestPacket(int encap,
+        int transactionId, Inet4Address clientIp, boolean broadcast,
+        byte[] clientMac, Inet4Address requestedIpAddress,
+        Inet4Address serverIdentifier, byte[] requestedParams, String hostName) {
+        DhcpPacket pkt = new DhcpRequestPacket(transactionId, clientIp,
+            clientMac, broadcast);
+        pkt.mRequestedIp = requestedIpAddress;
+        pkt.mServerIdentifier = serverIdentifier;
+        pkt.mHostName = hostName;
+        pkt.mRequestedParams = requestedParams;
+        ByteBuffer result = pkt.buildPacket(encap, DHCP_SERVER, DHCP_CLIENT);
+        return result;
+    }
+}
diff --git a/core/java/android/net/dhcp/DhcpRequestPacket.java b/services/net/java/android/net/dhcp/DhcpRequestPacket.java
similarity index 69%
rename from core/java/android/net/dhcp/DhcpRequestPacket.java
rename to services/net/java/android/net/dhcp/DhcpRequestPacket.java
index cf32957..42b7b0c 100644
--- a/core/java/android/net/dhcp/DhcpRequestPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpRequestPacket.java
@@ -18,7 +18,6 @@
 
 import android.util.Log;
 
-import java.net.InetAddress;
 import java.net.Inet4Address;
 import java.nio.ByteBuffer;
 
@@ -29,10 +28,9 @@
     /**
      * Generates a REQUEST packet with the specified parameters.
      */
-    DhcpRequestPacket(int transId, InetAddress clientIp, byte[] clientMac,
+    DhcpRequestPacket(int transId, Inet4Address clientIp, byte[] clientMac,
                       boolean broadcast) {
-        super(transId, clientIp, Inet4Address.ANY, Inet4Address.ANY,
-          Inet4Address.ANY, clientMac, broadcast);
+        super(transId, clientIp, INADDR_ANY, INADDR_ANY, INADDR_ANY, clientMac, broadcast);
     }
 
     public String toString() {
@@ -48,7 +46,7 @@
     public ByteBuffer buildPacket(int encap, short destUdp, short srcUdp) {
         ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
 
-        fillInPacket(encap, Inet4Address.ALL, Inet4Address.ANY, destUdp, srcUdp,
+        fillInPacket(encap, INADDR_BROADCAST, INADDR_ANY, destUdp, srcUdp,
             result, DHCP_BOOTREQUEST, mBroadcast);
         result.flip();
         return result;
@@ -65,22 +63,15 @@
         System.arraycopy(mClientMac, 0, clientId, 1, 6);
 
         addTlv(buffer, DHCP_MESSAGE_TYPE, DHCP_MESSAGE_TYPE_REQUEST);
-        addTlv(buffer, DHCP_PARAMETER_LIST, mRequestedParams);
-        addTlv(buffer, DHCP_REQUESTED_IP, mRequestedIp);
-        addTlv(buffer, DHCP_SERVER_IDENTIFIER, mServerIdentifier);
+        if (!INADDR_ANY.equals(mRequestedIp)) {
+            addTlv(buffer, DHCP_REQUESTED_IP, mRequestedIp);
+        }
+        if (!INADDR_ANY.equals(mServerIdentifier)) {
+            addTlv(buffer, DHCP_SERVER_IDENTIFIER, mServerIdentifier);
+        }
         addTlv(buffer, DHCP_CLIENT_IDENTIFIER, clientId);
+        addCommonClientTlvs(buffer);
+        addTlv(buffer, DHCP_PARAMETER_LIST, mRequestedParams);
         addTlvEnd(buffer);
     }
-
-    /**
-     * Notifies the specified state machine of the REQUEST packet parameters.
-     */
-    public void doNextOp(DhcpStateMachine machine) {
-        InetAddress clientRequest =
-            mRequestedIp == null ? mClientIp : mRequestedIp;
-        Log.v(TAG, "requested IP is " + mRequestedIp + " and client IP is " +
-            mClientIp);
-        machine.onRequestReceived(mBroadcast, mTransId, mClientMac,
-            clientRequest, mRequestedParams, mHostName);
-    }
 }
diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk
index f25fc62..33979b1 100644
--- a/services/tests/servicestests/Android.mk
+++ b/services/tests/servicestests/Android.mk
@@ -10,6 +10,7 @@
 LOCAL_STATIC_JAVA_LIBRARIES := \
     services.core \
     services.devicepolicy \
+    services.net \
     easymocklib \
     guava \
     mockito-target
diff --git a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
new file mode 100644
index 0000000..2658937
--- /dev/null
+++ b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2015 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.net.dhcp;
+
+import android.net.NetworkUtils;
+import android.system.OsConstants;
+import android.test.suitebuilder.annotation.SmallTest;
+import junit.framework.TestCase;
+
+import java.net.Inet4Address;
+import java.nio.ByteBuffer;
+
+import static android.net.dhcp.DhcpPacket.*;
+
+
+public class DhcpPacketTest extends TestCase {
+
+    private static Inet4Address SERVER_ADDR =
+            (Inet4Address) NetworkUtils.numericToInetAddress("192.0.2.1");
+    private static Inet4Address CLIENT_ADDR =
+            (Inet4Address) NetworkUtils.numericToInetAddress("192.0.2.234");
+    private static byte[] CLIENT_MAC = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 };
+
+    class TestDhcpPacket extends DhcpPacket {
+        private byte mType;
+        // TODO: Make this a map of option numbers to bytes instead.
+        private byte[] mDomainBytes, mVendorInfoBytes;
+
+        public TestDhcpPacket(byte type, byte[] domainBytes, byte[] vendorInfoBytes) {
+            super(0xdeadbeef, INADDR_ANY, CLIENT_ADDR, INADDR_ANY, INADDR_ANY, CLIENT_MAC, true);
+            mType = type;
+            mDomainBytes = domainBytes;
+            mVendorInfoBytes = vendorInfoBytes;
+        }
+
+        public ByteBuffer buildPacket(int encap, short unusedDestUdp, short unusedSrcUdp) {
+            ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
+            fillInPacket(encap, CLIENT_ADDR, SERVER_ADDR,
+                         DHCP_CLIENT, DHCP_SERVER, result, DHCP_BOOTREPLY, false);
+            return result;
+        }
+
+        public void finishPacket(ByteBuffer buffer) {
+            addTlv(buffer, DHCP_MESSAGE_TYPE, mType);
+            if (mDomainBytes != null) {
+                addTlv(buffer, DHCP_DOMAIN_NAME, mDomainBytes);
+            }
+            if (mVendorInfoBytes != null) {
+                addTlv(buffer, DHCP_VENDOR_CLASS_ID, mVendorInfoBytes);
+            }
+            addTlvEnd(buffer);
+        }
+
+        // Convenience method.
+        public ByteBuffer build() {
+            // ENCAP_BOOTP packets don't contain ports, so just pass in 0.
+            ByteBuffer pkt = buildPacket(ENCAP_BOOTP, (short) 0, (short) 0);
+            pkt.flip();
+            return pkt;
+        }
+    }
+
+    private void assertDomainAndVendorInfoParses(
+            String expectedDomain, byte[] domainBytes,
+            String expectedVendorInfo, byte[] vendorInfoBytes) {
+        ByteBuffer packet = new TestDhcpPacket(DHCP_MESSAGE_TYPE_OFFER,
+                domainBytes, vendorInfoBytes).build();
+        DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
+        assertEquals(expectedDomain, offerPacket.mDomainName);
+        assertEquals(expectedVendorInfo, offerPacket.mVendorId);
+    }
+
+    @SmallTest
+    public void testDomainName() throws Exception {
+        byte[] nullByte = new byte[] { 0x00 };
+        byte[] twoNullBytes = new byte[] { 0x00, 0x00 };
+        byte[] nonNullDomain = new byte[] {
+            (byte) 'g', (byte) 'o', (byte) 'o', (byte) '.', (byte) 'g', (byte) 'l'
+        };
+        byte[] trailingNullDomain = new byte[] {
+            (byte) 'g', (byte) 'o', (byte) 'o', (byte) '.', (byte) 'g', (byte) 'l', 0x00
+        };
+        byte[] embeddedNullsDomain = new byte[] {
+            (byte) 'g', (byte) 'o', (byte) 'o', 0x00, 0x00, (byte) 'g', (byte) 'l'
+        };
+        byte[] metered = "ANDROID_METERED".getBytes("US-ASCII");
+
+        byte[] meteredEmbeddedNull = metered.clone();
+        meteredEmbeddedNull[7] = (char) 0;
+
+        byte[] meteredTrailingNull = metered.clone();
+        meteredTrailingNull[meteredTrailingNull.length - 1] = (char) 0;
+
+        assertDomainAndVendorInfoParses("", nullByte, "\u0000", nullByte);
+        assertDomainAndVendorInfoParses("", twoNullBytes, "\u0000\u0000", twoNullBytes);
+        assertDomainAndVendorInfoParses("goo.gl", nonNullDomain, "ANDROID_METERED", metered);
+        assertDomainAndVendorInfoParses("goo", embeddedNullsDomain,
+                                        "ANDROID\u0000METERED", meteredEmbeddedNull);
+        assertDomainAndVendorInfoParses("goo.gl", trailingNullDomain,
+                                        "ANDROID_METERE\u0000", meteredTrailingNull);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
index beb353a..c198900 100644
--- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
@@ -20,7 +20,6 @@
 import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.ConnectivityManager.TYPE_WIFI;
 import static android.net.ConnectivityManager.getNetworkTypeName;
-import static android.net.NetworkStateTracker.EVENT_STATE_CHANGED;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Matchers.isA;
diff --git a/telecomm/java/android/telecom/AudioState.java b/telecomm/java/android/telecom/AudioState.java
index 9c03319..465c5f4 100644
--- a/telecomm/java/android/telecom/AudioState.java
+++ b/telecomm/java/android/telecom/AudioState.java
@@ -16,7 +16,6 @@
 
 package android.telecom;
 
-import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -25,9 +24,7 @@
 /**
  *  Encapsulates the telecom audio state, including the current audio routing, supported audio
  *  routing and mute.
- *  @hide
  */
-@SystemApi
 public final class AudioState implements Parcelable {
     /** Direct the audio stream through the device's earpiece. */
     public static final int ROUTE_EARPIECE      = 0x00000001;
@@ -47,21 +44,13 @@
      */
     public static final int ROUTE_WIRED_OR_EARPIECE = ROUTE_EARPIECE | ROUTE_WIRED_HEADSET;
 
-    /** Bit mask of all possible audio routes.
-     *
-     * @hide
-     */
-    public static final int ROUTE_ALL = ROUTE_EARPIECE | ROUTE_BLUETOOTH | ROUTE_WIRED_HEADSET |
+    /** Bit mask of all possible audio routes. */
+    private static final int ROUTE_ALL = ROUTE_EARPIECE | ROUTE_BLUETOOTH | ROUTE_WIRED_HEADSET |
             ROUTE_SPEAKER;
 
-    /** Note: Deprecated, please do not use if possible. */
-    @SystemApi public final boolean isMuted;
-
-    /** Note: Deprecated, please do not use if possible. */
-    @SystemApi public final int route;
-
-    /** Note: Deprecated, please do not use if possible. */
-    @SystemApi public final int supportedRouteMask;
+    private final boolean isMuted;
+    private final int route;
+    private final int supportedRouteMask;
 
     public AudioState(boolean muted, int route, int supportedRouteMask) {
         this.isMuted = muted;
@@ -97,7 +86,6 @@
                 audioRouteToString(supportedRouteMask));
     }
 
-    /** @hide */
     public static String audioRouteToString(int route) {
         if (route == 0 || (route & ~ROUTE_ALL) != 0x0) {
             return "UNKNOWN";
diff --git a/telecomm/java/android/telecom/AuthenticatorService.java b/telecomm/java/android/telecom/AuthenticatorService.java
new file mode 100644
index 0000000..1e43c71
--- /dev/null
+++ b/telecomm/java/android/telecom/AuthenticatorService.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2015 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.telecom;
+import android.accounts.AbstractAccountAuthenticator;
+import android.accounts.Account;
+import android.accounts.AccountAuthenticatorResponse;
+import android.accounts.NetworkErrorException;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+
+/**
+ * A generic stub account authenticator service often used for sync adapters that do not directly
+ * involve accounts.
+ *
+ * @hide
+ */
+public class AuthenticatorService extends Service {
+    private static Authenticator mAuthenticator;
+
+    @Override
+    public void onCreate() {
+        mAuthenticator = new Authenticator(this);
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return mAuthenticator.getIBinder();
+    }
+
+    /**
+     * Stub account authenticator. All methods either return null or throw an exception.
+     */
+    public class Authenticator extends AbstractAccountAuthenticator {
+        public Authenticator(Context context) {
+            super(context);
+        }
+
+        @Override
+        public Bundle editProperties(AccountAuthenticatorResponse accountAuthenticatorResponse,
+                                     String s) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Bundle addAccount(AccountAuthenticatorResponse accountAuthenticatorResponse,
+                                 String s, String s2, String[] strings, Bundle bundle)
+                throws NetworkErrorException {
+            return null;
+        }
+
+        @Override
+        public Bundle confirmCredentials(AccountAuthenticatorResponse accountAuthenticatorResponse,
+                                         Account account, Bundle bundle)
+                throws NetworkErrorException {
+            return null;
+        }
+
+        @Override
+        public Bundle getAuthToken(AccountAuthenticatorResponse accountAuthenticatorResponse,
+                                   Account account, String s, Bundle bundle)
+                throws NetworkErrorException {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public String getAuthTokenLabel(String s) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Bundle updateCredentials(AccountAuthenticatorResponse accountAuthenticatorResponse,
+                                        Account account, String s, Bundle bundle)
+                throws NetworkErrorException {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public Bundle hasFeatures(AccountAuthenticatorResponse accountAuthenticatorResponse,
+                                  Account account, String[] strings)
+                throws NetworkErrorException {
+            throw new UnsupportedOperationException();
+        }
+    }
+}
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index c1d2f9b..2a9e539 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -16,7 +16,6 @@
 
 package android.telecom;
 
-import android.annotation.SystemApi;
 import android.net.Uri;
 import android.os.Bundle;
 
@@ -30,10 +29,7 @@
 
 /**
  * Represents an ongoing phone call that the in-call app should present to the user.
- *
- * {@hide}
  */
-@SystemApi
 public final class Call {
     /**
      * The state of a {@code Call} when newly created.
@@ -91,8 +87,6 @@
      * The key to retrieve the optional {@code PhoneAccount}s Telecom can bundle with its Call
      * extras. Used to pass the phone accounts to display on the front end to the user in order to
      * select phone accounts to (for example) place a call.
-     *
-     * @hide
      */
     public static final String AVAILABLE_PHONE_ACCOUNTS = "selectPhoneAccountAccounts";
 
@@ -141,28 +135,36 @@
         public static final int CAPABILITY_MANAGE_CONFERENCE = 0x00000080;
 
         /**
-         * Local device supports video telephony.
-         * @hide
+         * Local device supports receiving video.
          */
-        public static final int CAPABILITY_SUPPORTS_VT_LOCAL = 0x00000100;
+        public static final int CAPABILITY_SUPPORTS_VT_LOCAL_RX = 0x00000100;
 
         /**
-         * Remote device supports video telephony.
-         * @hide
+         * Local device supports transmitting video.
          */
-        public static final int CAPABILITY_SUPPORTS_VT_REMOTE = 0x00000200;
+        public static final int CAPABILITY_SUPPORTS_VT_LOCAL_TX = 0x00000200;
 
         /**
-         * Call is using high definition audio.
-         * @hide
+         * Local device supports bidirectional video calling.
          */
-        public static final int CAPABILITY_HIGH_DEF_AUDIO = 0x00000400;
+        public static final int CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL =
+                CAPABILITY_SUPPORTS_VT_LOCAL_RX | CAPABILITY_SUPPORTS_VT_LOCAL_TX;
 
         /**
-         * Call is using WIFI.
-         * @hide
+         * Remote device supports receiving video.
          */
-        public static final int CAPABILITY_WIFI = 0x00000800;
+        public static final int CAPABILITY_SUPPORTS_VT_REMOTE_RX = 0x00000400;
+
+        /**
+         * Remote device supports transmitting video.
+         */
+        public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 0x00000800;
+
+        /**
+         * Remote device supports bidirectional video calling.
+         */
+        public static final int CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL =
+                CAPABILITY_SUPPORTS_VT_REMOTE_RX | CAPABILITY_SUPPORTS_VT_REMOTE_TX;
 
         /**
          * Call is able to be separated from its parent {@code Conference}, if any.
@@ -173,20 +175,50 @@
          * Call is able to be individually disconnected when in a {@code Conference}.
          */
         public static final int CAPABILITY_DISCONNECT_FROM_CONFERENCE = 0x00002000;
-        
+
         /**
          * Whether the call is a generic conference, where we do not know the precise state of
          * participants in the conference (eg. on CDMA).
-         *
-         * @hide
          */
         public static final int CAPABILITY_GENERIC_CONFERENCE = 0x00004000;
 
         /**
+         * Call is using high definition audio.
+         */
+        public static final int CAPABILITY_HIGH_DEF_AUDIO = 0x00008000;
+
+        /**
+         * Call is using WIFI.
+         */
+        public static final int CAPABILITY_WIFI = 0x00010000;
+
+        /**
+         * Indicates that the current device callback number should be shown.
+         */
+        public static final int CAPABILITY_SHOW_CALLBACK_NUMBER = 0x00020000;
+
+        /**
          * Speed up audio setup for MT call.
          * @hide
          */
-        public static final int CAPABILITY_SPEED_UP_MT_AUDIO = 0x00008000;
+        public static final int CAPABILITY_SPEED_UP_MT_AUDIO = 0x00040000;
+
+        /**
+         * Call can be upgraded to a video call.
+         * @hide
+         */
+        public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 0x00080000;
+
+        /**
+         * For video calls, indicates whether the outgoing video for the call can be paused using
+         * the {@link android.telecom.VideoProfile.VideoState#PAUSED} VideoState.
+         * @hide
+         */
+        public static final int CAPABILITY_CAN_PAUSE_VIDEO = 0x00100000;
+
+        //******************************************************************************************
+        // Next CAPABILITY value: 0x00200000
+        //******************************************************************************************
 
         private final Uri mHandle;
         private final int mHandlePresentation;
@@ -201,6 +233,7 @@
         private final int mVideoState;
         private final StatusHints mStatusHints;
         private final Bundle mExtras;
+        private final int mCallSubstate;
 
         /**
          * Whether the supplied capabilities  supports the specified capability.
@@ -208,7 +241,6 @@
          * @param capabilities A bit field of capabilities.
          * @param capability The capability to check capabilities for.
          * @return Whether the specified capability is supported.
-         * @hide
          */
         public static boolean can(int capabilities, int capability) {
             return (capabilities & capability) != 0;
@@ -219,7 +251,6 @@
          *
          * @param capability The capability to check capabilities for.
          * @return Whether the specified capability is supported.
-         * @hide
          */
         public boolean can(int capability) {
             return can(mCallCapabilities, capability);
@@ -255,11 +286,23 @@
             if (can(capabilities, CAPABILITY_MANAGE_CONFERENCE)) {
                 builder.append(" CAPABILITY_MANAGE_CONFERENCE");
             }
-            if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL)) {
-                builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL");
+            if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_RX)) {
+                builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL_RX");
             }
-            if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE)) {
-                builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE");
+            if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_TX)) {
+                builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL_TX");
+            }
+            if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL)) {
+                builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL");
+            }
+            if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_RX)) {
+                builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_RX");
+            }
+            if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_TX)) {
+                builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_TX");
+            }
+            if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL)) {
+                builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL");
             }
             if (can(capabilities, CAPABILITY_HIGH_DEF_AUDIO)) {
                 builder.append(" CAPABILITY_HIGH_DEF_AUDIO");
@@ -270,8 +313,17 @@
             if (can(capabilities, CAPABILITY_GENERIC_CONFERENCE)) {
                 builder.append(" CAPABILITY_GENERIC_CONFERENCE");
             }
+            if (can(capabilities, CAPABILITY_SHOW_CALLBACK_NUMBER)) {
+                builder.append(" CAPABILITY_SHOW_CALLBACK_NUMBER");
+            }
             if (can(capabilities, CAPABILITY_SPEED_UP_MT_AUDIO)) {
-                builder.append(" CAPABILITY_SPEED_UP_IMS_MT_AUDIO");
+                builder.append(" CAPABILITY_SPEED_UP_MT_AUDIO");
+            }
+            if (can(capabilities, CAPABILITY_CAN_UPGRADE_TO_VIDEO)) {
+                builder.append(" CAPABILITY_CAN_UPGRADE_TO_VIDEO");
+            }
+            if (can(capabilities, CAPABILITY_CAN_PAUSE_VIDEO)) {
+                builder.append(" CAPABILITY_CAN_PAUSE_VIDEO");
             }
             builder.append("]");
             return builder.toString();
@@ -345,7 +397,7 @@
          * periodically, but user interfaces should not rely on this to display any "call time
          * clock".
          */
-        public long getConnectTimeMillis() {
+        public final long getConnectTimeMillis() {
             return mConnectTimeMillis;
         }
 
@@ -378,6 +430,14 @@
             return mExtras;
         }
 
+        /**
+         * @return The substate of the {@code Call}.
+         * @hide
+         */
+        public int getCallSubstate() {
+            return mCallSubstate;
+        }
+
         @Override
         public boolean equals(Object o) {
             if (o instanceof Details) {
@@ -396,7 +456,8 @@
                         Objects.equals(mGatewayInfo, d.mGatewayInfo) &&
                         Objects.equals(mVideoState, d.mVideoState) &&
                         Objects.equals(mStatusHints, d.mStatusHints) &&
-                        Objects.equals(mExtras, d.mExtras);
+                        Objects.equals(mExtras, d.mExtras) &&
+                        Objects.equals(mCallSubstate, d.mCallSubstate);
             }
             return false;
         }
@@ -416,7 +477,8 @@
                     Objects.hashCode(mGatewayInfo) +
                     Objects.hashCode(mVideoState) +
                     Objects.hashCode(mStatusHints) +
-                    Objects.hashCode(mExtras);
+                    Objects.hashCode(mExtras) +
+                    Objects.hashCode(mCallSubstate);
         }
 
         /** {@hide} */
@@ -433,7 +495,8 @@
                 GatewayInfo gatewayInfo,
                 int videoState,
                 StatusHints statusHints,
-                Bundle extras) {
+                Bundle extras,
+                int callSubstate) {
             mHandle = handle;
             mHandlePresentation = handlePresentation;
             mCallerDisplayName = callerDisplayName;
@@ -447,6 +510,7 @@
             mVideoState = videoState;
             mStatusHints = statusHints;
             mExtras = extras;
+            mCallSubstate = callSubstate;
         }
     }
 
@@ -509,7 +573,6 @@
          *
          * @param call The {@code Call} invoking this method.
          * @param videoCall The {@code Call.VideoCall} associated with the {@code Call}.
-         * @hide
          */
         public void onVideoCallChanged(Call call, InCallService.VideoCall videoCall) {}
 
@@ -762,7 +825,6 @@
      * Obtains an object that can be used to display video from this {@code Call}.
      *
      * @return An {@code Call.VideoCall}.
-     * @hide
      */
     public InCallService.VideoCall getVideoCall() {
         return mVideoCall;
@@ -827,7 +889,8 @@
                 parcelableCall.getGatewayInfo(),
                 parcelableCall.getVideoState(),
                 parcelableCall.getStatusHints(),
-                parcelableCall.getExtras());
+                parcelableCall.getExtras(),
+                parcelableCall.getCallSubstate());
         boolean detailsChanged = !Objects.equals(mDetails, details);
         if (detailsChanged) {
             mDetails = details;
@@ -840,7 +903,8 @@
                     Collections.unmodifiableList(parcelableCall.getCannedSmsResponses());
         }
 
-        boolean videoCallChanged = !Objects.equals(mVideoCall, parcelableCall.getVideoCall());
+        boolean videoCallChanged = parcelableCall.isVideoCallProviderChanged() &&
+                !Objects.equals(mVideoCall, parcelableCall.getVideoCall());
         if (videoCallChanged) {
             mVideoCall = parcelableCall.getVideoCall();
         }
diff --git a/telecomm/java/android/telecom/CallProperties.java b/telecomm/java/android/telecom/CallProperties.java
index b1b82e2..1721a39 100644
--- a/telecomm/java/android/telecom/CallProperties.java
+++ b/telecomm/java/android/telecom/CallProperties.java
@@ -18,7 +18,6 @@
 
 /**
  * Defines properties of a phone call which may be affected by changes to the call.
- * @hide
  */
 public class CallProperties {
     /** Call is currently in a conference call. */
diff --git a/telecomm/java/android/telecom/CallState.java b/telecomm/java/android/telecom/CallState.java
index bd9223a..5584226 100644
--- a/telecomm/java/android/telecom/CallState.java
+++ b/telecomm/java/android/telecom/CallState.java
@@ -16,17 +16,12 @@
 
 package android.telecom;
 
-import android.annotation.SystemApi;
-
 /**
  * Defines call-state constants of the different states in which a call can exist. Although states
  * have the notion of normal transitions, due to the volatile nature of telephony systems, code
  * that uses these states should be resilient to unexpected state changes outside of what is
  * considered traditional.
- *
- * {@hide}
  */
-@SystemApi
 public final class CallState {
 
     private CallState() {}
diff --git a/telecomm/java/android/telecom/CameraCapabilities.java b/telecomm/java/android/telecom/CameraCapabilities.java
index f968c13..6242956 100644
--- a/telecomm/java/android/telecom/CameraCapabilities.java
+++ b/telecomm/java/android/telecom/CameraCapabilities.java
@@ -21,21 +21,10 @@
 
 /**
  * Represents the camera capabilities important to a Video Telephony provider.
- * @hide
  */
 public final class CameraCapabilities implements Parcelable {
 
     /**
-     * Whether the camera supports zoom.
-     */
-    private final boolean mZoomSupported;
-
-    /**
-     * The maximum zoom supported by the camera.
-     */
-    private final float mMaxZoom;
-
-    /**
      * The width of the camera video in pixels.
      */
     private final int mWidth;
@@ -46,18 +35,40 @@
     private final int mHeight;
 
     /**
+     * Whether the camera supports zoom.
+     */
+    private final boolean mZoomSupported;
+
+    /**
+     * The maximum zoom supported by the camera.
+     */
+    private final float mMaxZoom;
+
+    /**
      * Create a call camera capabilities instance.
      *
-     * @param zoomSupported True when camera supports zoom.
-     * @param maxZoom Maximum zoom supported by camera.
      * @param width The width of the camera video (in pixels).
      * @param height The height of the camera video (in pixels).
      */
-    public CameraCapabilities(boolean zoomSupported, float maxZoom, int width, int height) {
-        mZoomSupported = zoomSupported;
-        mMaxZoom = maxZoom;
+    public CameraCapabilities(int width, int height) {
+        this(width, height, false, 1.0f);
+    }
+
+    /**
+     * Create a call camera capabilities instance that optionally
+     * supports zoom.
+     *
+     * @param width The width of the camera video (in pixels).
+     * @param height The height of the camera video (in pixels).
+     * @param zoomSupported True when camera supports zoom.
+     * @param maxZoom Maximum zoom supported by camera.
+     * @hide
+     */
+    public CameraCapabilities(int width, int height, boolean zoomSupported, float maxZoom) {
         mWidth = width;
         mHeight = height;
+        mZoomSupported = zoomSupported;
+        mMaxZoom = maxZoom;
     }
 
     /**
@@ -73,12 +84,12 @@
                  */
                 @Override
                 public CameraCapabilities createFromParcel(Parcel source) {
-                    boolean supportsZoom = source.readByte() != 0;
-                    float maxZoom = source.readFloat();
                     int width = source.readInt();
                     int height = source.readInt();
+                    boolean supportsZoom = source.readByte() != 0;
+                    float maxZoom = source.readFloat();
 
-                    return new CameraCapabilities(supportsZoom, maxZoom, width, height);
+                    return new CameraCapabilities(width, height, supportsZoom, maxZoom);
                 }
 
                 @Override
@@ -108,24 +119,10 @@
      */
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeByte((byte) (isZoomSupported() ? 1 : 0));
-        dest.writeFloat(getMaxZoom());
         dest.writeInt(getWidth());
         dest.writeInt(getHeight());
-    }
-
-    /**
-     * Whether the camera supports zoom.
-     */
-    public boolean isZoomSupported() {
-        return mZoomSupported;
-    }
-
-    /**
-     * The maximum zoom supported by the camera.
-     */
-    public float getMaxZoom() {
-        return mMaxZoom;
+        dest.writeByte((byte) (isZoomSupported() ? 1 : 0));
+        dest.writeFloat(getMaxZoom());
     }
 
     /**
@@ -141,4 +138,20 @@
     public int getHeight() {
         return mHeight;
     }
+
+    /**
+     * Whether the camera supports zoom.
+     * @hide
+     */
+    public boolean isZoomSupported() {
+        return mZoomSupported;
+    }
+
+    /**
+     * The maximum zoom supported by the camera.
+     * @hide
+     */
+    public float getMaxZoom() {
+        return mMaxZoom;
+    }
 }
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index 8c3b066..3acf945 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -16,27 +16,26 @@
 
 package android.telecom;
 
-import android.annotation.SystemApi;
+import android.telecom.Connection.VideoProvider;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.Locale;
 import java.util.Set;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.CopyOnWriteArraySet;
 
 /**
  * Represents a conference call which can contain any number of {@link Connection} objects.
- * @hide
  */
-@SystemApi
 public abstract class Conference implements IConferenceable {
 
     /**
      * Used to indicate that the conference connection time is not specified.  If not specified,
      * Telecom will set the connect time.
      */
-    public static long CONNECT_TIME_NOT_SPECIFIED = 0;
+    public static final long CONNECT_TIME_NOT_SPECIFIED = 0;
 
     /** @hide */
     public abstract static class Listener {
@@ -49,6 +48,8 @@
         public void onDestroyed(Conference conference) {}
         public void onConnectionCapabilitiesChanged(
                 Conference conference, int connectionCapabilities) {}
+        public void onVideoStateChanged(Conference c, int videoState) { }
+        public void onVideoProviderChanged(Conference c, Connection.VideoProvider videoProvider) {}
         public void onStatusHintsChanged(Conference conference, StatusHints statusHints) {}
     }
 
@@ -60,7 +61,7 @@
     private final List<Connection> mUnmodifiableConferenceableConnections =
             Collections.unmodifiableList(mConferenceableConnections);
 
-    protected PhoneAccountHandle mPhoneAccount;
+    private PhoneAccountHandle mPhoneAccount;
     private AudioState mAudioState;
     private int mState = Connection.STATE_NEW;
     private DisconnectCause mDisconnectCause;
@@ -114,11 +115,6 @@
         return mState;
     }
 
-    /** @hide */
-    @Deprecated public final int getCapabilities() {
-        return getConnectionCapabilities();
-    }
-
     /**
      * Returns the capabilities of a conference. See {@code CAPABILITY_*} constants in class
      * {@link Connection} for valid values.
@@ -182,6 +178,22 @@
     }
 
     /**
+     * Returns VideoProvider of the primary call. This can be null.
+     *  @hide
+     */
+    public VideoProvider getVideoProvider() {
+        return null;
+    }
+
+    /**
+     * Returns video state of the primary call.
+     *  @hide
+     */
+    public int getVideoState() {
+        return VideoProfile.VideoState.AUDIO_ONLY;
+    }
+
+    /**
      * Invoked when the Conference and all it's {@link Connection}s should be disconnected.
      */
     public void onDisconnect() {}
@@ -283,11 +295,6 @@
         return mDisconnectCause;
     }
 
-    /** @hide */
-    @Deprecated public final void setCapabilities(int connectionCapabilities) {
-        setConnectionCapabilities(connectionCapabilities);
-    }
-
     /**
      * Sets the capabilities of a conference. See {@code CAPABILITY_*} constants of class
      * {@link Connection} for valid values.
@@ -311,6 +318,7 @@
      * @return True if the connection was successfully added.
      */
     public final boolean addConnection(Connection connection) {
+        Log.d(this, "Connection=%s, connection=", connection);
         if (connection != null && !mChildConnections.contains(connection)) {
             if (connection.setConference(this)) {
                 mChildConnections.add(connection);
@@ -357,6 +365,38 @@
         fireOnConferenceableConnectionsChanged();
     }
 
+    /**
+     * Set the video state for the conference.
+     * Valid values: {@link VideoProfile.VideoState#AUDIO_ONLY},
+     * {@link VideoProfile.VideoState#BIDIRECTIONAL},
+     * {@link VideoProfile.VideoState#TX_ENABLED},
+     * {@link VideoProfile.VideoState#RX_ENABLED}.
+     *
+     * @param videoState The new video state.
+     * @hide
+     */
+    public final void setVideoState(Connection c, int videoState) {
+        Log.d(this, "setVideoState Conference: %s Connection: %s VideoState: %s",
+                this, c, videoState);
+        for (Listener l : mListeners) {
+            l.onVideoStateChanged(this, videoState);
+        }
+    }
+
+    /**
+     * Sets the video connection provider.
+     *
+     * @param videoProvider The video provider.
+     * @hide
+     */
+    public final void setVideoProvider(Connection c, Connection.VideoProvider videoProvider) {
+        Log.d(this, "setVideoProvider Conference: %s Connection: %s VideoState: %s",
+                this, c, videoProvider);
+        for (Listener l : mListeners) {
+            l.onVideoProviderChanged(this, videoProvider);
+        }
+    }
+
     private final void fireOnConferenceableConnectionsChanged() {
         for (Listener l : mListeners) {
             l.onConferenceableConnectionsChanged(this, getConferenceableConnections());
@@ -446,7 +486,7 @@
      *
      * @return The time the {@code Conference} has been connected.
      */
-    public long getConnectTimeMillis() {
+    public final long getConnectTimeMillis() {
         return mConnectTimeMillis;
     }
 
@@ -487,6 +527,17 @@
         mConferenceableConnections.clear();
     }
 
+    @Override
+    public String toString() {
+        return String.format(Locale.US,
+                "[State: %s,Capabilites: %s, VideoState: %s, VideoProvider: %s, ThisObject %s]",
+                Connection.stateToString(mState),
+                Call.Details.capabilitiesToString(mConnectionCapabilities),
+                getVideoState(),
+                getVideoProvider(),
+                super.toString());
+    }
+
     /**
      * Sets the label and icon status to display in the InCall UI.
      *
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 569163a..99fc6b9 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -19,7 +19,6 @@
 import com.android.internal.telecom.IVideoCallback;
 import com.android.internal.telecom.IVideoProvider;
 
-import android.annotation.SystemApi;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.IBinder;
@@ -29,6 +28,7 @@
 
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
@@ -44,9 +44,7 @@
  * Implementations are then responsible for updating the state of the {@code Connection}, and
  * must call {@link #destroy()} to signal to the framework that the {@code Connection} is no
  * longer used and associated resources may be recovered.
- * @hide
  */
-@SystemApi
 public abstract class Connection implements IConferenceable {
 
     public static final int STATE_INITIALIZING = 0;
@@ -106,28 +104,42 @@
     public static final int CAPABILITY_MANAGE_CONFERENCE = 0x00000080;
 
     /**
-     * Local device supports video telephony.
+     * Local device supports receiving video.
      * @hide
      */
-    public static final int CAPABILITY_SUPPORTS_VT_LOCAL = 0x00000100;
+    public static final int CAPABILITY_SUPPORTS_VT_LOCAL_RX = 0x00000100;
 
     /**
-     * Remote device supports video telephony.
+     * Local device supports transmitting video.
      * @hide
      */
-    public static final int CAPABILITY_SUPPORTS_VT_REMOTE = 0x00000200;
+    public static final int CAPABILITY_SUPPORTS_VT_LOCAL_TX = 0x00000200;
 
     /**
-     * Connection is using high definition audio.
+     * Local device supports bidirectional video calling.
      * @hide
      */
-    public static final int CAPABILITY_HIGH_DEF_AUDIO = 0x00000400;
+    public static final int CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL =
+            CAPABILITY_SUPPORTS_VT_LOCAL_RX | CAPABILITY_SUPPORTS_VT_LOCAL_TX;
 
     /**
-     * Connection is using WIFI.
+     * Remote device supports receiving video.
      * @hide
      */
-    public static final int CAPABILITY_WIFI = 0x00000800;
+    public static final int CAPABILITY_SUPPORTS_VT_REMOTE_RX = 0x00000400;
+
+    /**
+     * Remote device supports transmitting video.
+     * @hide
+     */
+    public static final int CAPABILITY_SUPPORTS_VT_REMOTE_TX = 0x00000800;
+
+    /**
+     * Remote device supports bidirectional video calling.
+     * @hide
+     */
+    public static final int CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL =
+            CAPABILITY_SUPPORTS_VT_REMOTE_RX | CAPABILITY_SUPPORTS_VT_REMOTE_TX;
 
     /**
      * Connection is able to be separated from its parent {@code Conference}, if any.
@@ -148,10 +160,88 @@
     public static final int CAPABILITY_GENERIC_CONFERENCE = 0x00004000;
 
     /**
+     * Connection is using high definition audio.
+     * @hide
+     */
+    public static final int CAPABILITY_HIGH_DEF_AUDIO = 0x00008000;
+
+    /**
+     * Connection is using WIFI.
+     * @hide
+     */
+    public static final int CAPABILITY_WIFI = 0x00010000;
+
+    /**
+     * Indicates that the current device callback number should be shown.
+     *
+     * @hide
+     */
+    public static final int CAPABILITY_SHOW_CALLBACK_NUMBER = 0x00020000;
+
+    /**
      * Speed up audio setup for MT call.
      * @hide
     */
-    public static final int CAPABILITY_SPEED_UP_MT_AUDIO = 0x00008000;
+    public static final int CAPABILITY_SPEED_UP_MT_AUDIO = 0x00040000;
+
+    /**
+     * Call can be upgraded to a video call.
+     * @hide
+     */
+    public static final int CAPABILITY_CAN_UPGRADE_TO_VIDEO = 0x00080000;
+
+    /**
+     * For video calls, indicates whether the outgoing video for the call can be paused using
+     * the {@link android.telecom.VideoProfile.VideoState#PAUSED} VideoState.
+     * @hide
+     */
+    public static final int CAPABILITY_CAN_PAUSE_VIDEO = 0x00100000;
+
+    //**********************************************************************************************
+    // Next CAPABILITY value: 0x00200000
+    //**********************************************************************************************
+
+    /**
+     * Call substate bitmask values
+     */
+
+    /* Default case */
+    /**
+     * @hide
+     */
+    public static final int SUBSTATE_NONE = 0;
+
+    /* Indicates that the call is connected but audio attribute is suspended */
+    /**
+     * @hide
+     */
+    public static final int SUBSTATE_AUDIO_CONNECTED_SUSPENDED = 0x1;
+
+    /* Indicates that the call is connected but video attribute is suspended */
+    /**
+     * @hide
+     */
+    public static final int SUBSTATE_VIDEO_CONNECTED_SUSPENDED = 0x2;
+
+    /* Indicates that the call is established but media retry is needed */
+    /**
+     * @hide
+     */
+    public static final int SUBSTATE_AVP_RETRY = 0x4;
+
+    /* Indicates that the call is multitasking */
+    /**
+     * @hide
+     */
+    public static final int SUBSTATE_MEDIA_PAUSED = 0x8;
+
+    /* Mask containing all the call substate bits set */
+    /**
+     * @hide
+     */
+    public static final int SUBSTATE_ALL = SUBSTATE_AUDIO_CONNECTED_SUSPENDED |
+        SUBSTATE_VIDEO_CONNECTED_SUSPENDED | SUBSTATE_AVP_RETRY |
+        SUBSTATE_MEDIA_PAUSED;
 
     // Flag controlling whether PII is emitted into the logs
     private static final boolean PII_DEBUG = Log.isLoggable(android.util.Log.DEBUG);
@@ -224,11 +314,23 @@
         if (can(capabilities, CAPABILITY_MANAGE_CONFERENCE)) {
             builder.append(" CAPABILITY_MANAGE_CONFERENCE");
         }
-        if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL)) {
-            builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL");
+        if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_RX)) {
+            builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL_RX");
         }
-        if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE)) {
-            builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE");
+        if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_TX)) {
+            builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL_TX");
+        }
+        if (can(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL)) {
+            builder.append(" CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL");
+        }
+        if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_RX)) {
+            builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_RX");
+        }
+        if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_TX)) {
+            builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_TX");
+        }
+        if (can(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL)) {
+            builder.append(" CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL");
         }
         if (can(capabilities, CAPABILITY_HIGH_DEF_AUDIO)) {
             builder.append(" CAPABILITY_HIGH_DEF_AUDIO");
@@ -239,8 +341,17 @@
         if (can(capabilities, CAPABILITY_GENERIC_CONFERENCE)) {
             builder.append(" CAPABILITY_GENERIC_CONFERENCE");
         }
+        if (can(capabilities, CAPABILITY_SHOW_CALLBACK_NUMBER)) {
+            builder.append(" CAPABILITY_SHOW_CALLBACK_NUMBER");
+        }
         if (can(capabilities, CAPABILITY_SPEED_UP_MT_AUDIO)) {
-            builder.append(" CAPABILITY_SPEED_UP_IMS_MT_AUDIO");
+            builder.append(" CAPABILITY_SPEED_UP_MT_AUDIO");
+        }
+        if (can(capabilities, CAPABILITY_CAN_UPGRADE_TO_VIDEO)) {
+            builder.append(" CAPABILITY_CAN_UPGRADE_TO_VIDEO");
+        }
+        if (can(capabilities, CAPABILITY_CAN_PAUSE_VIDEO)) {
+            builder.append(" CAPABILITY_CAN_PAUSE_VIDEO");
         }
         builder.append("]");
         return builder.toString();
@@ -270,9 +381,9 @@
         public void onConferenceParticipantsChanged(Connection c,
                 List<ConferenceParticipant> participants) {}
         public void onConferenceStarted() {}
+        public void onCallSubstateChanged(Connection c, int substate) {}
     }
 
-    /** @hide */
     public static abstract class VideoProvider {
 
         /**
@@ -325,7 +436,17 @@
          */
         public static final int SESSION_MODIFY_REQUEST_INVALID = 3;
 
-        private static final int MSG_SET_VIDEO_CALLBACK = 1;
+        /**
+         * Session modify request timed out.
+         */
+        public static final int SESSION_MODIFY_REQUEST_TIMED_OUT = 4;
+
+        /**
+         * Session modify request rejected by remote UE.
+         */
+        public static final int SESSION_MODIFY_REQUEST_REJECTED_BY_REMOTE = 5;
+
+        private static final int MSG_ADD_VIDEO_CALLBACK = 1;
         private static final int MSG_SET_CAMERA = 2;
         private static final int MSG_SET_PREVIEW_SURFACE = 3;
         private static final int MSG_SET_DISPLAY_SURFACE = 4;
@@ -336,11 +457,16 @@
         private static final int MSG_REQUEST_CAMERA_CAPABILITIES = 9;
         private static final int MSG_REQUEST_CONNECTION_DATA_USAGE = 10;
         private static final int MSG_SET_PAUSE_IMAGE = 11;
+        private static final int MSG_REMOVE_VIDEO_CALLBACK = 12;
 
         private final VideoProvider.VideoProviderHandler
                 mMessageHandler = new VideoProvider.VideoProviderHandler();
         private final VideoProvider.VideoProviderBinder mBinder;
-        private IVideoCallback mVideoCallback;
+
+        /**
+         * Stores a list of the video callbacks, keyed by IBinder.
+         */
+        private HashMap<IBinder, IVideoCallback> mVideoCallbacks = new HashMap<>();
 
         /**
          * Default handler used to consolidate binder method calls onto a single thread.
@@ -349,9 +475,29 @@
             @Override
             public void handleMessage(Message msg) {
                 switch (msg.what) {
-                    case MSG_SET_VIDEO_CALLBACK:
-                        mVideoCallback = IVideoCallback.Stub.asInterface((IBinder) msg.obj);
+                    case MSG_ADD_VIDEO_CALLBACK: {
+                        IBinder binder = (IBinder) msg.obj;
+                        IVideoCallback callback = IVideoCallback.Stub
+                                .asInterface((IBinder) msg.obj);
+                        if (mVideoCallbacks.containsKey(binder)) {
+                            Log.i(this, "addVideoProvider - skipped; already present.");
+                            break;
+                        }
+                        mVideoCallbacks.put(binder, callback);
+                        Log.i(this, "addVideoProvider  "+ mVideoCallbacks.size());
                         break;
+                    }
+                    case MSG_REMOVE_VIDEO_CALLBACK: {
+                        IBinder binder = (IBinder) msg.obj;
+                        IVideoCallback callback = IVideoCallback.Stub
+                                .asInterface((IBinder) msg.obj);
+                        if (!mVideoCallbacks.containsKey(binder)) {
+                            Log.i(this, "removeVideoProvider - skipped; not present.");
+                            break;
+                        }
+                        mVideoCallbacks.remove(binder);
+                        break;
+                    }
                     case MSG_SET_CAMERA:
                         onSetCamera((String) msg.obj);
                         break;
@@ -392,9 +538,14 @@
          * IVideoProvider stub implementation.
          */
         private final class VideoProviderBinder extends IVideoProvider.Stub {
-            public void setVideoCallback(IBinder videoCallbackBinder) {
+            public void addVideoCallback(IBinder videoCallbackBinder) {
                 mMessageHandler.obtainMessage(
-                        MSG_SET_VIDEO_CALLBACK, videoCallbackBinder).sendToTarget();
+                        MSG_ADD_VIDEO_CALLBACK, videoCallbackBinder).sendToTarget();
+            }
+
+            public void removeVideoCallback(IBinder videoCallbackBinder) {
+                mMessageHandler.obtainMessage(
+                        MSG_REMOVE_VIDEO_CALLBACK, videoCallbackBinder).sendToTarget();
             }
 
             public void setCamera(String cameraId) {
@@ -410,7 +561,8 @@
             }
 
             public void setDeviceOrientation(int rotation) {
-                mMessageHandler.obtainMessage(MSG_SET_DEVICE_ORIENTATION, rotation).sendToTarget();
+                mMessageHandler.obtainMessage(
+                        MSG_SET_DEVICE_ORIENTATION, rotation, 0).sendToTarget();
             }
 
             public void setZoom(float value) {
@@ -531,21 +683,23 @@
         public abstract void onSetPauseImage(String uri);
 
         /**
-         * Invokes callback method defined in In-Call UI.
+         * Invokes callback method defined in listening {@link InCallService} implementations.
          *
          * @param videoProfile The requested video connection profile.
          */
         public void receiveSessionModifyRequest(VideoProfile videoProfile) {
-            if (mVideoCallback != null) {
+            if (mVideoCallbacks != null) {
                 try {
-                    mVideoCallback.receiveSessionModifyRequest(videoProfile);
+                    for (IVideoCallback callback : mVideoCallbacks.values()) {
+                        callback.receiveSessionModifyRequest(videoProfile);
+                    }
                 } catch (RemoteException ignored) {
                 }
             }
         }
 
         /**
-         * Invokes callback method defined in In-Call UI.
+         * Invokes callback method defined in listening {@link InCallService} implementations.
          *
          * @param status Status of the session modify request.  Valid values are
          *               {@link VideoProvider#SESSION_MODIFY_REQUEST_SUCCESS},
@@ -556,17 +710,19 @@
          */
         public void receiveSessionModifyResponse(int status,
                 VideoProfile requestedProfile, VideoProfile responseProfile) {
-            if (mVideoCallback != null) {
+            if (mVideoCallbacks != null) {
                 try {
-                    mVideoCallback.receiveSessionModifyResponse(
-                            status, requestedProfile, responseProfile);
+                    for (IVideoCallback callback : mVideoCallbacks.values()) {
+                        callback.receiveSessionModifyResponse(status, requestedProfile,
+                                responseProfile);
+                    }
                 } catch (RemoteException ignored) {
                 }
             }
         }
 
         /**
-         * Invokes callback method defined in In-Call UI.
+         * Invokes callback method defined in listening {@link InCallService} implementations.
          *
          * Valid values are: {@link VideoProvider#SESSION_EVENT_RX_PAUSE},
          * {@link VideoProvider#SESSION_EVENT_RX_RESUME},
@@ -576,52 +732,76 @@
          * @param event The event.
          */
         public void handleCallSessionEvent(int event) {
-            if (mVideoCallback != null) {
+            if (mVideoCallbacks != null) {
                 try {
-                    mVideoCallback.handleCallSessionEvent(event);
+                    for (IVideoCallback callback : mVideoCallbacks.values()) {
+                        callback.handleCallSessionEvent(event);
+                    }
                 } catch (RemoteException ignored) {
                 }
             }
         }
 
         /**
-         * Invokes callback method defined in In-Call UI.
+         * Invokes callback method defined in listening {@link InCallService} implementations.
          *
          * @param width  The updated peer video width.
          * @param height The updated peer video height.
          */
         public void changePeerDimensions(int width, int height) {
-            if (mVideoCallback != null) {
+            if (mVideoCallbacks != null) {
                 try {
-                    mVideoCallback.changePeerDimensions(width, height);
+                    for (IVideoCallback callback : mVideoCallbacks.values()) {
+                        callback.changePeerDimensions(width, height);
+                    }
                 } catch (RemoteException ignored) {
                 }
             }
         }
 
         /**
-         * Invokes callback method defined in In-Call UI.
+         * Invokes callback method defined in listening {@link InCallService} implementations.
          *
          * @param dataUsage The updated data usage.
          */
-        public void changeCallDataUsage(int dataUsage) {
-            if (mVideoCallback != null) {
+        public void changeCallDataUsage(long dataUsage) {
+            if (mVideoCallbacks != null) {
                 try {
-                    mVideoCallback.changeCallDataUsage(dataUsage);
+                    for (IVideoCallback callback : mVideoCallbacks.values()) {
+                        callback.changeCallDataUsage(dataUsage);
+                    }
                 } catch (RemoteException ignored) {
                 }
             }
         }
 
         /**
-         * Invokes callback method defined in In-Call UI.
+         * Invokes callback method defined in listening {@link InCallService} implementations.
          *
          * @param cameraCapabilities The changed camera capabilities.
          */
         public void changeCameraCapabilities(CameraCapabilities cameraCapabilities) {
-            if (mVideoCallback != null) {
+            if (mVideoCallbacks != null) {
                 try {
-                    mVideoCallback.changeCameraCapabilities(cameraCapabilities);
+                    for (IVideoCallback callback : mVideoCallbacks.values()) {
+                        callback.changeCameraCapabilities(cameraCapabilities);
+                    }
+                } catch (RemoteException ignored) {
+                }
+            }
+        }
+
+        /**
+         * Invokes callback method defined in listening {@link InCallService} implementations.
+         *
+         * @param videoQuality The updated video quality.
+         */
+        public void changeVideoQuality(int videoQuality) {
+            if (mVideoCallbacks != null) {
+                try {
+                    for (IVideoCallback callback : mVideoCallbacks.values()) {
+                        callback.changeVideoQuality(videoQuality);
+                    }
                 } catch (RemoteException ignored) {
                 }
             }
@@ -672,6 +852,7 @@
     private DisconnectCause mDisconnectCause;
     private Conference mConference;
     private ConnectionService mConnectionService;
+    private int mCallSubstate;
 
     /**
      * Create a new Connection.
@@ -730,6 +911,21 @@
     }
 
     /**
+     * Returns the call substate of the call.
+     * Valid values: {@link Connection#SUBSTATE_NONE},
+     * {@link Connection#SUBSTATE_AUDIO_CONNECTED_SUSPENDED},
+     * {@link Connection#SUBSTATE_VIDEO_CONNECTED_SUSPENDED},
+     * {@link Connection#SUBSTATE_AVP_RETRY},
+     * {@link Connection#SUBSTATE_MEDIA_PAUSED}.
+     *
+     * @param callSubstate The new call substate.
+     * @hide
+     */
+    public final int getCallSubstate() {
+        return mCallSubstate;
+    }
+
+    /**
      * @return The audio state of the connection, describing how its audio is currently
      *         being routed by the system. This is {@code null} if this Connection
      *         does not directly know about its audio state.
@@ -849,11 +1045,6 @@
         return mConnectionCapabilities;
     }
 
-    /** @hide */
-    @SystemApi @Deprecated public final int getCallCapabilities() {
-        return getConnectionCapabilities();
-    }
-
     /**
      * Sets the value of the {@link #getAddress()} property.
      *
@@ -908,6 +1099,25 @@
     }
 
     /**
+     * Set the call substate for the connection.
+     * Valid values: {@link Connection#SUBSTATE_NONE},
+     * {@link Connection#SUBSTATE_AUDIO_CONNECTED_SUSPENDED},
+     * {@link Connection#SUBSTATE_VIDEO_CONNECTED_SUSPENDED},
+     * {@link Connection#SUBSTATE_AVP_RETRY},
+     * {@link Connection#SUBSTATE_MEDIA_PAUSED}.
+     *
+     * @param callSubstate The new call substate.
+     * @hide
+     */
+    public final void setCallSubstate(int callSubstate) {
+        Log.d(this, "setCallSubstate %d", callSubstate);
+        mCallSubstate = callSubstate;
+        for (Listener l : mListeners) {
+            l.onCallSubstateChanged(this, mCallSubstate);
+        }
+    }
+
+    /**
      * Sets state to active (e.g., an ongoing connection where two or more parties can actively
      * communicate).
      */
@@ -970,7 +1180,6 @@
         }
     }
 
-    /** @hide */
     public final VideoProvider getVideoProvider() {
         return mVideoProvider;
     }
@@ -1041,11 +1250,6 @@
         }
     }
 
-    /** @hide */
-    @SystemApi @Deprecated public final void setCallCapabilities(int connectionCapabilities) {
-        setConnectionCapabilities(connectionCapabilities);
-    }
-
     /**
      * Sets the connection's capabilities as a bit mask of the {@code CAPABILITY_*} constants.
      *
@@ -1435,6 +1639,7 @@
 
     /**
      * Notifies listeners that a conference call has been started.
+     * @hide
      */
     protected void notifyConferenceStarted() {
         for (Listener l : mListeners) {
diff --git a/telecomm/java/android/telecom/ConnectionRequest.java b/telecomm/java/android/telecom/ConnectionRequest.java
index f691c17..71b481b 100644
--- a/telecomm/java/android/telecom/ConnectionRequest.java
+++ b/telecomm/java/android/telecom/ConnectionRequest.java
@@ -16,7 +16,6 @@
 
 package android.telecom;
 
-import android.annotation.SystemApi;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Parcel;
@@ -25,9 +24,7 @@
 /**
  * Simple data container encapsulating a request to some entity to
  * create a new {@link Connection}.
- * @hide
  */
-@SystemApi
 public final class ConnectionRequest implements Parcelable {
 
     // TODO: Token to limit recursive invocations
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 6ec3fa3..05a0d08 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -17,7 +17,6 @@
 package android.telecom;
 
 import android.annotation.SdkConstant;
-import android.annotation.SystemApi;
 import android.app.Service;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -72,9 +71,7 @@
  * receives call-commands such as answer, reject, hold and disconnect.
  * <p>
  * When there are no more live calls, telecom will unbind from the {@code ConnectionService}.
- * @hide
  */
-@SystemApi
 public abstract class ConnectionService extends Service {
     /**
      * The {@link Intent} that must be declared as handled by the service.
@@ -417,6 +414,21 @@
         }
 
         @Override
+        public void onVideoStateChanged(Conference c, int videoState) {
+            String id = mIdByConference.get(c);
+            Log.d(this, "onVideoStateChanged set video state %d", videoState);
+            mAdapter.setVideoState(id, videoState);
+        }
+
+        @Override
+        public void onVideoProviderChanged(Conference c, Connection.VideoProvider videoProvider) {
+            String id = mIdByConference.get(c);
+            Log.d(this, "onVideoProviderChanged: Connection: %s, VideoProvider: %s", c,
+                    videoProvider);
+            mAdapter.setVideoProvider(id, videoProvider);
+        }
+
+        @Override
         public void onStatusHintsChanged(Conference conference, StatusHints statusHints) {
             String id = mIdByConference.get(conference);
             mAdapter.setStatusHints(id, statusHints);
@@ -514,6 +526,8 @@
         @Override
         public void onVideoProviderChanged(Connection c, Connection.VideoProvider videoProvider) {
             String id = mIdByConnection.get(c);
+            Log.d(this, "onVideoProviderChanged: Connection: %s, VideoProvider: %s", c,
+                    videoProvider);
             mAdapter.setVideoProvider(id, videoProvider);
         }
 
@@ -548,6 +562,13 @@
                 mAdapter.setIsConferenced(id, conferenceId);
             }
         }
+
+        @Override
+        public void onCallSubstateChanged(Connection c, int callSubstate) {
+            String id = mIdByConnection.get(c);
+            Log.d(this, "Adapter set call substate %d", callSubstate);
+            mAdapter.setCallSubstate(id, callSubstate);
+        }
     };
 
     /** {@inheritDoc} */
@@ -617,7 +638,8 @@
                         connection.getAudioModeIsVoip(),
                         connection.getStatusHints(),
                         connection.getDisconnectCause(),
-                        createIdList(connection.getConferenceables())));
+                        createIdList(connection.getConferenceables()),
+                        connection.getCallSubstate()));
     }
 
     private void abort(String callId) {
@@ -877,6 +899,8 @@
      * @param conference The new conference object.
      */
     public final void addConference(Conference conference) {
+        Log.d(this, "addConference: conference=%s", conference);
+
         String id = addConferenceInternal(conference);
         if (id != null) {
             List<String> connectionIds = new ArrayList<>(2);
@@ -890,9 +914,15 @@
                     conference.getState(),
                     conference.getConnectionCapabilities(),
                     connectionIds,
-                    conference.getConnectTimeMillis(),
+                    conference.getVideoProvider() == null ?
+                            null : conference.getVideoProvider().getInterface(),
+                    conference.getVideoState(),
+                    conference.getConnectTimeMillis()
                     conference.getStatusHints());
+
             mAdapter.addConferenceCall(id, parcelableConference);
+            mAdapter.setVideoProvider(id, conference.getVideoProvider());
+            mAdapter.setVideoState(id, conference.getVideoState());
 
             // Go through any child calls and set the parent.
             for (Connection connection : conference.getConnections()) {
@@ -933,7 +963,7 @@
                     connection.getAudioModeIsVoip(),
                     connection.getStatusHints(),
                     connection.getDisconnectCause(),
-                    emptyList);
+                    emptyList, connection.getCallSubstate());
             mAdapter.addExistingConnection(id, parcelableConnection);
         }
     }
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapter.java b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
index d026a28..a410976 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapter.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
@@ -369,4 +369,26 @@
             }
         }
     }
+
+    /**
+     * Set the call substate for the connection.
+     * Valid values: {@link Connection#CALL_SUBSTATE_NONE},
+     * {@link Connection#CALL_SUBSTATE_AUDIO_CONNECTED_SUSPENDED},
+     * {@link Connection#CALL_SUBSTATE_VIDEO_CONNECTED_SUSPENDED},
+     * {@link Connection#CALL_SUBSTATE_AVP_RETRY},
+     * {@link Connection#CALL_SUBSTATE_MEDIA_PAUSED}.
+     *
+     * @param callId The unique ID of the call to set the substate for.
+     * @param callSubstate The new call substate.
+     * @hide
+     */
+    public final void setCallSubstate(String callId, int callSubstate) {
+        Log.v(this, "setCallSubstate: %d", callSubstate);
+        for (IConnectionServiceAdapter adapter : mAdapters) {
+            try {
+                adapter.setCallSubstate(callId, callSubstate);
+            } catch (RemoteException ignored) {
+            }
+        }
+    }
 }
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
index 429f296..5f93789 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
@@ -59,6 +59,7 @@
     private static final int MSG_SET_CONFERENCEABLE_CONNECTIONS = 20;
     private static final int MSG_ADD_EXISTING_CONNECTION = 21;
     private static final int MSG_ON_POST_DIAL_CHAR = 22;
+    private static final int MSG_SET_CALL_SUBSTATE = 23;
 
     private final IConnectionServiceAdapter mDelegate;
 
@@ -220,6 +221,10 @@
                     }
                     break;
                 }
+                case MSG_SET_CALL_SUBSTATE: {
+                    mDelegate.setCallSubstate((String) msg.obj, msg.arg1);
+                    break;
+                }
             }
         }
     };
@@ -384,6 +389,12 @@
             args.arg2 = connection;
             mHandler.obtainMessage(MSG_ADD_EXISTING_CONNECTION, args).sendToTarget();
         }
+
+        @Override
+        public void setCallSubstate(String connectionId, int callSubstate) {
+            mHandler.obtainMessage(MSG_SET_CALL_SUBSTATE, callSubstate, 0,
+                connectionId).sendToTarget();
+        }
     };
 
     public ConnectionServiceAdapterServant(IConnectionServiceAdapter delegate) {
diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java
index 130d676..73bcd0c 100644
--- a/telecomm/java/android/telecom/DisconnectCause.java
+++ b/telecomm/java/android/telecom/DisconnectCause.java
@@ -16,7 +16,6 @@
 
 package android.telecom;
 
-import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.media.ToneGenerator;
@@ -30,9 +29,7 @@
  * user. It is the responsibility of the {@link ConnectionService} to provide localized versions of
  * the label and description. It also may contain a reason for the disconnect, which is intended for
  * logging and not for display to the user.
- * @hide
  */
-@SystemApi
 public final class DisconnectCause implements Parcelable {
 
     /** Disconnected because of an unknown or unspecified reason. */
diff --git a/telecomm/java/android/telecom/GatewayInfo.java b/telecomm/java/android/telecom/GatewayInfo.java
index 5b8e4ab..928570e 100644
--- a/telecomm/java/android/telecom/GatewayInfo.java
+++ b/telecomm/java/android/telecom/GatewayInfo.java
@@ -16,7 +16,6 @@
 
 package android.telecom;
 
-import android.annotation.SystemApi;
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -34,17 +33,13 @@
  * <li> Call the appropriate gateway address.
  * <li> Display information about how the call is being routed to the user.
  * </ol>
- * @hide
  */
-@SystemApi
 public class GatewayInfo implements Parcelable {
 
     private final String mGatewayProviderPackageName;
     private final Uri mGatewayAddress;
     private final Uri mOriginalAddress;
 
-    /** @hide */
-    @SystemApi
     public GatewayInfo(String packageName, Uri gatewayUri, Uri originalAddress) {
         mGatewayProviderPackageName = packageName;
         mGatewayAddress = gatewayUri;
diff --git a/telecomm/java/android/telecom/IConferenceable.java b/telecomm/java/android/telecom/IConferenceable.java
index 095d7cb..a9be20b 100644
--- a/telecomm/java/android/telecom/IConferenceable.java
+++ b/telecomm/java/android/telecom/IConferenceable.java
@@ -16,16 +16,11 @@
 
 package android.telecom;
 
-import android.annotation.SystemApi;
-
 /**
  * Interface used to identify entities with which another entity can participate in a conference
  * call with.  The {@link ConnectionService} implementation will only recognize
  * {@link IConferenceable}s which are {@link Connection}s or {@link Conference}s.
- *
- * @hide
  */
-@SystemApi
 public interface IConferenceable {
 
 }
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index a85e84d..6691d11 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -16,7 +16,6 @@
 
 package android.telecom;
 
-import android.annotation.SystemApi;
 import android.annotation.SdkConstant;
 import android.app.Service;
 import android.content.Intent;
@@ -36,10 +35,7 @@
  * This service is implemented by any app that wishes to provide the user-interface for managing
  * phone calls. Telecom binds to this service while there exists a live (active or incoming) call,
  * and uses it to notify the in-call app of any live and and recently disconnected calls.
- *
- * {@hide}
  */
-@SystemApi
 public abstract class InCallService extends Service {
 
     /**
@@ -177,7 +173,7 @@
      *         if the {@code InCallService} is not in a state where it has an associated
      *         {@code Phone}.
      */
-    public Phone getPhone() {
+    public final Phone getPhone() {
         return mPhone;
     }
 
@@ -205,7 +201,6 @@
 
     /**
      * Class to invoke functionality related to video calls.
-     * @hide
      */
     public static abstract class VideoCall {
 
@@ -218,6 +213,11 @@
         public abstract void setVideoCallListener(VideoCall.Listener videoCallListener);
 
         /**
+         * Clears the video call listener set via {@link #setVideoCallListener(Listener)}.
+         */
+        public abstract void removeVideoCallListener();
+
+        /**
          * Sets the camera to be used for video recording in a video call.
          *
          * @param cameraId The id of the camera.
@@ -302,7 +302,6 @@
 
         /**
          * Listener class which invokes callbacks after video call actions occur.
-         * @hide
          */
         public static abstract class Listener {
             /**
@@ -361,11 +360,18 @@
             public abstract void onPeerDimensionsChanged(int width, int height);
 
             /**
+             * Handles a change to the video quality.
+             *
+             * @param videoQuality  The updated peer video quality.
+             */
+            public abstract void onVideoQualityChanged(int videoQuality);
+
+            /**
              * Handles an update to the total data used for the current session.
              *
              * @param dataUsage The updated data usage.
              */
-            public abstract void onCallDataUsageChanged(int dataUsage);
+            public abstract void onCallDataUsageChanged(long dataUsage);
 
             /**
              * Handles a change in camera capabilities.
diff --git a/telecomm/java/android/telecom/ParcelableCall.java b/telecomm/java/android/telecom/ParcelableCall.java
index c5c3d11..bf6c318 100644
--- a/telecomm/java/android/telecom/ParcelableCall.java
+++ b/telecomm/java/android/telecom/ParcelableCall.java
@@ -46,6 +46,7 @@
     private final int mCallerDisplayNamePresentation;
     private final GatewayInfo mGatewayInfo;
     private final PhoneAccountHandle mAccountHandle;
+    private final boolean mIsVideoCallProviderChanged;
     private final IVideoProvider mVideoCallProvider;
     private InCallService.VideoCall mVideoCall;
     private final String mParentCallId;
@@ -54,6 +55,7 @@
     private final int mVideoState;
     private final List<String> mConferenceableCallIds;
     private final Bundle mExtras;
+    private int mCallSubstate;
 
     public ParcelableCall(
             String id,
@@ -69,13 +71,15 @@
             int callerDisplayNamePresentation,
             GatewayInfo gatewayInfo,
             PhoneAccountHandle accountHandle,
+            boolean isVideoCallProviderChanged,
             IVideoProvider videoCallProvider,
             String parentCallId,
             List<String> childCallIds,
             StatusHints statusHints,
             int videoState,
             List<String> conferenceableCallIds,
-            Bundle extras) {
+            Bundle extras,
+            int callSubstate) {
         mId = id;
         mState = state;
         mDisconnectCause = disconnectCause;
@@ -89,6 +93,7 @@
         mCallerDisplayNamePresentation = callerDisplayNamePresentation;
         mGatewayInfo = gatewayInfo;
         mAccountHandle = accountHandle;
+        mIsVideoCallProviderChanged = isVideoCallProviderChanged;
         mVideoCallProvider = videoCallProvider;
         mParentCallId = parentCallId;
         mChildCallIds = childCallIds;
@@ -96,6 +101,7 @@
         mVideoState = videoState;
         mConferenceableCallIds = Collections.unmodifiableList(conferenceableCallIds);
         mExtras = extras;
+        mCallSubstate = callSubstate;
     }
 
     /** The unique ID of the call. */
@@ -232,6 +238,26 @@
         return mExtras;
     }
 
+    /**
+     * The call substate.
+     * @return The substate of the call.
+     */
+    public int getCallSubstate() {
+        return mCallSubstate;
+    }
+
+    /**
+     * Indicates to the receiver of the {@link ParcelableCall} whether a change has occurred in the
+     * {@link android.telecom.InCallService.VideoCall} associated with this call.  Since
+     * {@link #getVideoCall()} creates a new {@link VideoCallImpl}, it is useful to know whether
+     * the provider has changed (which can influence whether it is accessed).
+     *
+     * @return {@code true} if the video call changed, {@code false} otherwise.
+     */
+    public boolean isVideoCallProviderChanged() {
+        return mIsVideoCallProviderChanged;
+    }
+
     /** Responsible for creating ParcelableCall objects for deserialized Parcels. */
     public static final Parcelable.Creator<ParcelableCall> CREATOR =
             new Parcelable.Creator<ParcelableCall> () {
@@ -252,6 +278,7 @@
             int callerDisplayNamePresentation = source.readInt();
             GatewayInfo gatewayInfo = source.readParcelable(classLoader);
             PhoneAccountHandle accountHandle = source.readParcelable(classLoader);
+            boolean isVideoCallProviderChanged = source.readByte() == 1;
             IVideoProvider videoCallProvider =
                     IVideoProvider.Stub.asInterface(source.readStrongBinder());
             String parentCallId = source.readString();
@@ -262,6 +289,7 @@
             List<String> conferenceableCallIds = new ArrayList<>();
             source.readList(conferenceableCallIds, classLoader);
             Bundle extras = source.readParcelable(classLoader);
+            int callSubstate = source.readInt();
             return new ParcelableCall(
                     id,
                     state,
@@ -276,13 +304,15 @@
                     callerDisplayNamePresentation,
                     gatewayInfo,
                     accountHandle,
+                    isVideoCallProviderChanged,
                     videoCallProvider,
                     parentCallId,
                     childCallIds,
                     statusHints,
                     videoState,
                     conferenceableCallIds,
-                    extras);
+                    extras,
+                    callSubstate);
         }
 
         @Override
@@ -313,6 +343,7 @@
         destination.writeInt(mCallerDisplayNamePresentation);
         destination.writeParcelable(mGatewayInfo, 0);
         destination.writeParcelable(mAccountHandle, 0);
+        destination.writeByte((byte) (mIsVideoCallProviderChanged ? 1 : 0));
         destination.writeStrongBinder(
                 mVideoCallProvider != null ? mVideoCallProvider.asBinder() : null);
         destination.writeString(mParentCallId);
@@ -321,6 +352,7 @@
         destination.writeInt(mVideoState);
         destination.writeList(mConferenceableCallIds);
         destination.writeParcelable(mExtras, 0);
+        destination.writeInt(mCallSubstate);
     }
 
     @Override
diff --git a/telecomm/java/android/telecom/ParcelableConference.java b/telecomm/java/android/telecom/ParcelableConference.java
index d127855..e54e79d 100644
--- a/telecomm/java/android/telecom/ParcelableConference.java
+++ b/telecomm/java/android/telecom/ParcelableConference.java
@@ -22,6 +22,8 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import com.android.internal.telecom.IVideoProvider;
+
 /**
  * A parcelable representation of a conference connection.
  * @hide
@@ -33,6 +35,8 @@
     private int mConnectionCapabilities;
     private List<String> mConnectionIds;
     private long mConnectTimeMillis = Conference.CONNECT_TIME_NOT_SPECIFIED;
+    private final IVideoProvider mVideoProvider;
+    private final int mVideoState;
     private StatusHints mStatusHints;
 
     public ParcelableConference(
@@ -40,12 +44,17 @@
             int state,
             int connectionCapabilities,
             List<String> connectionIds,
+            IVideoProvider videoProvider,
+            int videoState,
             long connectTimeMillis,
             StatusHints statusHints) {
         mPhoneAccount = phoneAccount;
         mState = state;
         mConnectionCapabilities = connectionCapabilities;
         mConnectionIds = connectionIds;
+        mConnectTimeMillis = Conference.CONNECT_TIME_NOT_SPECIFIED;
+        mVideoProvider = videoProvider;
+        mVideoState = videoState;
         mConnectTimeMillis = connectTimeMillis;
         mStatusHints = statusHints;
     }
@@ -63,6 +72,10 @@
                 .append(mConnectTimeMillis)
                 .append(", children: ")
                 .append(mConnectionIds)
+                .append(", VideoState: ")
+                .append(mVideoState)
+                .append(", VideoProvider: ")
+                .append(mVideoProvider)
                 .toString();
     }
 
@@ -85,6 +98,13 @@
     public long getConnectTimeMillis() {
         return mConnectTimeMillis;
     }
+    public IVideoProvider getVideoProvider() {
+        return mVideoProvider;
+    }
+
+    public int getVideoState() {
+        return mVideoState;
+    }
 
     public StatusHints getStatusHints() {
         return mStatusHints;
@@ -103,8 +123,12 @@
             long connectTimeMillis = source.readLong();
             StatusHints statusHints = source.readParcelable(classLoader);
 
+            IVideoProvider videoCallProvider =
+                    IVideoProvider.Stub.asInterface(source.readStrongBinder());
+            int videoState = source.readInt();
+
             return new ParcelableConference(phoneAccount, state, capabilities, connectionIds,
-                    connectTimeMillis, statusHints);
+                    videoCallProvider, videoState, connectTimeMillis, statusHints);
         }
 
         @Override
@@ -127,6 +151,9 @@
         destination.writeInt(mConnectionCapabilities);
         destination.writeList(mConnectionIds);
         destination.writeLong(mConnectTimeMillis);
+        destination.writeStrongBinder(
+                mVideoProvider != null ? mVideoProvider.asBinder() : null);
+        destination.writeInt(mVideoState);
         destination.writeParcelable(mStatusHints, 0);
     }
 }
diff --git a/telecomm/java/android/telecom/ParcelableConnection.java b/telecomm/java/android/telecom/ParcelableConnection.java
index 552e250..b60b99d 100644
--- a/telecomm/java/android/telecom/ParcelableConnection.java
+++ b/telecomm/java/android/telecom/ParcelableConnection.java
@@ -46,6 +46,7 @@
     private final StatusHints mStatusHints;
     private final DisconnectCause mDisconnectCause;
     private final List<String> mConferenceableConnectionIds;
+    private final int mCallSubstate;
 
     /** @hide */
     public ParcelableConnection(
@@ -62,7 +63,8 @@
             boolean isVoipAudioMode,
             StatusHints statusHints,
             DisconnectCause disconnectCause,
-            List<String> conferenceableConnectionIds) {
+            List<String> conferenceableConnectionIds,
+            int callSubstate) {
         mPhoneAccount = phoneAccount;
         mState = state;
         mConnectionCapabilities = capabilities;
@@ -77,6 +79,7 @@
         mStatusHints = statusHints;
         mDisconnectCause = disconnectCause;
         this.mConferenceableConnectionIds = conferenceableConnectionIds;
+        mCallSubstate = callSubstate;
     }
 
     public PhoneAccountHandle getPhoneAccount() {
@@ -136,6 +139,10 @@
         return mConferenceableConnectionIds;
     }
 
+    public int getCallSubstate() {
+        return mCallSubstate;
+    }
+
     @Override
     public String toString() {
         return new StringBuilder()
@@ -170,6 +177,7 @@
             DisconnectCause disconnectCause = source.readParcelable(classLoader);
             List<String> conferenceableConnectionIds = new ArrayList<>();
             source.readStringList(conferenceableConnectionIds);
+            int callSubstate = source.readInt();
 
             return new ParcelableConnection(
                     phoneAccount,
@@ -185,7 +193,8 @@
                     audioModeIsVoip,
                     statusHints,
                     disconnectCause,
-                    conferenceableConnectionIds);
+                    conferenceableConnectionIds,
+                    callSubstate);
         }
 
         @Override
@@ -218,5 +227,6 @@
         destination.writeParcelable(mStatusHints, 0);
         destination.writeParcelable(mDisconnectCause, 0);
         destination.writeStringList(mConferenceableConnectionIds);
+        destination.writeInt(mCallSubstate);
     }
 }
diff --git a/telecomm/java/android/telecom/Phone.java b/telecomm/java/android/telecom/Phone.java
index 6344181..d456dfa 100644
--- a/telecomm/java/android/telecom/Phone.java
+++ b/telecomm/java/android/telecom/Phone.java
@@ -16,7 +16,6 @@
 
 package android.telecom;
 
-import android.annotation.SystemApi;
 import android.util.ArrayMap;
 
 import java.util.Collections;
@@ -27,10 +26,7 @@
 
 /**
  * A unified virtual device providing a means of voice (and other) communication on a device.
- *
- * {@hide}
  */
-@SystemApi
 public final class Phone {
 
     public abstract static class Listener {
@@ -123,6 +119,11 @@
     final void internalRemoveCall(Call call) {
         mCallByTelecomCallId.remove(call.internalGetCallId());
         mCalls.remove(call);
+
+        InCallService.VideoCall videoCall = call.getVideoCall();
+        if (videoCall != null) {
+            videoCall.removeVideoCallListener();
+        }
         fireCallRemoved(call);
     }
 
@@ -175,6 +176,10 @@
      */
     final void destroy() {
         for (Call call : mCalls) {
+            InCallService.VideoCall videoCall = call.getVideoCall();
+            if (videoCall != null) {
+                videoCall.removeVideoCallListener();
+            }
             if (call.getState() != Call.STATE_DISCONNECTED) {
                 call.internalSetDisconnected();
             }
@@ -244,6 +249,8 @@
      * become active, and the touch screen and display will be turned off when the user's face
      * is detected to be in close proximity to the screen. This operation is a no-op on devices
      * that do not have a proximity sensor.
+     *
+     * @hide
      */
     public final void setProximitySensorOn() {
         mInCallAdapter.turnProximitySensorOn();
@@ -257,6 +264,8 @@
      * @param screenOnImmediately If true, the screen will be turned on immediately if it was
      * previously off. Otherwise, the screen will only be turned on after the proximity sensor
      * is no longer triggered.
+     *
+     * @hide
      */
     public final void setProximitySensorOff(boolean screenOnImmediately) {
         mInCallAdapter.turnProximitySensorOff(screenOnImmediately);
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index 052a481..00d170f 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -40,15 +40,13 @@
 /**
  * Represents a distinct method to place or receive a phone call. Apps which can place calls and
  * want those calls to be integrated into the dialer and in-call UI should build an instance of
- * this class and register it with the system using {@link TelecomManager#registerPhoneAccount}.
+ * this class and register it with the system using {@link TelecomManager}.
  * <p>
  * {@link TelecomManager} uses registered {@link PhoneAccount}s to present the user with
  * alternative options when placing a phone call. When building a {@link PhoneAccount}, the app
- * should supply a valid {@link PhoneAccountHandle} that references the {@link ConnectionService}
+ * should supply a valid {@link PhoneAccountHandle} that references the connection service
  * implementation Telecom will use to interact with the app.
- * @hide
  */
-@SystemApi
 public class PhoneAccount implements Parcelable {
 
     /**
@@ -74,7 +72,6 @@
      * <p>
      * See {@link #getCapabilities}
      * <p>
-     * {@hide}
      */
     public static final int CAPABILITY_CALL_PROVIDER = 0x2;
 
@@ -94,6 +91,7 @@
      * See {@link #getCapabilities}
      * @hide
      */
+    @SystemApi
     public static final int CAPABILITY_VIDEO_CALLING = 0x8;
 
     /**
@@ -111,6 +109,7 @@
      * See {@link #getCapabilities}
      * @hide
      */
+    @SystemApi
     public static final int CAPABILITY_MULTI_USER = 0x20;
 
     /**
@@ -202,12 +201,6 @@
             mSupportedUriSchemes.addAll(phoneAccount.getSupportedUriSchemes());
         }
 
-        /** @hide */
-        public Builder setAccountHandle(PhoneAccountHandle accountHandle) {
-            mAccountHandle = accountHandle;
-            return this;
-        }
-
         /**
          * Sets the address. See {@link PhoneAccount#getAddress}.
          *
@@ -331,7 +324,6 @@
          *
          * @param uriScheme The URI scheme.
          * @return The builder.
-         * @hide
          */
         public Builder addSupportedUriScheme(String uriScheme) {
             if (!TextUtils.isEmpty(uriScheme) && !mSupportedUriSchemes.contains(uriScheme)) {
@@ -421,7 +413,6 @@
      * Returns a builder initialized with the current {@link PhoneAccount} instance.
      *
      * @return The builder.
-     * @hide
      */
     public Builder toBuilder() { return new Builder(this); }
 
diff --git a/telecomm/java/android/telecom/PhoneAccountHandle.java b/telecomm/java/android/telecom/PhoneAccountHandle.java
index 97af41a..60917b2 100644
--- a/telecomm/java/android/telecom/PhoneAccountHandle.java
+++ b/telecomm/java/android/telecom/PhoneAccountHandle.java
@@ -16,7 +16,6 @@
 
 package android.telecom;
 
-import android.annotation.SystemApi;
 import android.content.ComponentName;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -29,16 +28,13 @@
  * The unique identifier for a {@link PhoneAccount}. A {@code PhoneAccountHandle} is made of two
  * parts:
  * <ul>
- *  <li>The component name of the associated {@link ConnectionService}.</li>
+ *  <li>The component name of the associated connection service.</li>
  *  <li>A string identifier that is unique across {@code PhoneAccountHandle}s with the same
  *      component name.</li>
  * </ul>
  *
- * See {@link PhoneAccount},
- * {@link TelecomManager#registerPhoneAccount TelecomManager.registerPhoneAccount}.
- * @hide
+ * See {@link PhoneAccount}, {@link TelecomManager}.
  */
-@SystemApi
 public class PhoneAccountHandle implements Parcelable {
     private final ComponentName mComponentName;
     private final String mId;
@@ -50,7 +46,6 @@
         this(componentName, id, Process.myUserHandle());
     }
 
-    /** @hide */
     public PhoneAccountHandle(
             ComponentName componentName,
             String id,
@@ -61,8 +56,8 @@
     }
 
     /**
-     * The {@code ComponentName} of the {@link android.telecom.ConnectionService} which is
-     * responsible for making phone calls using this {@code PhoneAccountHandle}.
+     * The {@code ComponentName} of the connection service which is responsible for making phone
+     * calls using this {@code PhoneAccountHandle}.
      *
      * @return A suitable {@code ComponentName}.
      */
@@ -72,9 +67,9 @@
 
     /**
      * A string that uniquely distinguishes this particular {@code PhoneAccountHandle} from all the
-     * others supported by the {@link ConnectionService} that created it.
+     * others supported by the connection service that created it.
      * <p>
-     * A {@code ConnectionService} must select identifiers that are stable for the lifetime of
+     * A connection service must select identifiers that are stable for the lifetime of
      * their users' relationship with their service, across many Android devices. For example, a
      * good set of identifiers might be the email addresses with which with users registered for
      * their accounts with a particular service. Depending on how a service chooses to operate,
@@ -82,6 +77,9 @@
      * ({@code 0}, {@code 1}, {@code 2}, ...) that are generated locally on each phone and could
      * collide with values generated on other phones or after a data wipe of a given phone.
      *
+     * Important: A non-unique identifier could cause non-deterministic call-log backup/restore
+     * behavior.
+     *
      * @return A service-specific unique identifier for this {@code PhoneAccountHandle}.
      */
     public String getId() {
@@ -90,7 +88,6 @@
 
     /**
      * @return the {@link UserHandle} to use when connecting to this PhoneAccount.
-     * @hide
      */
     public UserHandle getUserHandle() {
         return mUserHandle;
diff --git a/telecomm/java/android/telecom/RemoteConference.java b/telecomm/java/android/telecom/RemoteConference.java
index a8879ae..fba3ee3 100644
--- a/telecomm/java/android/telecom/RemoteConference.java
+++ b/telecomm/java/android/telecom/RemoteConference.java
@@ -18,7 +18,6 @@
 
 import com.android.internal.telecom.IConnectionService;
 
-import android.annotation.SystemApi;
 import android.os.RemoteException;
 
 import java.util.ArrayList;
@@ -30,9 +29,7 @@
 
 /**
  * Represents a conference call which can contain any number of {@link Connection} objects.
- * @hide
  */
-@SystemApi
 public final class RemoteConference {
 
     public abstract static class Callback {
@@ -164,11 +161,6 @@
         return mState;
     }
 
-    /** @hide */
-    @Deprecated public final int getCallCapabilities() {
-        return getConnectionCapabilities();
-    }
-
     public final int getConnectionCapabilities() {
         return mConnectionCapabilities;
     }
diff --git a/telecomm/java/android/telecom/RemoteConnection.java b/telecomm/java/android/telecom/RemoteConnection.java
index 486691f..eec8076 100644
--- a/telecomm/java/android/telecom/RemoteConnection.java
+++ b/telecomm/java/android/telecom/RemoteConnection.java
@@ -20,7 +20,6 @@
 import com.android.internal.telecom.IVideoCallback;
 import com.android.internal.telecom.IVideoProvider;
 
-import android.annotation.SystemApi;
 import android.net.Uri;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -38,9 +37,7 @@
  *
  * @see ConnectionService#createRemoteOutgoingConnection(PhoneAccountHandle, ConnectionRequest)
  * @see ConnectionService#createRemoteIncomingConnection(PhoneAccountHandle, ConnectionRequest)
- * @hide
  */
-@SystemApi
 public final class RemoteConnection {
 
     public static abstract class Callback {
@@ -73,11 +70,6 @@
          */
         public void onRingbackRequested(RemoteConnection connection, boolean ringback) {}
 
-        /** @hide */
-        @Deprecated public void onCallCapabilitiesChanged(
-                RemoteConnection connection,
-                int callCapabilities) {}
-
         /**
          * Indicates that the call capabilities of this {@code RemoteConnection} have changed.
          * See {@link #getConnectionCapabilities()}.
@@ -161,6 +153,16 @@
         public void onVideoStateChanged(RemoteConnection connection, int videoState) {}
 
         /**
+         * Indicates that the call substate of this {@code RemoteConnection} has changed.
+         * See {@link #getCallSubstate()}.
+         *
+         * @param connection The {@code RemoteConnection} invoking this method.
+         * @param callSubstate The new call substate of the {@code RemoteConnection}.
+         * @hide
+         */
+        public void onCallSubstateChanged(RemoteConnection connection, int callSubstate) {}
+
+        /**
          * Indicates that this {@code RemoteConnection} has been destroyed. No further requests
          * should be made to the {@code RemoteConnection}, and references to it should be cleared.
          *
@@ -223,11 +225,13 @@
 
             public void onPeerDimensionsChanged(VideoProvider videoProvider, int width, int height) {}
 
-            public void onCallDataUsageChanged(VideoProvider videoProvider, int dataUsage) {}
+            public void onCallDataUsageChanged(VideoProvider videoProvider, long dataUsage) {}
 
             public void onCameraCapabilitiesChanged(
                     VideoProvider videoProvider,
                     CameraCapabilities cameraCapabilities) {}
+
+            public void onVideoQualityChanged(VideoProvider videoProvider, int videoQuality) {}
         }
 
         private final IVideoCallback mVideoCallbackDelegate = new IVideoCallback() {
@@ -265,7 +269,7 @@
             }
 
             @Override
-            public void changeCallDataUsage(int dataUsage) {
+            public void changeCallDataUsage(long dataUsage) {
                 for (Listener l : mListeners) {
                     l.onCallDataUsageChanged(VideoProvider.this, dataUsage);
                 }
@@ -279,6 +283,13 @@
             }
 
             @Override
+            public void changeVideoQuality(int videoQuality) {
+                for (Listener l : mListeners) {
+                    l.onVideoQualityChanged(VideoProvider.this, videoQuality);
+                }
+            }
+
+            @Override
             public IBinder asBinder() {
                 return null;
             }
@@ -300,7 +311,7 @@
         public VideoProvider(IVideoProvider videoProviderBinder) {
             mVideoProviderBinder = videoProviderBinder;
             try {
-                mVideoProviderBinder.setVideoCallback(mVideoCallbackServant.getStub().asBinder());
+                mVideoProviderBinder.addVideoCallback(mVideoCallbackServant.getStub().asBinder());
             } catch (RemoteException e) {
             }
         }
@@ -403,6 +414,7 @@
     private boolean mConnected;
     private int mConnectionCapabilities;
     private int mVideoState;
+    private int mCallSubstate;
     private VideoProvider mVideoProvider;
     private boolean mIsVoipAudioMode;
     private StatusHints mStatusHints;
@@ -583,8 +595,16 @@
     }
 
     /**
-     * Obtains the video provider of this {@code RemoteConnection}.
      *
+     * @return The call substate of the {@code RemoteConnection}. See
+     * @hide
+     */
+    public int getCallSubstate() {
+        return mCallSubstate;
+    }
+
+    /**
+     * Obtains the video provider of this {@code RemoteConnection}.
      * @return The video provider associated with this {@code RemoteConnection}.
      * @hide
      */
@@ -842,7 +862,6 @@
         mConnectionCapabilities = connectionCapabilities;
         for (Callback c : mCallbacks) {
             c.onConnectionCapabilitiesChanged(this, connectionCapabilities);
-            c.onCallCapabilitiesChanged(this, connectionCapabilities);
         }
     }
 
@@ -897,6 +916,16 @@
     /**
      * @hide
      */
+    void setCallSubstate(int callSubstate) {
+        mCallSubstate = callSubstate;
+        for (Callback c : mCallbacks) {
+            c.onCallSubstateChanged(this, callSubstate);
+        }
+    }
+
+    /**
+     * @hide
+     */
     void setVideoProvider(VideoProvider videoProvider) {
         mVideoProvider = videoProvider;
         for (Callback c : mCallbacks) {
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
index 43a92cb..179db55 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -79,6 +79,7 @@
                 }
                 connection.setConferenceableConnections(conferenceable);
                 connection.setVideoState(parcel.getVideoState());
+                connection.setCallSubstate(parcel.getCallSubstate());
                 if (connection.getState() == Connection.STATE_DISCONNECTED) {
                     // ... then, if it was created in a disconnected state, that indicates
                     // failure on the providing end, so immediately mark it destroyed
@@ -306,6 +307,12 @@
 
             mOurConnectionServiceImpl.addRemoteExistingConnection(remoteConnction);
         }
+
+        @Override
+        public void setCallSubstate(String callId, int callSubstate) {
+            findConnectionForAction(callId, "callSubstate")
+                    .setCallSubstate(callSubstate);
+        }
     };
 
     private final ConnectionServiceAdapterServant mServant =
diff --git a/telecomm/java/android/telecom/StatusHints.java b/telecomm/java/android/telecom/StatusHints.java
index dd3a639..a32eae7 100644
--- a/telecomm/java/android/telecom/StatusHints.java
+++ b/telecomm/java/android/telecom/StatusHints.java
@@ -16,7 +16,6 @@
 
 package android.telecom;
 
-import android.annotation.SystemApi;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
@@ -30,9 +29,7 @@
 
 /**
  * Contains status label and icon displayed in the in-call UI.
- * @hide
  */
-@SystemApi
 public final class StatusHints implements Parcelable {
 
     private final ComponentName mPackageName;
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 1a6b292..2ea84be 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -55,8 +55,6 @@
      * Input: get*Extra field {@link #EXTRA_PHONE_ACCOUNT_HANDLE} contains the component name of the
      * {@link android.telecom.ConnectionService} that Telecom should bind to. Telecom will then
      * ask the connection service for more information about the call prior to showing any UI.
-     *
-     * @hide
      */
     public static final String ACTION_INCOMING_CALL = "android.telecom.action.INCOMING_CALL";
 
@@ -85,11 +83,18 @@
     /**
      * The {@link android.content.Intent} action used to show the settings page used to configure
      * {@link PhoneAccount} preferences.
+     */
+    public static final String ACTION_CHANGE_PHONE_ACCOUNTS =
+            "android.telecom.action.CHANGE_PHONE_ACCOUNTS";
+
+    /**
+     * The {@link android.content.Intent} action used indicate that a new phone account was
+     * just registered.
      * @hide
      */
     @SystemApi
-    public static final String ACTION_CHANGE_PHONE_ACCOUNTS =
-            "android.telecom.action.CHANGE_PHONE_ACCOUNTS";
+    public static final String ACTION_PHONE_ACCOUNT_REGISTERED =
+            "android.telecom.action.PHONE_ACCOUNT_REGISTERED";
 
     /**
      * Optional extra for {@link android.content.Intent#ACTION_CALL} containing a boolean that
@@ -106,7 +111,6 @@
      * {@link VideoProfile.VideoState#BIDIRECTIONAL},
      * {@link VideoProfile.VideoState#RX_ENABLED},
      * {@link VideoProfile.VideoState#TX_ENABLED}.
-     * @hide
      */
     public static final String EXTRA_START_CALL_WITH_VIDEO_STATE =
             "android.telecom.extra.START_CALL_WITH_VIDEO_STATE";
@@ -117,9 +121,7 @@
      * {@link PhoneAccountHandle} to use when making the call.
      * <p class="note">
      * Retrieve with {@link android.content.Intent#getParcelableExtra(String)}.
-     * @hide
      */
-    @SystemApi
     public static final String EXTRA_PHONE_ACCOUNT_HANDLE =
             "android.telecom.extra.PHONE_ACCOUNT_HANDLE";
 
@@ -127,10 +129,7 @@
      * Optional extra for {@link #ACTION_INCOMING_CALL} containing a {@link Bundle} which contains
      * metadata about the call. This {@link Bundle} will be returned to the
      * {@link ConnectionService}.
-     *
-     * @hide
      */
-    @SystemApi
     public static final String EXTRA_INCOMING_CALL_EXTRAS =
             "android.telecom.extra.INCOMING_CALL_EXTRAS";
 
@@ -139,10 +138,7 @@
      * {@link android.content.Intent#ACTION_DIAL} {@code Intent} containing a {@link Bundle}
      * which contains metadata about the call. This {@link Bundle} will be saved into
      * {@code Call.Details}.
-     *
-     * @hide
      */
-    @SystemApi
     public static final String EXTRA_OUTGOING_CALL_EXTRAS =
             "android.telecom.extra.OUTGOING_CALL_EXTRAS";
 
@@ -206,9 +202,7 @@
      * {@link ConnectionService}s which interact with {@link RemoteConnection}s should only populate
      * this if the {@link android.telephony.TelephonyManager#getLine1Number()} value, as that is the
      * user's expected caller ID.
-     * @hide
      */
-    @SystemApi
     public static final String EXTRA_CALL_BACK_NUMBER = "android.telecom.extra.CALL_BACK_NUMBER";
 
     /**
@@ -349,9 +343,7 @@
      * @param uriScheme The URI scheme.
      * @return The {@link PhoneAccountHandle} corresponding to the user-chosen default for outgoing
      * phone calls for a specified URI scheme.
-     * @hide
      */
-    @SystemApi
     public PhoneAccountHandle getDefaultOutgoingPhoneAccount(String uriScheme) {
         try {
             if (isServiceConnected()) {
@@ -404,7 +396,6 @@
      * {@code null}, indicating that there currently exists no user-chosen default
      * {@code PhoneAccount}.
      * @return The phone account handle of the current sim call manager.
-     * @hide
      */
     public PhoneAccountHandle getSimCallManager() {
         try {
@@ -503,8 +494,6 @@
      *
      * @see #EXTRA_PHONE_ACCOUNT_HANDLE
      * @return A list of {@code PhoneAccountHandle} objects.
-     *
-     * @hide
      */
     public List<PhoneAccountHandle> getCallCapablePhoneAccounts() {
         try {
@@ -518,19 +507,6 @@
     }
 
     /**
-     * Determine whether the device has more than one account registered that can make and receive
-     * phone calls.
-     *
-     * @return {@code true} if the device has more than one account registered and {@code false}
-     * otherwise.
-     * @hide
-     */
-    @SystemApi
-    public boolean hasMultipleCallCapableAccounts() {
-        return getCallCapablePhoneAccounts().size() > 1;
-    }
-
-    /**
      *  Returns a list of all {@link PhoneAccount}s registered for the calling package.
      *
      * @return A list of {@code PhoneAccountHandle} objects.
@@ -554,9 +530,7 @@
      *
      * @param account The {@link PhoneAccountHandle}.
      * @return The {@link PhoneAccount} object.
-     * @hide
      */
-    @SystemApi
     public PhoneAccount getPhoneAccount(PhoneAccountHandle account) {
         try {
             if (isServiceConnected()) {
@@ -635,10 +609,7 @@
      * {@link PhoneAccountHandle#getComponentName()} does not match the package name of the app.
      *
      * @param account The complete {@link PhoneAccount}.
-     *
-     * @hide
      */
-    @SystemApi
     public void registerPhoneAccount(PhoneAccount account) {
         try {
             if (isServiceConnected()) {
@@ -653,9 +624,7 @@
      * Remove a {@link PhoneAccount} registration from the system.
      *
      * @param accountHandle A {@link PhoneAccountHandle} for the {@link PhoneAccount} to unregister.
-     * @hide
      */
-    @SystemApi
     public void unregisterPhoneAccount(PhoneAccountHandle accountHandle) {
         try {
             if (isServiceConnected()) {
@@ -671,6 +640,15 @@
      * @hide
      */
     @SystemApi
+    public void clearPhoneAccounts() {
+        clearAccounts();
+    }
+    /**
+     * Remove all Accounts that belong to the calling package from the system.
+     * @deprecated Use {@link #clearPhoneAccounts()} instead.
+     * @hide
+     */
+    @SystemApi
     public void clearAccounts() {
         try {
             if (isServiceConnected()) {
@@ -716,10 +694,7 @@
      *
      * @param accountHandle The handle for the account to check the voicemail number against
      * @param number The number to look up.
-     *
-     * @hide
      */
-    @SystemApi
     public boolean isVoiceMailNumber(PhoneAccountHandle accountHandle, String number) {
         try {
             if (isServiceConnected()) {
@@ -732,23 +707,21 @@
     }
 
     /**
-     * Return whether a given phone account has a voicemail number configured.
+     * Return the voicemail number for a given phone account.
      *
-     * @param accountHandle The handle for the account to check for a voicemail number.
-     * @return {@code true} If the given phone account has a voicemail number.
-     *
-     * @hide
+     * @param accountHandle The handle for the phone account.
+     * @return The voicemail number for the phone account, and {@code null} if one has not been
+     *         configured.
      */
-    @SystemApi
-    public boolean hasVoiceMailNumber(PhoneAccountHandle accountHandle) {
+    public String getVoiceMailNumber(PhoneAccountHandle accountHandle) {
         try {
             if (isServiceConnected()) {
-                return getTelecomService().hasVoiceMailNumber(accountHandle);
+                return getTelecomService().getVoiceMailNumber(accountHandle);
             }
         } catch (RemoteException e) {
             Log.e(TAG, "RemoteException calling ITelecomService#hasVoiceMailNumber.", e);
         }
-        return false;
+        return null;
     }
 
     /**
@@ -756,10 +729,7 @@
      *
      * @param accountHandle The handle for the account retrieve a number for.
      * @return A string representation of the line 1 phone number.
-     *
-     * @hide
      */
-    @SystemApi
     public String getLine1Number(PhoneAccountHandle accountHandle) {
         try {
             if (isServiceConnected()) {
@@ -869,10 +839,7 @@
 
     /**
      * Silences the ringer if a ringing call exists.
-     *
-     * @hide
      */
-    @SystemApi
     public void silenceRinger() {
         try {
             if (isServiceConnected()) {
@@ -933,9 +900,7 @@
      *            {@link #registerPhoneAccount}.
      * @param extras A bundle that will be passed through to
      *            {@link ConnectionService#onCreateIncomingConnection}.
-     * @hide
      */
-    @SystemApi
     public void addNewIncomingCall(PhoneAccountHandle phoneAccount, Bundle extras) {
         try {
             if (isServiceConnected()) {
@@ -1005,10 +970,8 @@
      * @param accountHandle The handle for the account the MMI code should apply to.
      * @param dialString The digits to dial.
      * @return True if the digits were processed as an MMI code, false otherwise.
-     * @hide
      */
-    @SystemApi
-    public boolean handleMmi(PhoneAccountHandle accountHandle, String dialString) {
+    public boolean handleMmi(String dialString, PhoneAccountHandle accountHandle) {
         ITelecomService service = getTelecomService();
         if (service != null) {
             try {
@@ -1025,9 +988,7 @@
      * {@code null} to return a URI which will use the default account.
      * @return The URI (with the content:// scheme) specific to the specified {@link PhoneAccount}
      * for the the content retrieve.
-     * @hide
      */
-    @SystemApi
     public Uri getAdnUriForPhoneAccount(PhoneAccountHandle accountHandle) {
         ITelecomService service = getTelecomService();
         if (service != null && accountHandle != null) {
diff --git a/telecomm/java/android/telecom/VideoCallImpl.java b/telecomm/java/android/telecom/VideoCallImpl.java
index 925058e..fa2dbb1 100644
--- a/telecomm/java/android/telecom/VideoCallImpl.java
+++ b/telecomm/java/android/telecom/VideoCallImpl.java
@@ -42,6 +42,7 @@
     private static final int MSG_CHANGE_PEER_DIMENSIONS = 4;
     private static final int MSG_CHANGE_CALL_DATA_USAGE = 5;
     private static final int MSG_CHANGE_CAMERA_CAPABILITIES = 6;
+    private static final int MSG_CHANGE_VIDEO_QUALITY = 7;
 
     private final IVideoProvider mVideoProvider;
     private final VideoCallListenerBinder mBinder;
@@ -88,7 +89,12 @@
         }
 
         @Override
-        public void changeCallDataUsage(int dataUsage) {
+        public void changeVideoQuality(int videoQuality) {
+            mHandler.obtainMessage(MSG_CHANGE_VIDEO_QUALITY, videoQuality, 0).sendToTarget();
+        }
+
+        @Override
+        public void changeCallDataUsage(long dataUsage) {
             mHandler.obtainMessage(MSG_CHANGE_CALL_DATA_USAGE, dataUsage).sendToTarget();
         }
 
@@ -139,12 +145,15 @@
                     }
                     break;
                 case MSG_CHANGE_CALL_DATA_USAGE:
-                    mVideoCallListener.onCallDataUsageChanged(msg.arg1);
+                    mVideoCallListener.onCallDataUsageChanged((long) msg.obj);
                     break;
                 case MSG_CHANGE_CAMERA_CAPABILITIES:
                     mVideoCallListener.onCameraCapabilitiesChanged(
                             (CameraCapabilities) msg.obj);
                     break;
+                case MSG_CHANGE_VIDEO_QUALITY:
+                    mVideoCallListener.onVideoQualityChanged(msg.arg1);
+                    break;
                 default:
                     break;
             }
@@ -157,7 +166,7 @@
         mVideoProvider.asBinder().linkToDeath(mDeathRecipient, 0);
 
         mBinder = new VideoCallListenerBinder();
-        mVideoProvider.setVideoCallback(mBinder);
+        mVideoProvider.addVideoCallback(mBinder);
     }
 
     /** {@inheritDoc} */
@@ -166,6 +175,15 @@
     }
 
     /** {@inheritDoc} */
+    public void removeVideoCallListener() {
+        mVideoCallListener = null;
+        try {
+            mVideoProvider.removeVideoCallback(mBinder);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /** {@inheritDoc} */
     public void setCamera(String cameraId) {
         try {
             mVideoProvider.setCamera(cameraId);
@@ -244,4 +262,4 @@
         } catch (RemoteException e) {
         }
     }
-}
\ No newline at end of file
+}
diff --git a/telecomm/java/android/telecom/VideoCallbackServant.java b/telecomm/java/android/telecom/VideoCallbackServant.java
index d0e3f22..1123621 100644
--- a/telecomm/java/android/telecom/VideoCallbackServant.java
+++ b/telecomm/java/android/telecom/VideoCallbackServant.java
@@ -38,6 +38,7 @@
     private static final int MSG_CHANGE_PEER_DIMENSIONS = 3;
     private static final int MSG_CHANGE_CALL_DATA_USAGE = 4;
     private static final int MSG_CHANGE_CAMERA_CAPABILITIES = 5;
+    private static final int MSG_CHANGE_VIDEO_QUALITY = 6;
 
     private final IVideoCallback mDelegate;
 
@@ -90,7 +91,7 @@
                 case MSG_CHANGE_CALL_DATA_USAGE: {
                     SomeArgs args = (SomeArgs) msg.obj;
                     try {
-                        mDelegate.changeCallDataUsage(args.argi1);
+                        mDelegate.changeCallDataUsage((long) args.arg1);
                     } finally {
                         args.recycle();
                     }
@@ -100,6 +101,10 @@
                     mDelegate.changeCameraCapabilities((CameraCapabilities) msg.obj);
                     break;
                 }
+                case MSG_CHANGE_VIDEO_QUALITY: {
+                    mDelegate.changeVideoQuality(msg.arg1);
+                    break;
+                }
             }
         }
     };
@@ -136,9 +141,9 @@
         }
 
         @Override
-        public void changeCallDataUsage(int dataUsage) throws RemoteException {
+        public void changeCallDataUsage(long dataUsage) throws RemoteException {
             SomeArgs args = SomeArgs.obtain();
-            args.argi1 = dataUsage;
+            args.arg1 = dataUsage;
             mHandler.obtainMessage(MSG_CHANGE_CALL_DATA_USAGE, args).sendToTarget();
         }
 
@@ -148,6 +153,11 @@
             mHandler.obtainMessage(MSG_CHANGE_CAMERA_CAPABILITIES, cameraCapabilities)
                     .sendToTarget();
         }
+
+        @Override
+        public void changeVideoQuality(int videoQuality) throws RemoteException {
+            mHandler.obtainMessage(MSG_CHANGE_VIDEO_QUALITY, videoQuality, 0).sendToTarget();
+        }
     };
 
     public VideoCallbackServant(IVideoCallback delegate) {
diff --git a/telecomm/java/android/telecom/VideoProfile.java b/telecomm/java/android/telecom/VideoProfile.java
index f5cb054..2fd438a 100644
--- a/telecomm/java/android/telecom/VideoProfile.java
+++ b/telecomm/java/android/telecom/VideoProfile.java
@@ -21,11 +21,14 @@
 
 /**
  * Represents attributes of video calls.
- *
- * {@hide}
  */
 public class VideoProfile implements Parcelable {
     /**
+     * "Unknown" video quality.
+     * @hide
+     */
+    public static final int QUALITY_UNKNOWN = 0;
+    /**
      * "High" video quality.
      */
     public static final int QUALITY_HIGH = 1;
@@ -181,6 +184,17 @@
         }
 
         /**
+         * Whether the video state is any of the video type
+         * @param videoState The video state.
+         * @hide
+         * @return Returns true if the video state TX or RX or Bidirectional
+         */
+        public static boolean isVideo(int videoState) {
+            return hasState(videoState, TX_ENABLED) || hasState(videoState, RX_ENABLED)
+                    || hasState(videoState, BIDIRECTIONAL);
+        }
+
+        /**
          * Whether the video transmission is enabled.
          * @param videoState The video state.
          * @return Returns true if the video transmission is enabled.
diff --git a/telecomm/java/android/telecom/Voicemail.java b/telecomm/java/android/telecom/Voicemail.java
new file mode 100644
index 0000000..dbec2ad
--- /dev/null
+++ b/telecomm/java/android/telecom/Voicemail.java
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2015 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.telecom;
+
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Represents a single voicemail stored in the voicemail content provider.
+ *
+ * @hide
+ */
+public class Voicemail implements Parcelable {
+    private final Long mTimestamp;
+    private final String mNumber;
+    private final Long mId;
+    private final Long mDuration;
+    private final String mSource;
+    private final String mProviderData;
+    private final Uri mUri;
+    private final Boolean mIsRead;
+    private final Boolean mHasContent;
+
+    private Voicemail(Long timestamp, String number, Long id, Long duration, String source,
+            String providerData, Uri uri, Boolean isRead, Boolean hasContent) {
+        mTimestamp = timestamp;
+        mNumber = number;
+        mId = id;
+        mDuration = duration;
+        mSource = source;
+        mProviderData = providerData;
+        mUri = uri;
+        mIsRead = isRead;
+        mHasContent = hasContent;
+    }
+
+    /**
+     * Create a {@link Builder} for a new {@link Voicemail} to be inserted.
+     * <p>
+     * The number and the timestamp are mandatory for insertion.
+     */
+    public static Builder createForInsertion(long timestamp, String number) {
+        return new Builder().setNumber(number).setTimestamp(timestamp);
+    }
+
+    /**
+     * Builder pattern for creating a {@link Voicemail}. The builder must be created with the
+     * {@link #createForInsertion(long, String)} method.
+     * <p>
+     * This class is <b>not thread safe</b>
+     */
+    public static class Builder {
+        private Long mBuilderTimestamp;
+        private String mBuilderNumber;
+        private Long mBuilderId;
+        private Long mBuilderDuration;
+        private String mBuilderSourcePackage;
+        private String mBuilderSourceData;
+        private Uri mBuilderUri;
+        private Boolean mBuilderIsRead;
+        private boolean mBuilderHasContent;
+
+        /** You should use the correct factory method to construct a builder. */
+        private Builder() {
+        }
+
+        public Builder setNumber(String number) {
+            mBuilderNumber = number;
+            return this;
+        }
+
+        public Builder setTimestamp(long timestamp) {
+            mBuilderTimestamp = timestamp;
+            return this;
+        }
+
+        public Builder setId(long id) {
+            mBuilderId = id;
+            return this;
+        }
+
+        public Builder setDuration(long duration) {
+            mBuilderDuration = duration;
+            return this;
+        }
+
+        public Builder setSourcePackage(String sourcePackage) {
+            mBuilderSourcePackage = sourcePackage;
+            return this;
+        }
+
+        public Builder setSourceData(String sourceData) {
+            mBuilderSourceData = sourceData;
+            return this;
+        }
+
+        public Builder setUri(Uri uri) {
+            mBuilderUri = uri;
+            return this;
+        }
+
+        public Builder setIsRead(boolean isRead) {
+            mBuilderIsRead = isRead;
+            return this;
+        }
+
+        public Builder setHasContent(boolean hasContent) {
+            mBuilderHasContent = hasContent;
+            return this;
+        }
+
+        public Voicemail build() {
+            mBuilderId = mBuilderId == null ? -1 : mBuilderId;
+            mBuilderTimestamp = mBuilderTimestamp == null ? 0 : mBuilderTimestamp;
+            mBuilderDuration = mBuilderDuration == null ? 0: mBuilderDuration;
+            mBuilderIsRead = mBuilderIsRead == null ? false : mBuilderIsRead;
+            return new Voicemail(mBuilderTimestamp, mBuilderNumber, mBuilderId, mBuilderDuration,
+                    mBuilderSourcePackage, mBuilderSourceData, mBuilderUri, mBuilderIsRead,
+                    mBuilderHasContent);
+        }
+    }
+
+    /**
+     * The identifier of the voicemail in the content provider.
+     * <p>
+     * This may be missing in the case of a new {@link Voicemail} that we plan to insert into the
+     * content provider, since until it has been inserted we don't know what id it should have. If
+     * none is specified, we return -1.
+     */
+    public long getId() {
+        return mId;
+    }
+
+    /** The number of the person leaving the voicemail, empty string if unknown, null if not set. */
+    public String getNumber() {
+        return mNumber;
+    }
+
+    /** The timestamp the voicemail was received, in millis since the epoch, zero if not set. */
+    public long getTimestampMillis() {
+        return mTimestamp;
+    }
+
+    /** Gets the duration of the voicemail in millis, or zero if the field is not set. */
+    public long getDuration() {
+        return mDuration;
+    }
+
+    /**
+     * Returns the package name of the source that added this voicemail, or null if this field is
+     * not set.
+     */
+    public String getSourcePackage() {
+        return mSource;
+    }
+
+    /**
+     * Returns the application-specific data type stored with the voicemail, or null if this field
+     * is not set.
+     * <p>
+     * Source data is typically used as an identifier to uniquely identify the voicemail against
+     * the voicemail server. This is likely to be something like the IMAP UID, or some other
+     * server-generated identifying string.
+     */
+    public String getSourceData() {
+        return mProviderData;
+    }
+
+    /**
+     * Gets the Uri that can be used to refer to this voicemail, and to make it play.
+     * <p>
+     * Returns null if we don't know the Uri.
+     */
+    public Uri getUri() {
+        return mUri;
+    }
+
+    /**
+     * Tells us if the voicemail message has been marked as read.
+     * <p>
+     * Always returns false if this field has not been set, i.e. if hasRead() returns false.
+     */
+    public boolean isRead() {
+        return mIsRead;
+    }
+
+    /**
+     * Tells us if there is content stored at the Uri.
+     */
+    public boolean hasContent() {
+        return mHasContent;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeLong(mTimestamp);
+        dest.writeCharSequence(mNumber);
+        dest.writeLong(mId);
+        dest.writeLong(mDuration);
+        dest.writeCharSequence(mSource);
+        dest.writeCharSequence(mProviderData);
+        if (mUri == null) {
+            dest.writeInt(0);
+        } else {
+            dest.writeInt(1);
+            mUri.writeToParcel(dest, flags);
+        }
+        if (mIsRead) {
+            dest.writeInt(1);
+        } else {
+            dest.writeInt(0);
+        }
+        if (mHasContent) {
+            dest.writeInt(1);
+        } else {
+            dest.writeInt(0);
+        }
+    }
+
+    public static final Creator<Voicemail> CREATOR
+            = new Creator<Voicemail>() {
+        @Override
+        public Voicemail createFromParcel(Parcel in) {
+            return new Voicemail(in);
+        }
+
+        @Override
+        public Voicemail[] newArray(int size) {
+            return new Voicemail[size];
+        }
+    };
+
+    private Voicemail(Parcel in) {
+        mTimestamp = in.readLong();
+        mNumber = (String) in.readCharSequence();
+        mId = in.readLong();
+        mDuration = in.readLong();
+        mSource = (String) in.readCharSequence();
+        mProviderData = (String) in.readCharSequence();
+        if (in.readInt() > 0) {
+            mUri = Uri.CREATOR.createFromParcel(in);
+        } else {
+            mUri = null;
+        }
+        mIsRead = in.readInt() > 0 ? true : false;
+        mHasContent = in.readInt() > 0 ? true : false;
+    }
+}
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
index 7e7e9cc..e6c28f3 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
@@ -81,4 +81,6 @@
     void setConferenceableConnections(String callId, in List<String> conferenceableCallIds);
 
     void addExistingConnection(String callId, in ParcelableConnection connection);
+
+    void setCallSubstate(String callId, int callSubstate);
 }
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index d2030f2..35db97f 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -121,9 +121,9 @@
     boolean isVoiceMailNumber(in PhoneAccountHandle accountHandle, String number);
 
     /**
-     * @see TelecomServiceImpl#hasVoiceMailNumber
+     * @see TelecomServiceImpl#getVoiceMailNumber
      */
-    boolean hasVoiceMailNumber(in PhoneAccountHandle accountHandle);
+    String getVoiceMailNumber(in PhoneAccountHandle accountHandle);
 
     /**
      * @see TelecomServiceImpl#getLine1Number
diff --git a/telecomm/java/com/android/internal/telecom/IVideoCallback.aidl b/telecomm/java/com/android/internal/telecom/IVideoCallback.aidl
index f758b60..59f8f0c 100644
--- a/telecomm/java/com/android/internal/telecom/IVideoCallback.aidl
+++ b/telecomm/java/com/android/internal/telecom/IVideoCallback.aidl
@@ -39,7 +39,9 @@
 
     void changePeerDimensions(int width, int height);
 
-    void changeCallDataUsage(int dataUsage);
+    void changeCallDataUsage(long dataUsage);
 
     void changeCameraCapabilities(in CameraCapabilities cameraCapabilities);
+
+    void changeVideoQuality(int videoQuality);
 }
diff --git a/telecomm/java/com/android/internal/telecom/IVideoProvider.aidl b/telecomm/java/com/android/internal/telecom/IVideoProvider.aidl
index e96d9d3..bff3865 100644
--- a/telecomm/java/com/android/internal/telecom/IVideoProvider.aidl
+++ b/telecomm/java/com/android/internal/telecom/IVideoProvider.aidl
@@ -25,7 +25,9 @@
  * @hide
  */
 oneway interface IVideoProvider {
-    void setVideoCallback(IBinder videoCallbackBinder);
+    void addVideoCallback(IBinder videoCallbackBinder);
+
+    void removeVideoCallback(IBinder videoCallbackBinder);
 
     void setCamera(String cameraId);
 
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
new file mode 100644
index 0000000..b6f6888
--- /dev/null
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2015 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.telephony;
+
+import com.android.internal.telephony.ICarrierConfigLoader;
+
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+/**
+ * Provides access to telephony configuration values that are carrier-specific.
+ * <p>
+ * Users should obtain an instance of this class by calling
+ * {@code mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE);}
+ * </p>
+ *
+ * @see Context#getSystemService
+ * @see Context#CARRIER_CONFIG_SERVICE
+ */
+public class CarrierConfigManager {
+
+    /**
+     * This intent is broadcast by the system when carrier config changes.
+     */
+    public static final String
+            ACTION_CARRIER_CONFIG_CHANGED = "android.intent.action.carrier_config_changed";
+
+    /**
+     * Flag specifying whether VoLTE should be available for carrier, independent of carrier
+     * provisioning. If false: hard disabled. If true: then depends on carrier provisioning,
+     * availability, etc.
+     */
+    public static final String BOOL_CARRIER_VOLTE_AVAILABLE = "bool_carrier_volte_available";
+
+    /**
+     * Flag specifying whether VoLTE availability is based on provisioning.
+     */
+    public static final String BOOL_CARRIER_VOLTE_PROVISIONED = "bool_carrier_volte_provisioned";
+
+    /**
+     * Flag specifying whether VoLTE TTY is supported.
+     */
+    public static final String BOOL_CARRIER_VOLTE_TTY_SUPPORTED
+            = "bool_carrier_volte_tty_supported";
+
+    /**
+     * Show APN Settings for some CDMA carriers.
+     */
+    public static final String BOOL_SHOW_APN_SETTING_CDMA = "bool_show_apn_setting_cdma";
+
+    /**
+     * If Voice Radio Technology is RIL_RADIO_TECHNOLOGY_LTE:14 or RIL_RADIO_TECHNOLOGY_UNKNOWN:0
+     * this is the value that should be used instead. A configuration value of
+     * RIL_RADIO_TECHNOLOGY_UNKNOWN:0 means there is no replacement value and that the default
+     * assumption for phone type (GSM) should be used.
+     */
+    public static final String INT_VOLTE_REPLACEMENT_RAT = "int_volte_replacement_rat";
+
+    /* The following 3 fields are related to carrier visual voicemail. */
+
+    /**
+     *  The carrier number MO sms messages are sent to.
+     *
+     *  @hide
+     */
+    @SystemApi
+    public static final String STRING_VVM_DESTINATION_NUMBER = "string_vvm_destination_number";
+
+    /**
+     * The port through which the MO sms messages are sent through.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String SHORT_VVM_PORT_NUMBER = "string_vvm_port_number";
+
+    /**
+     * The type of visual voicemail protocol the carrier adheres to (see below).
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String STRING_VVM_TYPE = "string_vvm_type";
+
+    /* Visual voicemail protocols */
+
+    /**
+     * The OMTP protocol.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final String VVM_TYPE_OMTP = "vvm_type_omtp";
+
+    private final static String TAG = "CarrierConfigManager";
+
+    /** The default value for every variable. */
+    private final static Bundle sDefaults;
+
+    static {
+        sDefaults = new Bundle();
+        sDefaults.putBoolean(BOOL_CARRIER_VOLTE_AVAILABLE, false);
+        sDefaults.putBoolean(BOOL_CARRIER_VOLTE_PROVISIONED, false);
+        sDefaults.putBoolean(BOOL_CARRIER_VOLTE_TTY_SUPPORTED, true);
+        sDefaults.putBoolean(BOOL_SHOW_APN_SETTING_CDMA, false);
+
+        sDefaults.putInt(INT_VOLTE_REPLACEMENT_RAT, 0);
+    }
+
+    /**
+     * Gets the configuration values for a particular subscription, which is associated with a
+     * specific SIM card. If an invalid subId is used, the returned config will contain default
+     * values.
+     *
+     * @param subId the subscription ID, normally obtained from {@link SubscriptionManager}.
+     * @return A {@link Bundle} containing the config for the given subId, or default values for an
+     *         invalid subId.
+     */
+    public Bundle getConfigForSubId(int subId) {
+        try {
+            return getICarrierConfigLoader().getConfigForSubId(subId);
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "Error getting config for subId " + Integer.toString(subId) + ": "
+                    + ex.toString());
+        } catch (NullPointerException ex) {
+            Rlog.e(TAG, "Error getting config for subId " + Integer.toString(subId) + ": "
+                    + ex.toString());
+        }
+        return null;
+    }
+
+    /**
+     * Gets the configuration values for the default subscription.
+     *
+     * @see #getConfigForSubId
+     */
+    public Bundle getConfig() {
+        return getConfigForSubId(SubscriptionManager.getDefaultSubId());
+    }
+
+    /**
+     * Calling this method triggers telephony services to fetch the current carrier configuration.
+     * <p>
+     * Normally this does not need to be called because the platform reloads config on its own. Call
+     * this method if your app wants to update config at an arbitrary moment.
+     * </p>
+     * <p>
+     * This method returns before the reload has completed, and
+     * {@link android.service.carrier.CarrierConfigService#onLoadConfig} will be called from an
+     * arbitrary thread.
+     * </p>
+     */
+    public void reloadCarrierConfigForSubId(int subId) {
+        try {
+            getICarrierConfigLoader().reloadCarrierConfigForSubId(subId);
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "Error reloading config for subId=" + subId + ": " + ex.toString());
+        } catch (NullPointerException ex) {
+            Rlog.e(TAG, "Error reloading config for subId=" + subId + ": " + ex.toString());
+        }
+    }
+
+    /**
+     * Request the carrier config loader to update the cofig for phoneId.
+     *
+     * Depending on simState, the config may be cleared or loaded from config app.
+     * This is only used by SubscriptionInfoUpdater.
+     *
+     * @hide
+     */
+    @SystemApi
+    public void updateConfigForPhoneId(int phoneId, String simState) {
+        try {
+            getICarrierConfigLoader().updateConfigForPhoneId(phoneId, simState);
+        } catch (RemoteException ex) {
+            Rlog.e(TAG, "Error updating config for phoneId=" + phoneId + ": " + ex.toString());
+        } catch (NullPointerException ex) {
+            Rlog.e(TAG, "Error updating config for phoneId=" + phoneId + ": " + ex.toString());
+        }
+    }
+
+    /**
+     * Returns a bundle with the default value for every supported configuration variable.
+     *
+     * @hide
+     */
+    @SystemApi
+    public static Bundle getDefaultConfig() {
+        return sDefaults;
+    }
+
+    /** @hide */
+    private ICarrierConfigLoader getICarrierConfigLoader() {
+        return ICarrierConfigLoader.Stub
+                .asInterface(ServiceManager.getService(Context.CARRIER_CONFIG_SERVICE));
+    }
+}
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index c043659..3572846 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -34,11 +34,11 @@
 import android.text.Spannable;
 import android.text.SpannableStringBuilder;
 import android.text.TextUtils;
-import android.telephony.Rlog;
 import android.text.style.TtsSpan;
 import android.util.SparseIntArray;
 
-import static com.android.internal.telephony.PhoneConstants.SUBSCRIPTION_KEY;
+import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ISO_COUNTRY;
+import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY;
 import static com.android.internal.telephony.TelephonyProperties.PROPERTY_OPERATOR_IDP_STRING;
 
 import java.util.Locale;
@@ -2311,15 +2311,13 @@
      *
      * @param phoneNumber A {@code CharSequence} the entirety of which represents a phone number.
      * @return A {@code CharSequence} with appropriate annotations.
-     *
-     * @hide
      */
-    public static CharSequence ttsSpanAsPhoneNumber(CharSequence phoneNumber) {
+    public static CharSequence getPhoneTtsSpannable(CharSequence phoneNumber) {
         if (phoneNumber == null) {
             return null;
         }
         Spannable spannable = Spannable.Factory.getInstance().newSpannable(phoneNumber);
-        PhoneNumberUtils.ttsSpanAsPhoneNumber(spannable, 0, spannable.length());
+        PhoneNumberUtils.addPhoneTtsSpan(spannable, 0, spannable.length());
         return spannable;
     }
 
@@ -2330,19 +2328,83 @@
      * @param s A {@code Spannable} to annotate.
      * @param start The starting character position of the phone number in {@code s}.
      * @param end The ending character position of the phone number in {@code s}.
-     *
-     * @hide
      */
-    public static void ttsSpanAsPhoneNumber(Spannable s, int start, int end) {
-        s.setSpan(
-                new TtsSpan.TelephoneBuilder()
-                        .setNumberParts(splitAtNonNumerics(s.subSequence(start, end)))
-                        .build(),
+    public static void addPhoneTtsSpan(Spannable s, int start, int end) {
+        s.setSpan(getPhoneTtsSpan(s.subSequence(start, end).toString()),
                 start,
                 end,
                 Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
     }
 
+    /**
+     * Wrap the supplied {@code CharSequence} with a {@code TtsSpan}, annotating it as
+     * containing a phone number in its entirety.
+     *
+     * @param phoneNumber A {@code CharSequence} the entirety of which represents a phone number.
+     * @return A {@code CharSequence} with appropriate annotations.
+     * @deprecated Renamed {@link #getPhoneTtsSpannable}.
+     *
+     * @hide
+     */
+    @Deprecated
+    public static CharSequence ttsSpanAsPhoneNumber(CharSequence phoneNumber) {
+        return getPhoneTtsSpannable(phoneNumber);
+    }
+
+    /**
+     * Attach a {@link TtsSpan} to the supplied {@code Spannable} at the indicated location,
+     * annotating that location as containing a phone number.
+     *
+     * @param s A {@code Spannable} to annotate.
+     * @param start The starting character position of the phone number in {@code s}.
+     * @param end The ending character position of the phone number in {@code s}.
+     *
+     * @deprecated Renamed {@link #addPhoneTtsSpan}.
+     *
+     * @hide
+     */
+    @Deprecated
+    public static void ttsSpanAsPhoneNumber(Spannable s, int start, int end) {
+        addPhoneTtsSpan(s, start, end);
+    }
+
+    /**
+     * Create a {@code TtsSpan} for the supplied {@code String}.
+     *
+     * @param phoneNumberString A {@code String} the entirety of which represents a phone number.
+     * @return A {@code TtsSpan} for {@param phoneNumberString}.
+     */
+    public static TtsSpan getPhoneTtsSpan(String phoneNumberString) {
+        if (phoneNumberString == null) {
+            return null;
+        }
+
+        // Parse the phone number
+        final PhoneNumberUtil phoneNumberUtil = PhoneNumberUtil.getInstance();
+        PhoneNumber phoneNumber = null;
+        try {
+            // Don't supply a defaultRegion so this fails for non-international numbers because
+            // we don't want to TalkBalk to read a country code (e.g. +1) if it is not already
+            // present
+            phoneNumber = phoneNumberUtil.parse(phoneNumberString, /* defaultRegion */ null);
+        } catch (NumberParseException ignored) {
+        }
+
+        // Build a telephone tts span
+        final TtsSpan.TelephoneBuilder builder = new TtsSpan.TelephoneBuilder();
+        if (phoneNumber == null) {
+            // Strip separators otherwise TalkBack will be silent
+            // (this behavior was observed with TalkBalk 4.0.2 from their alpha channel)
+            builder.setNumberParts(splitAtNonNumerics(phoneNumberString));
+        } else {
+            if (phoneNumber.hasCountryCode()) {
+                builder.setCountryCode(Integer.toString(phoneNumber.getCountryCode()));
+            }
+            builder.setNumberParts(Long.toString(phoneNumber.getNationalNumber()));
+        }
+        return builder.build();
+    }
+
     // Split a phone number like "+20(123)-456#" using spaces, ignoring anything that is not
     // a digit, to produce a result like "20 123 456".
     private static String splitAtNonNumerics(CharSequence number) {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index b8de144..d0a1dc2 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -29,6 +29,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
+import android.telecom.PhoneAccount;
 import android.util.Log;
 
 import com.android.internal.telecom.ITelecomService;
@@ -161,7 +162,6 @@
      * Returns 1 for Single standby mode (Single SIM functionality)
      * Returns 2 for Dual standby mode.(Dual SIM functionality)
      */
-    /** {@hide} */
     public int getPhoneCount() {
         int phoneCount = 1;
         switch (getMultiSimConfiguration()) {
@@ -638,7 +638,6 @@
      *
      * @param slotId of which deviceID is returned
      */
-    /** {@hide} */
     public String getDeviceId(int slotId) {
         // FIXME this assumes phoneId == slotId
         try {
@@ -3779,7 +3778,7 @@
 
    /**
     * Returns the IMS Registration Status
-    *@hide
+    * @hide
     */
    public boolean isImsRegistered() {
        try {
@@ -4158,4 +4157,21 @@
                     ServiceState.rilRadioTechnologyToString(type));
         }
     }
+
+    /**
+     * Returns the subscription ID for the given phone account.
+     * @hide
+     */
+    public int getSubIdForPhoneAccount(PhoneAccount phoneAccount) {
+        int retval = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+        try {
+            ITelephony service = getITelephony();
+            if (service != null) {
+                retval = service.getSubIdForPhoneAccount(phoneAccount);
+            }
+        } catch (RemoteException e) {
+        }
+
+        return retval;
+    }
 }
diff --git a/telephony/java/com/android/ims/ImsCallProfile.java b/telephony/java/com/android/ims/ImsCallProfile.java
index 239c16a..604d32d 100644
--- a/telephony/java/com/android/ims/ImsCallProfile.java
+++ b/telephony/java/com/android/ims/ImsCallProfile.java
@@ -270,7 +270,6 @@
         return "{ serviceType=" + mServiceType +
                 ", callType=" + mCallType +
                 ", restrictCause=" + mRestrictCause +
-                //", callExtras=" + mCallExtras.toString() +
                 ", mediaProfile=" + mMediaProfile.toString() + " }";
     }
 
@@ -313,22 +312,31 @@
      * @param callType The call type.
      * @return The video state.
      */
-    public static int getVideoStateFromCallType(int callType) {
-        switch (callType) {
-            case CALL_TYPE_VT_NODIR:
-                return VideoProfile.VideoState.PAUSED |
-                        VideoProfile.VideoState.BIDIRECTIONAL;
+    public static int getVideoStateFromImsCallProfile(ImsCallProfile callProfile) {
+        int videostate = VideoProfile.VideoState.AUDIO_ONLY;
+        switch (callProfile.mCallType) {
             case CALL_TYPE_VT_TX:
-                return VideoProfile.VideoState.TX_ENABLED;
+                videostate = VideoProfile.VideoState.TX_ENABLED;
+                break;
             case CALL_TYPE_VT_RX:
-                return VideoProfile.VideoState.RX_ENABLED;
+                videostate = VideoProfile.VideoState.RX_ENABLED;
+                break;
             case CALL_TYPE_VT:
-                return VideoProfile.VideoState.BIDIRECTIONAL;
+                videostate = VideoProfile.VideoState.BIDIRECTIONAL;
+                break;
             case CALL_TYPE_VOICE:
-                return VideoProfile.VideoState.AUDIO_ONLY;
+                videostate = VideoProfile.VideoState.AUDIO_ONLY;
+                break;
             default:
-                return VideoProfile.VideoState.AUDIO_ONLY;
+                videostate = VideoProfile.VideoState.AUDIO_ONLY;
+                break;
         }
+        if (callProfile.isVideoPaused() && videostate != VideoProfile.VideoState.AUDIO_ONLY) {
+            videostate |= VideoProfile.VideoState.PAUSED;
+        } else {
+            videostate &= ~VideoProfile.VideoState.PAUSED;
+        }
+        return videostate;
     }
 
     /**
@@ -387,6 +395,14 @@
     }
 
     /**
+     * Checks if video call is paused
+     * @return true if call is video paused
+     */
+    public boolean isVideoPaused() {
+        return mMediaProfile.mVideoDirection == ImsStreamMediaProfile.DIRECTION_INACTIVE;
+    }
+
+    /**
      * Determines if a video state is set in a video state bit-mask.
      *
      * @param videoState The video state bit mask.
diff --git a/telephony/java/com/android/ims/ImsConfigListener.aidl b/telephony/java/com/android/ims/ImsConfigListener.aidl
index e827774..64a5015 100644
--- a/telephony/java/com/android/ims/ImsConfigListener.aidl
+++ b/telephony/java/com/android/ims/ImsConfigListener.aidl
@@ -48,4 +48,26 @@
      * @return void.
      */
     void onSetFeatureResponse(int feature, int network, int value, int status);
-}
\ No newline at end of file
+
+    /**
+     * Notifies client the value of the get operation result on the video quality item.
+     *
+     * @param status. as defined in com.android.ims.ImsConfig#OperationStatusConstants.
+     * @param quality. as defined in com.android.ims.ImsConfig#OperationValuesConstants.
+     * @return void
+     *
+     * @throws ImsException if calling the IMS service results in an error.
+     */
+     void onGetVideoQuality(int status, int quality);
+
+    /**
+     * Notifies client the set value operation result for video quality item.
+     * Used by clients that need to be notified the set operation result.
+     *
+     * @param status. as defined in com.android.ims.ImsConfig#OperationStatusConstants.
+     * @return void
+     *
+     * @throws ImsException if calling the IMS service results in an error.
+     */
+     void onSetVideoQuality(int status);
+}
diff --git a/telephony/java/com/android/ims/internal/IImsCallSession.aidl b/telephony/java/com/android/ims/internal/IImsCallSession.aidl
index b1f2d32..9b435dc 100644
--- a/telephony/java/com/android/ims/internal/IImsCallSession.aidl
+++ b/telephony/java/com/android/ims/internal/IImsCallSession.aidl
@@ -255,4 +255,11 @@
      * @return {@code True} if the session is multiparty.
      */
     boolean isMultiparty();
+
+    /**
+     * Gets the call substate for this session.
+     *
+     * @return the call substate for this session.
+     */
+    int getCallSubstate();
 }
diff --git a/telephony/java/com/android/ims/internal/IImsConfig.aidl b/telephony/java/com/android/ims/internal/IImsConfig.aidl
index 441e03e..7324814 100644
--- a/telephony/java/com/android/ims/internal/IImsConfig.aidl
+++ b/telephony/java/com/android/ims/internal/IImsConfig.aidl
@@ -100,4 +100,24 @@
      * @return void
      */
     boolean getVolteProvisioned();
+
+    /**
+     *
+     * Gets the value for ims fature item video quality.
+     *
+     * @param listener. Video quality value returned asynchronously through listener.
+     * @return void
+     */
+    oneway void getVideoQuality(ImsConfigListener listener);
+
+    /**
+     * Sets the value for IMS feature item video quality.
+     *
+     * @param quality, defines the value of video quality.
+     * @param listener, provided if caller needs to be notified for set result.
+     * @return void
+     *
+     * @throws ImsException if calling the IMS service results in an error.
+     */
+     oneway void setVideoQuality(int quality, ImsConfigListener listener);
 }
diff --git a/telephony/java/com/android/ims/internal/IImsVideoCallCallback.aidl b/telephony/java/com/android/ims/internal/IImsVideoCallCallback.aidl
index f867fcba..be8751b 100644
--- a/telephony/java/com/android/ims/internal/IImsVideoCallCallback.aidl
+++ b/telephony/java/com/android/ims/internal/IImsVideoCallCallback.aidl
@@ -41,7 +41,9 @@
 
     void changePeerDimensions(int width, int height);
 
-    void changeCallDataUsage(int dataUsage);
+    void changeCallDataUsage(long dataUsage);
 
     void changeCameraCapabilities(in CameraCapabilities cameraCapabilities);
+
+    void changeVideoQuality(int videoQuality);
 }
diff --git a/telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl b/telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl
new file mode 100644
index 0000000..b5cdd9a2
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2015 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.telephony;
+
+import android.os.Bundle;
+
+/**
+ * Interface used to interact with the CarrierConfigLoader
+ */
+interface ICarrierConfigLoader {
+
+    Bundle getConfigForSubId(int subId);
+
+    void reloadCarrierConfigForSubId(int subId);
+
+    void updateConfigForPhoneId(int phoneId, String simState);
+}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index bb0bd04..d03fa3a 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -18,6 +18,7 @@
 
 import android.content.Intent;
 import android.os.Bundle;
+import android.telecom.PhoneAccount;
 import android.telephony.CellInfo;
 import android.telephony.IccOpenLogicalChannelResponse;
 import android.telephony.NeighboringCellInfo;
@@ -891,4 +892,9 @@
       *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
       */
     String getDeviceId();
+
+    /**
+     * Returns the subscription ID associated with the specified PhoneAccount.
+     */
+    int getSubIdForPhoneAccount(in PhoneAccount phoneAccount);
 }
diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java
index b8e8064..0ebd719 100644
--- a/telephony/java/com/android/internal/telephony/PhoneConstants.java
+++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java
@@ -186,4 +186,11 @@
 
     //FIXME maybe this shouldn't be here - sprout only
     public static final int CAPABILITY_3G   = 1;
+
+    /**
+     * Values for the adb property "persist.radio.videocall.audio.output"
+     */
+    public static final int AUDIO_OUTPUT_ENABLE_SPEAKER = 0;
+    public static final int AUDIO_OUTPUT_DISABLE_SPEAKER = 1;
+    public static final int AUDIO_OUTPUT_DEFAULT = AUDIO_OUTPUT_ENABLE_SPEAKER;
 }
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 082e8bbb..12541d8 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -69,6 +69,14 @@
     int SS_MODIFIED_TO_USSD = 25;             /* SS request modified to USSD */
     int SUBSCRIPTION_NOT_SUPPORTED = 26;      /* Subscription not supported */
     int SS_MODIFIED_TO_SS = 27;               /* SS request modified to different SS request */
+    int SIM_ALREADY_POWERED_OFF = 29;         /* SAP: 0x03, Error card aleready powered off */
+    int SIM_ALREADY_POWERED_ON = 30;          /* SAP: 0x05, Error card already powered on */
+    int SIM_DATA_NOT_AVAILABLE = 31;          /* SAP: 0x06, Error data not available */
+    int SIM_SAP_CONNECT_FAILURE = 32;
+    int SIM_SAP_MSG_SIZE_TOO_LARGE = 33;
+    int SIM_SAP_MSG_SIZE_TOO_SMALL = 34;
+    int SIM_SAP_CONNECT_OK_CALL_ONGOING = 35;
+    int LCE_NOT_SUPPORTED = 36;               /* Link Capacity Estimation (LCE) not supported */
 
 
     /* NETWORK_MODE_* See ril.h RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE */
@@ -135,6 +143,11 @@
     int NV_CONFIG_ERASE_RESET = 2;
     int NV_CONFIG_FACTORY_RESET = 3;
 
+    /* LCE service related constants. */
+    int LCE_NOT_AVAILABLE = -1;
+    int LCE_STOPPED = 0;
+    int LCE_ACTIVE = 1;
+
 /*
 cat include/telephony/ril.h | \
    egrep '^#define' | \
@@ -307,6 +320,9 @@
     int RIL_REQUEST_SHUTDOWN = 129;
     int RIL_REQUEST_GET_RADIO_CAPABILITY = 130;
     int RIL_REQUEST_SET_RADIO_CAPABILITY = 131;
+    int RIL_REQUEST_START_LCE = 132;
+    int RIL_REQUEST_STOP_LCE = 133;
+    int RIL_REQUEST_PULL_LCEDATA = 134;
 
     int RIL_UNSOL_RESPONSE_BASE = 1000;
     int RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED = 1000;
@@ -354,4 +370,5 @@
     int RIL_UNSOL_RADIO_CAPABILITY = 1042;
     int RIL_UNSOL_ON_SS = 1043;
     int RIL_UNSOL_STK_CC_ALPHA_NOTIFY = 1044;
+    int RIL_UNSOL_LCEDATA_RECV = 1045;
 }
diff --git a/telephony/java/com/android/internal/telephony/TelephonyProperties.java b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
index c89208d..645c3a1 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyProperties.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
@@ -209,4 +209,12 @@
      * Set to the sim count.
      */
     static final String PROPERTY_SIM_COUNT = "ro.telephony.sim.count";
+
+    /**
+     * Controls audio route for video calls.
+     * 0 - Use the default audio routing strategy.
+     * 1 - Disable the speaker. Route the audio to Headset or Bluetooth
+     *     or Earpiece, based on the default audio routing strategy.
+     */
+    static final String PROPERTY_VIDEOCALL_AUDIO_OUTPUT = "persist.radio.call.audio.output";
 }
diff --git a/wifi/java/android/net/wifi/IRttManager.aidl b/wifi/java/android/net/wifi/IRttManager.aidl
index d929f55..90f66c4 100644
--- a/wifi/java/android/net/wifi/IRttManager.aidl
+++ b/wifi/java/android/net/wifi/IRttManager.aidl
@@ -15,8 +15,8 @@
  */
 
 package android.net.wifi;
-
 import android.os.Messenger;
+import android.net.wifi.RttManager;
 
 /**
  * {@hide}
@@ -24,4 +24,5 @@
 interface IRttManager
 {
     Messenger getMessenger();
+    RttManager.RttCapabilities getRttCapabilities();
 }
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index ca95ec1..6e6d6f67 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -47,6 +47,8 @@
 
     List<WifiConfiguration> getPrivilegedConfiguredNetworks();
 
+    WifiConfiguration getMatchingWifiConfig(in ScanResult scanResult);
+
     int addOrUpdateNetwork(in WifiConfiguration config);
 
     boolean removeNetwork(int netId);
@@ -79,6 +81,8 @@
 
     void setCountryCode(String country, boolean persist);
 
+    String getCountryCode();
+
     void setFrequencyBand(int band, boolean persist);
 
     int getFrequencyBand();
@@ -111,6 +115,8 @@
 
     WifiConfiguration getWifiApConfiguration();
 
+    WifiConfiguration buildWifiConfig(String uriString, String mimeType, in byte[] data);
+
     void setWifiApConfiguration(in WifiConfiguration wifiConfig);
 
     void startWifi();
diff --git a/wifi/java/android/net/wifi/RttManager.aidl b/wifi/java/android/net/wifi/RttManager.aidl
new file mode 100644
index 0000000..5c6d447
--- /dev/null
+++ b/wifi/java/android/net/wifi/RttManager.aidl
@@ -0,0 +1,18 @@
+/**
+ * Copyright (c) 2015, 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.net.wifi;
+parcelable RttManager.RttCapabilities;
\ No newline at end of file
diff --git a/wifi/java/android/net/wifi/RttManager.java b/wifi/java/android/net/wifi/RttManager.java
index 57343c5..b156d0c 100644
--- a/wifi/java/android/net/wifi/RttManager.java
+++ b/wifi/java/android/net/wifi/RttManager.java
@@ -26,33 +26,109 @@
     private static final boolean DBG = true;
     private static final String TAG = "RttManager";
 
-    public static final int RTT_TYPE_UNSPECIFIED    = 0;
-    public static final int RTT_TYPE_ONE_SIDED      = 1;
-    public static final int RTT_TYPE_11_V           = 2;
-    public static final int RTT_TYPE_11_MC          = 4;
+    /** @deprecated It is Not supported anymore. */
+    @Deprecated
+    public static final int RTT_TYPE_UNSPECIFIED        = 0;
 
+    public static final int RTT_TYPE_ONE_SIDED          = 1;
+    public static final int RTT_TYPE_TWO_SIDED          = 2;
+
+    /** @deprecated It is not supported anymore. */
+    @Deprecated
+    public static final int RTT_TYPE_11_V               = 2;
+
+    /** @deprecated It is not supported anymore. */
+    @Deprecated
+    public static final int RTT_TYPE_11_MC              = 4;
+
+    /** @deprecated It is not supported anymore. */
+    @Deprecated
     public static final int RTT_PEER_TYPE_UNSPECIFIED    = 0;
+
     public static final int RTT_PEER_TYPE_AP             = 1;
     public static final int RTT_PEER_TYPE_STA            = 2;       /* requires NAN */
+    public static final int RTT_PEER_P2P_GO              = 3;
+    public static final int RTT_PEER_P2P_CLIENT          = 4;
+    public static final int RTT_PEER_NAN                 = 5;
 
+    /**
+     * @deprecated It is not supported anymore.
+     * Use {@link android.net.wifi.RttManager#RTT_BW_20_SUPPORT} API.
+     */
+    @Deprecated
     public static final int RTT_CHANNEL_WIDTH_20      = 0;
+
+    /**
+     * @deprecated It is not supported anymore.
+     * Use {@link android.net.wifi.RttManager#RTT_BW_40_SUPPORT} API.
+     */
+    @Deprecated
     public static final int RTT_CHANNEL_WIDTH_40      = 1;
+
+    /**
+     * @deprecated It is not supported anymore.
+     * Use {@link android.net.wifi.RttManager#RTT_BW_80_SUPPORT} API.
+     */
+    @Deprecated
     public static final int RTT_CHANNEL_WIDTH_80      = 2;
+
+    /**@deprecated It is not supported anymore.
+     * Use {@link android.net.wifi.RttManager#RTT_BW_160_SUPPORT} API.
+     */
+    @Deprecated
     public static final int RTT_CHANNEL_WIDTH_160     = 3;
+
+    /**@deprecated not supported anymore*/
+    @Deprecated
     public static final int RTT_CHANNEL_WIDTH_80P80   = 4;
+
+    /**@deprecated It is not supported anymore.
+     * Use {@link android.net.wifi.RttManager#RTT_BW_5_SUPPORT} API.
+     */
+    @Deprecated
     public static final int RTT_CHANNEL_WIDTH_5       = 5;
+
+    /**@deprecated It is not supported anymore.
+     * Use {@link android.net.wifi.RttManager#RTT_BW_10_SUPPORT} API.
+     */
+    @Deprecated
     public static final int RTT_CHANNEL_WIDTH_10      = 6;
+
+    /** @deprecated channel info must be specified. */
+    @Deprecated
     public static final int RTT_CHANNEL_WIDTH_UNSPECIFIED = -1;
 
     public static final int RTT_STATUS_SUCCESS                  = 0;
+    /** General failure*/
     public static final int RTT_STATUS_FAILURE                  = 1;
+    /** Destination does not respond to RTT request*/
     public static final int RTT_STATUS_FAIL_NO_RSP              = 2;
+    /** RTT request is rejected by the destination. Double side RTT only*/
     public static final int RTT_STATUS_FAIL_REJECTED            = 3;
+    /** */
     public static final int RTT_STATUS_FAIL_NOT_SCHEDULED_YET   = 4;
+    /** Timing measurement timeout*/
     public static final int RTT_STATUS_FAIL_TM_TIMEOUT          = 5;
+    /** Destination is on a different channel from the RTT Request*/
     public static final int RTT_STATUS_FAIL_AP_ON_DIFF_CHANNEL  = 6;
+    /** This type of Ranging is not support by Hardware*/
     public static final int RTT_STATUS_FAIL_NO_CAPABILITY       = 7;
+    /** Request abort fro uncertain reason*/
     public static final int RTT_STATUS_ABORTED                  = 8;
+    /** The T1-T4 or TOD/TOA Timestamp is illegal*/
+    public static final int RTT_STATUS_FAIL_INVALID_TS          = 9;
+    /** 11mc protocol level failed, eg, unrecognized FTMR/FTM frame*/
+    public static final int RTT_STATUS_FAIL_PROTOCOL            = 10;
+    /** Request can not be scheduled by hardware*/
+    public static final int RTT_STATUS_FAIL_SCHEDULE            = 11;
+    /** destination is busy now, you can try after a specified time from destination*/
+    public static final int RTT_STATUS_FAIL_BUSY_TRY_LATER      = 12;
+    /** Bad Request argument*/
+    public static final int RTT_STATUS_INVALID_REQ              = 13;
+    /** Wifi is not enabled*/
+    public static final int RTT_STATUS_NO_WIFI                  = 14;
+    /** Responder overrides param info, cannot range with new params 2-side RTT only*/
+    public static final int RTT_STATUS_FAIL_FTM_PARAM_OVERRIDE  = 15;
 
     public static final int REASON_UNSPECIFIED              = -1;
     public static final int REASON_NOT_AVAILABLE            = -2;
@@ -61,41 +137,312 @@
 
     public static final String DESCRIPTION_KEY  = "android.net.wifi.RttManager.Description";
 
+    /**
+     * RTT BW supported bit mask, used as RTT param bandWidth too
+     */
+    public static final int RTT_BW_5_SUPPORT   = 0x01;
+    public static final int RTT_BW_10_SUPPORT  = 0x02;
+    public static final int RTT_BW_20_SUPPORT  = 0x04;
+    public static final int RTT_BW_40_SUPPORT  = 0x08;
+    public static final int RTT_BW_80_SUPPORT  = 0x10;
+    public static final int RTT_BW_160_SUPPORT = 0x20;
+
+    /**
+     * RTT Preamble Support bit mask
+     */
+    public static final int PREAMBLE_LEGACY  = 0x01;
+    public static final int PREAMBLE_HT      = 0x02;
+    public static final int PREAMBLE_VHT     = 0x04;
+
+    /** @deprecated Use the new {@link android.net.wifi.RttManager.RttCapabilities} API */
+    @Deprecated
     public class Capabilities {
         public int supportedType;
         public int supportedPeerType;
     }
 
+    /** @deprecated Use the new {@link android.net.wifi.RttManager#getRttCapabilities()} API.*/
+    @Deprecated
     public Capabilities getCapabilities() {
         return new Capabilities();
     }
 
+    /**
+     * This class describe the RTT capability of the Hardware
+     */
+    public static class RttCapabilities implements Parcelable {
+        /** @deprecated It is not supported*/
+        @Deprecated
+        public boolean supportedType;
+        /** @deprecated It is not supported*/
+        @Deprecated
+        public boolean supportedPeerType;
+        //1-sided rtt measurement is supported
+        public boolean oneSidedRttSupported;
+        //11mc 2-sided rtt measurement is supported
+        public boolean twoSided11McRttSupported;
+        //location configuration information supported
+        public boolean lciSupported;
+        //location civic records supported
+        public boolean lcrSupported;
+        //preamble supported, see bit mask definition above
+        public int preambleSupported;
+        //RTT bandwidth supported
+        public int bwSupported;
+
+        @Override
+        public String toString() {
+            StringBuffer sb = new StringBuffer();
+            sb.append("oneSidedRtt ").
+            append(oneSidedRttSupported ? "is Supported. " : "is not supported. ").
+            append("twoSided11McRtt ").
+            append(twoSided11McRttSupported ? "is Supported. " : "is not supported. ").
+            append("lci ").
+            append(lciSupported ? "is Supported. " : "is not supported. ").
+            append("lcr ").
+            append(lcrSupported ? "is Supported. " : "is not supported. ");
+
+            if ((preambleSupported & PREAMBLE_LEGACY) != 0) {
+                sb.append("Legacy ");
+            }
+
+            if ((preambleSupported & PREAMBLE_HT) != 0) {
+                sb.append("HT ");
+            }
+
+            if ((preambleSupported & PREAMBLE_VHT) != 0) {
+                sb.append("VHT ");
+            }
+
+            sb.append("is supported. \n");
+
+            if ((bwSupported & RTT_BW_5_SUPPORT) != 0) {
+                sb.append("5 MHz ");
+            }
+
+            if ((bwSupported & RTT_BW_10_SUPPORT) != 0) {
+                sb.append("10 MHz ");
+            }
+
+            if ((bwSupported & RTT_BW_20_SUPPORT) != 0) {
+                sb.append("20 MHz ");
+            }
+
+            if ((bwSupported & RTT_BW_40_SUPPORT) != 0) {
+                sb.append("40 MHz ");
+            }
+
+            if ((bwSupported & RTT_BW_80_SUPPORT) != 0) {
+                sb.append("80 MHz ");
+            }
+
+            if ((bwSupported & RTT_BW_160_SUPPORT) != 0) {
+                sb.append("160 MHz ");
+            }
+
+            sb.append("is supported.");
+
+            return sb.toString();
+        }
+        /** Implement the Parcelable interface {@hide} */
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        /** Implement the Parcelable interface {@hide} */
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(oneSidedRttSupported ? 1 : 0);
+            dest.writeInt(twoSided11McRttSupported ? 1 : 0);
+            dest.writeInt(lciSupported ? 1 : 0);
+            dest.writeInt(lcrSupported ? 1 : 0);
+            dest.writeInt(preambleSupported);
+            dest.writeInt(bwSupported);
+
+        }
+
+        /** Implement the Parcelable interface {@hide} */
+        public static final Creator<RttCapabilities> CREATOR =
+            new Creator<RttCapabilities>() {
+               public RttCapabilities createFromParcel(Parcel in) {
+                    RttCapabilities capabilities = new RttCapabilities();
+                    capabilities.oneSidedRttSupported = in.readInt() == 1 ? true : false;
+                        capabilities.twoSided11McRttSupported = in.readInt() == 1 ? true : false;
+                        capabilities.lciSupported = in.readInt() == 1 ? true : false;
+                        capabilities.lcrSupported = in.readInt() == 1 ? true : false;
+                        capabilities.preambleSupported = in.readInt();
+                        capabilities.bwSupported = in.readInt();
+                        return capabilities;
+                    }
+                /** Implement the Parcelable interface {@hide} */
+                @Override
+                public RttCapabilities[] newArray(int size) {
+                    return new RttCapabilities[size];
+                }
+             };
+    }
+
+    public RttCapabilities getRttCapabilities() {
+        synchronized (sCapabilitiesLock) {
+            if (mRttCapabilities == null) {
+                try {
+                    mRttCapabilities = mService.getRttCapabilities();
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Can not get RTT Capabilities");
+                }
+            }
+            return mRttCapabilities;
+        }
+    }
+
     /** specifies parameters for RTT request */
     public static class RttParams {
-
-        /** type of device being ranged; one of RTT_PEER_TYPE_AP or RTT_PEER_TYPE_STA */
+        /**
+         * type of destination device being ranged
+         * currently only support RTT_PEER_TYPE_AP
+         * Range:RTT_PEER_TYPE_xxxx Default value:RTT_PEER_TYPE_AP
+         */
         public int deviceType;
 
-        /** type of RTT being sought; one of RTT_TYPE_ONE_SIDED
-         *  RTT_TYPE_11_V or RTT_TYPE_11_MC or RTT_TYPE_UNSPECIFIED */
+        /**
+         * type of RTT measurement method. Need check scan result and RttCapabilities first
+         * Range: RTT_TYPE_ONE_SIDED or RTT_TYPE_TWO_SIDED
+         * Default value: RTT_TYPE_ONE_SIDED
+         */
         public int requestType;
 
-        /** mac address of the device being ranged */
+        /**
+         * mac address of the device being ranged
+         * Default value: null
+         */
         public String bssid;
 
-        /** channel frequency that the device is on; optional */
+        /**
+         * The primary control channel over which the client is
+         * communicating with the AP.Same as ScanResult.frequency
+         * Default value: 0
+         */
         public int frequency;
 
-        /** optional channel width. wider channels result in better accuracy,
-         *  but they take longer time, and even get aborted may times; use
-         *  RTT_CHANNEL_WIDTH_UNSPECIFIED if not specifying */
+        /**
+         * channel width of the destination AP. Same as ScanResult.channelWidth
+         * Default value: 0
+         */
         public int channelWidth;
 
-        /** number of samples to be taken */
+        /**
+         * Not used if the AP bandwidth is 20 MHz
+         * If the AP use 40, 80 or 160 MHz, this is the center frequency
+         * if the AP use 80 + 80 MHz, this is the center frequency of the first segment
+         * same as ScanResult.centerFreq0
+         * Default value: 0
+         */
+         public int centerFreq0;
+
+         /**
+          * Only used if the AP bandwidth is 80 + 80 MHz
+          * if the AP use 80 + 80 MHz, this is the center frequency of the second segment
+          * same as ScanResult.centerFreq1
+          * Default value: 0
+          */
+          public int centerFreq1;
+
+        /**
+         * number of samples to be taken
+         * @deprecated Use the new {@link android.net.wifi.RttManager.RttParams#numSamplesPerBurst}
+         */
+        @Deprecated
         public int num_samples;
 
-        /** number of retries if a sample fails */
+        /**
+         * number of retries if a sample fails
+         * @deprecated
+         * Use {@link android.net.wifi.RttManager.RttParams#numRetriesPerMeasurementFrame} API.
+         */
+        @Deprecated
         public int num_retries;
+
+        /** Number of burst in exp , 2^x. 0 means single shot measurement, range 0-15
+         * Currently only single shot is supported
+         * Default value: 0
+         */
+        public int numberBurst;
+
+        /**
+         * valid only if numberBurst > 1, interval between burst(100ms).
+         * Range : 0-31, 0--means no specific
+         * Default value: 0
+         */
+        public int interval;
+
+        /**
+         * number of samples to be taken in one burst
+         * Range: 1-31
+         * Default value: 8
+         */
+        public int numSamplesPerBurst;
+
+        /** number of retries for each measurement frame if a sample fails
+         *  Only used by single side RTT,
+         *  Range 0 - 3 Default value: 0
+         */
+        public int numRetriesPerMeasurementFrame;
+
+        /**
+         * number of retries for FTMR frame (control frame) if it fails.
+         * Only used by 80211MC double side RTT
+         * Range: 0-3  Default Value : 0
+         */
+        public int numRetriesPerFTMR;
+
+        /**
+         * Request LCI information, only available when choose double side RTT measurement
+         * need check RttCapabilties first.
+         * Default value: false
+         * */
+        public boolean LCIRequest;
+
+        /**
+         * Request LCR information, only available when choose double side RTT measurement
+         * need check RttCapabilties first.
+         * Default value: false
+         * */
+        public boolean LCRRequest;
+
+        /**
+         * Timeout for each burst, (250 * 2^x) us,
+         * Range 1-11 and 15. 15 means no control Default value: 15
+         * */
+        public int burstTimeout;
+
+        /** preamble used for RTT measurement
+         *  Range: PREAMBLE_LEGACY, PREAMBLE_HT, PREAMBLE_VHT
+         *  Default value: PREAMBLE_HT
+         */
+        public int preamble;
+
+        /** bandWidth used for RTT measurement.User need verify the highest BW the destination
+         * support (from scan result etc) before set this value. Wider channels result usually give
+         * better accuracy. However, the frame loss can increase too.
+         * should be one of RTT_BW_5_SUPPORT to RTT_BW_160_SUPPORT. However, need check
+         * RttCapabilities firstto verify HW support this bandwidth.
+         * Default value:RTT_BW_20_SUPPORT
+         */
+        public int bandwidth;
+
+        public RttParams() {
+            //provide initial value for RttParams
+            deviceType = RTT_PEER_TYPE_AP;
+            requestType = RTT_TYPE_ONE_SIDED;
+            numberBurst = 0;
+            numSamplesPerBurst = 8;
+            numRetriesPerMeasurementFrame  = 0;
+            numRetriesPerFTMR = 0;
+            burstTimeout = 15;
+            preamble = PREAMBLE_HT;
+            bandwidth = RTT_BW_20_SUPPORT;
+        }
     }
 
     /** pseudo-private class used to parcel arguments */
@@ -121,10 +468,20 @@
                     dest.writeInt(params.deviceType);
                     dest.writeInt(params.requestType);
                     dest.writeString(params.bssid);
-                    dest.writeInt(params.frequency);
                     dest.writeInt(params.channelWidth);
-                    dest.writeInt(params.num_samples);
-                    dest.writeInt(params.num_retries);
+                    dest.writeInt(params.frequency);
+                    dest.writeInt(params.centerFreq0);
+                    dest.writeInt(params.centerFreq1);
+                    dest.writeInt(params.numberBurst);
+                    dest.writeInt(params.interval);
+                    dest.writeInt(params.numSamplesPerBurst);
+                    dest.writeInt(params.numRetriesPerMeasurementFrame);
+                    dest.writeInt(params.numRetriesPerFTMR);
+                    dest.writeInt(params.LCIRequest ? 1 : 0);
+                    dest.writeInt(params.LCRRequest ? 1 : 0);
+                    dest.writeInt(params.burstTimeout);
+                    dest.writeInt(params.preamble);
+                    dest.writeInt(params.bandwidth);
                 }
             } else {
                 dest.writeInt(0);
@@ -148,11 +505,20 @@
                             params[i].deviceType = in.readInt();
                             params[i].requestType = in.readInt();
                             params[i].bssid = in.readString();
-                            params[i].frequency = in.readInt();
                             params[i].channelWidth = in.readInt();
-                            params[i].num_samples = in.readInt();
-                            params[i].num_retries = in.readInt();
-
+                            params[i].frequency = in.readInt();
+                            params[i].centerFreq0 = in.readInt();
+                            params[i].centerFreq1 = in.readInt();
+                            params[i].numberBurst = in.readInt();
+                            params[i].interval = in.readInt();
+                            params[i].numSamplesPerBurst = in.readInt();
+                            params[i].numRetriesPerMeasurementFrame = in.readInt();
+                            params[i].numRetriesPerFTMR = in.readInt();
+                            params[i].LCIRequest = in.readInt() == 1 ? true : false;
+                            params[i].LCRRequest = in.readInt() == 1 ? true : false;
+                            params[i].burstTimeout = in.readInt();
+                            params[i].preamble = in.readInt();
+                            params[i].bandwidth = in.readInt();
                         }
 
                         ParcelableRttParams parcelableParams = new ParcelableRttParams(params);
@@ -165,50 +531,158 @@
                 };
     }
 
+    public static class WifiInformationElement {
+        /** Information Element ID 0xFF means element is invalid. */
+        public byte id;
+        public byte[] data;
+    }
     /** specifies RTT results */
     public static class RttResult {
-        /** mac address of the device being ranged */
+        /** mac address of the device being ranged. */
         public String bssid;
 
+        /** # of burst for this measurement. */
+        public int burstNumber;
+
+        /** total number of measurement frames attempted in this measurement. */
+        public int measurementFrameNumber;
+
+        /** total successful number of measurement frames in this measurement. */
+        public int successMeasurementFrameNumber;
+
+        /**
+         * Maximum number of frames per burst supported by peer. Two side RTT only
+         * Valid only if less than request
+         */
+        public int frameNumberPerBurstPeer;
+
         /** status of the request */
         public int status;
 
-        /** type of the request used */
+        /**
+         * type of the request used
+         * @deprecated Use {@link android.net.wifi.RttManager.RttResult#measurementType}
+         */
+        @Deprecated
         public int requestType;
 
-        /** timestamp of completion, in microsecond since boot */
+        /** RTT measurement method type used, should be one of RTT_TYPE_ONE_SIDED or
+         *  RTT_TYPE_TWO_SIDED.
+         */
+        public int measurementType;
+
+        /**
+         * only valid when status ==  RTT_STATUS_FAIL_BUSY_TRY_LATER
+         * please retry RTT measurement after this duration since peer indicate busy at ths moment
+         *  Unit S  Range:1-31
+         */
+        public int retryAfterDuration;
+
+        /** timestamp of completion, in microsecond since boot. */
         public long ts;
 
-        /** average RSSI observed */
+        /** average RSSI observed, unit of 0.5 dB. */
         public int rssi;
 
-        /** RSSI spread (i.e. max - min) */
+        /**
+         * RSSI spread (i.e. max - min)
+         * @deprecated Use {@link android.net.wifi.RttManager.RttResult#rssiSpread} API.
+         */
+        @Deprecated
         public int rssi_spread;
 
-        /** average transmit rate */
+        /**RSSI spread (i.e. max - min), unit of 0.5 dB. */
+        public int rssiSpread;
+
+        /**
+         * average transmit rate
+         * @deprecated Use {@link android.net.wifi.RttManager.RttResult#txRate} API.
+         */
+        @Deprecated
         public int tx_rate;
 
-        /** average round trip time in nano second */
+        /** average transmit rate. Unit (100kbps). */
+        public int txRate;
+
+        /** average receiving rate Unit (100kbps). */
+        public int rxRate;
+
+       /**
+        * average round trip time in nano second
+        * @deprecated  Use {@link android.net.wifi.RttManager.RttResult#rtt} API.
+        */
+        @Deprecated
         public long rtt_ns;
 
-        /** standard deviation observed in round trip time */
+        /** average round trip time in 0.1 nano second. */
+        public long rtt;
+
+        /**
+         * standard deviation observed in round trip time
+         * @deprecated Use {@link android.net.wifi.RttManager.RttResult#rttStandardDeviation} API.
+         */
+        @Deprecated
         public long rtt_sd_ns;
 
-        /** spread (i.e. max - min) round trip time */
+        /** standard deviation of RTT in 0.1 ns. */
+        public long rttStandardDeviation;
+
+        /**
+         * spread (i.e. max - min) round trip time
+         * @deprecated Use {@link android.net.wifi.RttManager.RttResult#rttSpread} API.
+         */
+        @Deprecated
         public long rtt_spread_ns;
 
-        /** average distance in centimeter, computed based on rtt_ns */
+        /** spread (i.e. max - min) RTT in 0.1 ns. */
+        public long rttSpread;
+
+        /**
+         * average distance in centimeter, computed based on rtt_ns
+         * @deprecated use {@link android.net.wifi.RttManager.RttResult#distance} API.
+         */
+        @Deprecated
         public int distance_cm;
 
-        /** standard deviation observed in distance */
+        /** average distance in cm, computed based on rtt. */
+        public int distance;
+
+        /**
+         * standard deviation observed in distance
+         * @deprecated
+         * Use {@link .android.net.wifi.RttManager.RttResult#distanceStandardDeviation} API.
+         */
+        @Deprecated
         public int distance_sd_cm;
 
-        /** spread (i.e. max - min) distance */
+        /** standard deviation observed in distance in cm. */
+        public int distanceStandardDeviation;
+
+        /**
+         * spread (i.e. max - min) distance
+         * @deprecate Use {@link android.net.wifi.RttManager.RttResult#distanceSpread} API.
+         */
+        @Deprecated
         public int distance_spread_cm;
+
+        /** spread (i.e. max - min) distance in cm. */
+        public int distanceSpread;
+
+        /** the duration of this measurement burst, unit ms. */
+        public int burstDuration;
+
+        /** Burst number supported by peer after negotiation, 2side RTT only*/
+        public int negotiatedBurstNum;
+
+        /** LCI information Element, only available for double side RTT. */
+        public WifiInformationElement LCI;
+
+        /** LCR information Element, only available to double side RTT. */
+        public WifiInformationElement LCR;
     }
 
 
-    /** pseudo-private class used to parcel results */
+    /** pseudo-private class used to parcel results. */
     public static class ParcelableRttResults implements Parcelable {
 
         public RttResult mResults[];
@@ -228,18 +702,35 @@
                 dest.writeInt(mResults.length);
                 for (RttResult result : mResults) {
                     dest.writeString(result.bssid);
+                    dest.writeInt(result.burstNumber);
+                    dest.writeInt(result.measurementFrameNumber);
+                    dest.writeInt(result.successMeasurementFrameNumber);
+                    dest.writeInt(result.frameNumberPerBurstPeer);
                     dest.writeInt(result.status);
-                    dest.writeInt(result.requestType);
+                    dest.writeInt(result.measurementType);
+                    dest.writeInt(result.retryAfterDuration);
                     dest.writeLong(result.ts);
                     dest.writeInt(result.rssi);
-                    dest.writeInt(result.rssi_spread);
-                    dest.writeInt(result.tx_rate);
-                    dest.writeLong(result.rtt_ns);
-                    dest.writeLong(result.rtt_sd_ns);
-                    dest.writeLong(result.rtt_spread_ns);
-                    dest.writeInt(result.distance_cm);
-                    dest.writeInt(result.distance_sd_cm);
-                    dest.writeInt(result.distance_spread_cm);
+                    dest.writeInt(result.rssiSpread);
+                    dest.writeInt(result.txRate);
+                    dest.writeLong(result.rtt);
+                    dest.writeLong(result.rttStandardDeviation);
+                    dest.writeLong(result.rttSpread);
+                    dest.writeInt(result.distance);
+                    dest.writeInt(result.distanceStandardDeviation);
+                    dest.writeInt(result.distanceSpread);
+                    dest.writeInt(result.burstDuration);
+                    dest.writeInt(result.negotiatedBurstNum);
+                    dest.writeByte(result.LCI.id);
+                    if (result.LCI.id != (byte) 0xFF) {
+                        dest.writeByte((byte)result.LCI.data.length);
+                        dest.writeByteArray(result.LCI.data);
+                    }
+                    dest.writeByte(result.LCR.id);
+                    if (result.LCR.id != (byte) 0xFF) {
+                        dest.writeInt((byte) result.LCR.data.length);
+                        dest.writeByte(result.LCR.id);
+                    }
                 }
             } else {
                 dest.writeInt(0);
@@ -261,18 +752,39 @@
                         for (int i = 0; i < num; i++) {
                             results[i] = new RttResult();
                             results[i].bssid = in.readString();
+                            results[i].burstNumber = in.readInt();
+                            results[i].measurementFrameNumber = in.readInt();
+                            results[i].successMeasurementFrameNumber = in.readInt();
+                            results[i].frameNumberPerBurstPeer = in.readInt();
                             results[i].status = in.readInt();
-                            results[i].requestType = in.readInt();
+                            results[i].measurementType = in.readInt();
+                            results[i].retryAfterDuration = in.readInt();
                             results[i].ts = in.readLong();
                             results[i].rssi = in.readInt();
-                            results[i].rssi_spread = in.readInt();
-                            results[i].tx_rate = in.readInt();
-                            results[i].rtt_ns = in.readLong();
-                            results[i].rtt_sd_ns = in.readLong();
-                            results[i].rtt_spread_ns = in.readLong();
-                            results[i].distance_cm = in.readInt();
-                            results[i].distance_sd_cm = in.readInt();
-                            results[i].distance_spread_cm = in.readInt();
+                            results[i].rssiSpread = in.readInt();
+                            results[i].txRate = in.readInt();
+                            results[i].rtt = in.readLong();
+                            results[i].rttStandardDeviation = in.readLong();
+                            results[i].rttSpread = in.readLong();
+                            results[i].distance = in.readInt();
+                            results[i].distanceStandardDeviation = in.readInt();
+                            results[i].distanceSpread = in.readInt();
+                            results[i].burstDuration = in.readInt();
+                            results[i].negotiatedBurstNum = in.readInt();
+                            results[i].LCI = new WifiInformationElement();
+                            results[i].LCI.id = in.readByte();
+                            if (results[i].LCI.id != (byte) 0xFF) {
+                                byte length = in.readByte();
+                                results[i].LCI.data = new byte[length];
+                                in.readByteArray(results[i].LCI.data);
+                            }
+                            results[i].LCR = new WifiInformationElement();
+                            results[i].LCR.id = in.readByte();
+                            if (results[i].LCR.id != (byte) 0xFF) {
+                                byte length = in.readByte();
+                                results[i].LCR.data = new byte[length];
+                                in.readByteArray(results[i].LCR.data);
+                            }
                         }
 
                         ParcelableRttResults parcelableResults = new ParcelableRttResults(results);
@@ -292,7 +804,77 @@
         public void onAborted();
     }
 
+    private boolean rttParamSanity(RttParams params, int index) {
+        if (mRttCapabilities == null) {
+            if(getRttCapabilities() == null) {
+                Log.e(TAG, "Can not get RTT capabilities");
+                //throw new IllegalStateException("RTT chip is not working");
+            }
+        }
+
+        if (params.deviceType != RTT_PEER_TYPE_AP) {
+            return false;
+        } else if (params.requestType != RTT_TYPE_ONE_SIDED && params.requestType !=
+                RTT_TYPE_TWO_SIDED) {
+            Log.e(TAG, "Request " + index + ": Illegal Request Type: " + params.requestType);
+            return false;
+        } else if (params.requestType == RTT_TYPE_ONE_SIDED &&
+                !mRttCapabilities.oneSidedRttSupported) {
+            Log.e(TAG, "Request " + index + ": One side RTT is not supported");
+            return false;
+        } else if (params.requestType == RTT_TYPE_TWO_SIDED &&
+                !mRttCapabilities.twoSided11McRttSupported) {
+            Log.e(TAG, "Request " + index + ": two side RTT is not supported");
+            return false;
+        }  else if(params.bssid == null || params.bssid.isEmpty()) {
+            Log.e(TAG,"No BSSID is input");
+        } else if ( params.numberBurst != 0 ) {
+            Log.e(TAG, "Request " + index + ": Illegal number of burst: " + params.numberBurst);
+            return false;
+        } else if (params.numSamplesPerBurst <= 0 || params.numSamplesPerBurst > 31) {
+            Log.e(TAG, "Request " + index + ": Illegal sample number per burst: " +
+                    params.numSamplesPerBurst);
+            return false;
+        } else if (params.numRetriesPerMeasurementFrame < 0 ||
+                params.numRetriesPerMeasurementFrame > 3) {
+            Log.e(TAG, "Request " + index + ": Illegal measurement frame retry number:" +
+                    params.numRetriesPerMeasurementFrame);
+            return false;
+        } else if(params.numRetriesPerFTMR < 0 ||
+                params.numRetriesPerFTMR > 3) {
+            Log.e(TAG, "Request " + index + ": Illegal FTMR frame retry number:" +
+                    params.numRetriesPerFTMR);
+            return false;
+        } else if (params.LCIRequest && !mRttCapabilities.lciSupported) {
+            Log.e(TAG, "Request " + index + ": LCI is not supported");
+            return false;
+        } else if (params.LCRRequest && !mRttCapabilities.lcrSupported) {
+            Log.e(TAG, "Request " + index + ": LCR is not supported");
+            return false;
+        } else if (params.burstTimeout < 1 ||
+                (params.burstTimeout > 11 && params.burstTimeout != 15)){
+            Log.e(TAG, "Request " + index + ": Illegal burst timeout: " + params.burstTimeout);
+            return false;
+        } else if ((params.preamble & mRttCapabilities.preambleSupported) == 0) {
+            Log.e(TAG, "Request " + index + ": Do not support this preamble: " + params.preamble);
+            return false;
+        } else if ((params.bandwidth & mRttCapabilities.bwSupported) == 0) {
+            Log.e(TAG, "Request " + index + ": Do not support this bandwidth: " + params.bandwidth);
+            return false;
+        }
+
+        return true;
+    }
+
     public void startRanging(RttParams[] params, RttListener listener) {
+        int index  = 0;
+        for(RttParams rttParam : params) {
+            if (!rttParamSanity(rttParam, index)) {
+                throw new IllegalArgumentException("RTT Request Parameter Illegal");
+            }
+            index++;
+        }
+
         validateChannel();
         ParcelableRttParams parcelableParams = new ParcelableRttParams(params);
         sAsyncChannel.sendMessage(CMD_OP_START_RANGING,
@@ -315,12 +897,14 @@
 
     private Context mContext;
     private IRttManager mService;
+    private RttCapabilities mRttCapabilities;
 
     private static final int INVALID_KEY = 0;
     private static int sListenerKey = 1;
 
     private static final SparseArray sListenerMap = new SparseArray();
     private static final Object sListenerMapLock = new Object();
+    private static final Object sCapabilitiesLock = new Object();
 
     private static AsyncChannel sAsyncChannel;
     private static CountDownLatch sConnected;
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index 9729c91..5dc70bd 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -53,11 +53,55 @@
      */
     public int level;
     /**
-     * The frequency in MHz of the channel over which the client is communicating
+     * The primary 20 MHz frequency (in MHz) of the channel over which the client is communicating
      * with the access point.
      */
     public int frequency;
 
+   /**
+    * AP Channel bandwidth is 20 MHZ
+    */
+    public static final int CHANNEL_WIDTH_20MHZ = 0;
+   /**
+    * AP Channel bandwidth is 40 MHZ
+    */
+    public static final int CHANNEL_WIDTH_40MHZ = 1;
+   /**
+    * AP Channel bandwidth is 80 MHZ
+    */
+    public static final int CHANNEL_WIDTH_80MHZ = 2;
+   /**
+    * AP Channel bandwidth is 160 MHZ
+    */
+    public static final int CHANNEL_WIDTH_160MHZ = 3;
+   /**
+    * AP Channel bandwidth is 160 MHZ, but 80MHZ + 80MHZ
+    */
+    public static final int CHANNEL_WIDTH_80MHZ_PLUS_MHZ = 4;
+
+   /**
+    * AP Channel bandwidth
+    */
+    public int channelWidth;
+
+    /**
+     * Not used if the AP bandwidth is 20 MHz
+     * If the AP use 40, 80 or 160 MHz, this is the center frequency
+     * if the AP use 80 + 80 MHz, this is the center frequency of the first segment
+     */
+    public int centerFreq0;
+
+    /**
+     * Only used if the AP bandwidth is 80 + 80 MHz
+     * if the AP use 80 + 80 MHz, this is the center frequency of the second segment
+     */
+    public int centerFreq1;
+
+    /**
+     * Whether the AP support 802.11mc Responder
+     */
+   public boolean is80211McRTTResponder;
+
     /**
      * timestamp in microseconds (since boot) when
      * this result was last seen.
@@ -169,6 +213,21 @@
     public int distanceSdCm;
 
     /**
+     * Indicates if the scan result represents a passpoint AP
+     */
+    public boolean passpointNetwork;
+
+    /**
+     * Indicates if venue name
+     */
+    public String venueName;
+
+    /**
+     * Indicates operator name
+     */
+    public String operatorFriendlyName;
+
+    /**
      * {@hide}
      */
     public final static int UNSPECIFIED = -1;
@@ -202,6 +261,12 @@
         return freq > 4900 && freq < 5900;
     }
 
+    /**
+     *  @hide
+     * storing the raw bytes of full result IEs
+     **/
+    public byte[] bytes;
+
     /** information element from beacon
      * @hide
      */
@@ -235,6 +300,11 @@
         this.timestamp = tsf;
         this.distanceCm = UNSPECIFIED;
         this.distanceSdCm = UNSPECIFIED;
+        this.channelWidth = UNSPECIFIED;
+        this.centerFreq0 = UNSPECIFIED;
+        this.centerFreq1 = UNSPECIFIED;
+        this.is80211McRTTResponder = false;
+        this.passpointNetwork = false;
     }
 
     /** {@hide} */
@@ -249,6 +319,31 @@
         this.timestamp = tsf;
         this.distanceCm = distCm;
         this.distanceSdCm = distSdCm;
+        this.channelWidth = UNSPECIFIED;
+        this.centerFreq0 = UNSPECIFIED;
+        this.centerFreq1 = UNSPECIFIED;
+        this.is80211McRTTResponder = false;
+        this.passpointNetwork = false;
+    }
+
+    /** {@hide} */
+    public ScanResult(WifiSsid wifiSsid, String BSSID, String caps, int level, int frequency,
+            long tsf, int distCm, int distSdCm, int channelWidth, int centerFreq0, int centerFreq1,
+            boolean is80211McRTTResponder) {
+        this.wifiSsid = wifiSsid;
+        this.SSID = (wifiSsid != null) ? wifiSsid.toString() : WifiSsid.NONE;
+        this.BSSID = BSSID;
+        this.capabilities = caps;
+        this.level = level;
+        this.frequency = frequency;
+        this.timestamp = tsf;
+        this.distanceCm = distCm;
+        this.distanceSdCm = distSdCm;
+        this.channelWidth = channelWidth;
+        this.centerFreq0 = centerFreq0;
+        this.centerFreq1 = centerFreq1;
+        this.is80211McRTTResponder = is80211McRTTResponder;
+        this.passpointNetwork = false;
     }
 
     /** copy constructor {@hide} */
@@ -260,6 +355,10 @@
             capabilities = source.capabilities;
             level = source.level;
             frequency = source.frequency;
+            channelWidth = source.channelWidth;
+            centerFreq0 = source.centerFreq0;
+            centerFreq1 = source.centerFreq1;
+            is80211McRTTResponder = source.is80211McRTTResponder;
             timestamp = source.timestamp;
             distanceCm = source.distanceCm;
             distanceSdCm = source.distanceSdCm;
@@ -270,6 +369,9 @@
             numUsage = source.numUsage;
             numIpConfigFailures = source.numIpConfigFailures;
             isAutoJoinCandidate = source.isAutoJoinCandidate;
+            passpointNetwork = source.passpointNetwork;
+            venueName = source.venueName;
+            operatorFriendlyName = source.operatorFriendlyName;
         }
     }
 
@@ -303,9 +405,15 @@
         sb.append(", distanceSd: ").append((distanceSdCm != UNSPECIFIED ? distanceSdCm : "?")).
                 append("(cm)");
 
+        sb.append(", passpoint: ").append(passpointNetwork ? "yes" : "no");
         if (autoJoinStatus != 0) {
             sb.append(", status: ").append(autoJoinStatus);
         }
+        sb.append(", ChannelBandwidth: ").append(channelWidth);
+        sb.append(", centerFreq0: ").append(centerFreq0);
+        sb.append(", centerFreq1: ").append(centerFreq1);
+        sb.append(", 80211mcResponder: ").append(is80211McRTTResponder?
+                "is supported":"is not supported");
         return sb.toString();
     }
 
@@ -329,6 +437,10 @@
         dest.writeLong(timestamp);
         dest.writeInt(distanceCm);
         dest.writeInt(distanceSdCm);
+        dest.writeInt(channelWidth);
+        dest.writeInt(centerFreq0);
+        dest.writeInt(centerFreq1);
+        dest.writeInt(is80211McRTTResponder ? 1 : 0);
         dest.writeLong(seen);
         dest.writeInt(autoJoinStatus);
         dest.writeInt(untrusted ? 1 : 0);
@@ -336,6 +448,10 @@
         dest.writeInt(numUsage);
         dest.writeInt(numIpConfigFailures);
         dest.writeInt(isAutoJoinCandidate);
+        dest.writeInt(passpointNetwork ? 1 : 0);
+        dest.writeString(venueName);
+        dest.writeString(operatorFriendlyName);
+
         if (informationElements != null) {
             dest.writeInt(informationElements.length);
             for (int i = 0; i < informationElements.length; i++) {
@@ -364,7 +480,11 @@
                     in.readInt(),
                     in.readLong(),
                     in.readInt(),
-                    in.readInt()
+                    in.readInt(),
+                    in.readInt(),
+                    in.readInt(),
+                    in.readInt(),
+                    in.readInt() == 1
                 );
                 sr.seen = in.readLong();
                 sr.autoJoinStatus = in.readInt();
@@ -373,6 +493,9 @@
                 sr.numUsage = in.readInt();
                 sr.numIpConfigFailures = in.readInt();
                 sr.isAutoJoinCandidate = in.readInt();
+                sr.passpointNetwork = in.readInt() == 1;
+                sr.venueName = in.readString();
+                sr.operatorFriendlyName = in.readString();
                 int n = in.readInt();
                 if (n != 0) {
                     sr.informationElements = new InformationElement[n];
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java b/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java
index 533b8bc..c9a2f07 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java
+++ b/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java
@@ -116,6 +116,7 @@
         return (int)mControllerIdleTimeMs;
     }
 
+
     /**
      * product of current(mA), voltage(V) and time(ms)
      * @return energy used
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 87db951..8ee2273 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -19,19 +19,19 @@
 import android.annotation.SystemApi;
 import android.net.IpConfiguration;
 import android.net.IpConfiguration.ProxySettings;
-import android.net.IpConfiguration.IpAssignment;
 import android.net.ProxyInfo;
 import android.net.StaticIpConfiguration;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
-import android.annotation.SystemApi;
+import android.util.Log;
 
 import java.util.HashMap;
 import java.util.BitSet;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.HashSet;
 
 /**
  * A class representing a configured Wi-Fi network, including the
@@ -59,6 +59,7 @@
     public static final String updateIdentiferVarName = "update_identifier";
     /** {@hide} */
     public static final int INVALID_NETWORK_ID = -1;
+
     /**
      * Recognized key management schemes.
      */
@@ -233,17 +234,21 @@
      * <code>XX:XX:XX:XX:XX:XX</code> where each <code>X</code> is a hex digit.
      */
     public String BSSID;
+
     /**
-     * Fully qualified domain name (FQDN) of AAA server or RADIUS server
-     * e.g. {@code "mail.example.com"}.
+     * The band which AP resides on
+     * 0-2G  1-5G
+     * By default, 2G is chosen
      */
-    public String FQDN;
+    public int apBand = 0;
+
     /**
-     * Network access identifier (NAI) realm, for Passpoint credential.
-     * e.g. {@code "myhost.example.com"}.
-     * @hide
+     * The channel which AP resides on,currently, US only
+     * 2G  1-11
+     * 5G  36,40,44,48,149,153,157,161,165
+     * 0 - find a random available channel according to the apBand
      */
-    public String naiRealm;
+    public int apChannel = 0;
 
     /**
      * Pre-shared key for use with WPA-PSK.
@@ -328,6 +333,21 @@
     public WifiEnterpriseConfig enterpriseConfig;
 
     /**
+     * Fully qualified domain name of a passpoint configuration
+     */
+    public String FQDN;
+
+    /**
+     * Service provider name, for Passpoint credential.
+     */
+    public String providerFriendlyName;
+
+    /**
+     * Roaming Consortium Id, for Passpoint credential.
+     */
+    public HashSet<Long> roamingConsortiumIds;
+
+    /**
      * @hide
      */
     private IpConfiguration mIpConfiguration;
@@ -382,13 +402,6 @@
      */
     public String autoJoinBSSID;
 
-    /**
-     * @hide
-     * BSSID list on which this configuration was seen.
-     * TODO: prevent this list to grow infinitely, age-out the results
-     */
-    public HashMap<String, ScanResult> scanResultCache;
-
     /** The Below RSSI thresholds are used to configure AutoJoin
      *  - GOOD/LOW/BAD thresholds are used so as to calculate link score
      *  - UNWANTED_SOFT are used by the blacklisting logic so as to handle
@@ -478,7 +491,7 @@
      * A summary of the RSSI and Band status for that configuration
      * This is used as a temporary value by the auto-join controller
      */
-    public final class Visibility {
+    public static final class Visibility {
         public int rssi5;   // strongest 5GHz RSSI
         public int rssi24;  // strongest 2.4GHz RSSI
         public int num5;    // number of BSSIDs on 5GHz
@@ -554,47 +567,8 @@
      * age in milliseconds: we will consider only ScanResults that are more recent,
      * i.e. younger.
      ***/
-    public Visibility setVisibility(long age) {
-        if (scanResultCache == null) {
-            visibility = null;
-            return null;
-        }
-
-        Visibility status = new Visibility();
-
-        long now_ms = System.currentTimeMillis();
-        for(ScanResult result : scanResultCache.values()) {
-            if (result.seen == 0)
-                continue;
-
-            if (result.is5GHz()) {
-                //strictly speaking: [4915, 5825]
-                //number of known BSSID on 5GHz band
-                status.num5 = status.num5 + 1;
-            } else if (result.is24GHz()) {
-                //strictly speaking: [2412, 2482]
-                //number of known BSSID on 2.4Ghz band
-                status.num24 = status.num24 + 1;
-            }
-
-            if ((now_ms - result.seen) > age) continue;
-
-            if (result.is5GHz()) {
-                if (result.level > status.rssi5) {
-                    status.rssi5 = result.level;
-                    status.age5 = result.seen;
-                    status.BSSID5 = result.BSSID;
-                }
-            } else if (result.is24GHz()) {
-                if (result.level > status.rssi24) {
-                    status.rssi24 = result.level;
-                    status.age24 = result.seen;
-                    status.BSSID24 = result.BSSID;
-                }
-            }
-        }
+    public void setVisibility(Visibility status) {
         visibility = status;
-        return status;
     }
 
     /** @hide */
@@ -862,7 +836,7 @@
         SSID = null;
         BSSID = null;
         FQDN = null;
-        naiRealm = null;
+        roamingConsortiumIds = new HashSet<Long>();
         priority = 0;
         hiddenSSID = false;
         disableReason = DISABLED_UNKNOWN_REASON;
@@ -890,25 +864,62 @@
      * @hide
      */
     public boolean isValid() {
+        String reason = strIsValid();
+        if (reason != null) {
+            Log.e("WFII", "WiFi Config not valid: " + reason);
+            return false;
+        }
+        else {
+            return true;
+        }
+    }
+
+    private String strIsValid() {
 
         if (allowedKeyManagement == null)
-            return false;
+            return "allowed kmgmt";
 
         if (allowedKeyManagement.cardinality() > 1) {
             if (allowedKeyManagement.cardinality() != 2) {
-                return false;
+                return "cardinality != 2";
             }
-            if (allowedKeyManagement.get(KeyMgmt.WPA_EAP) == false) {
-                return false;
+            if (!allowedKeyManagement.get(KeyMgmt.WPA_EAP)) {
+                return "not WPA_EAP";
             }
-            if ((allowedKeyManagement.get(KeyMgmt.IEEE8021X) == false)
-                    && (allowedKeyManagement.get(KeyMgmt.WPA_PSK) == false)) {
-                return false;
+            if ((!allowedKeyManagement.get(KeyMgmt.IEEE8021X))
+                    && (!allowedKeyManagement.get(KeyMgmt.WPA_PSK))) {
+                return "not PSK or 8021X";
+            }
+        }
+
+        if (!TextUtils.isEmpty(FQDN)) {
+            /* this is passpoint configuration; it must not have an SSID */
+            if (!TextUtils.isEmpty(SSID)) {
+                return "no SSID";
+            }
+            /* this is passpoint configuration; it must have a providerFriendlyName */
+            if (TextUtils.isEmpty(providerFriendlyName)) {
+                return "no provider friendly name";
+            }
+            /* this is passpoint configuration; it must have enterprise config */
+            if (enterpriseConfig == null
+                    || enterpriseConfig.getEapMethod() == WifiEnterpriseConfig.Eap.NONE ) {
+                return "no enterprise config";
             }
         }
 
         // TODO: Add more checks
-        return true;
+        return null;
+    }
+
+    /**
+     * Identify if this configuration represents a passpoint network
+     */
+    public boolean isPasspoint() {
+        return !TextUtils.isEmpty(FQDN)
+                && !TextUtils.isEmpty(providerFriendlyName)
+                && enterpriseConfig != null
+                && enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE;
     }
 
     /**
@@ -925,31 +936,6 @@
         return  false;
     }
 
-    /**
-     * most recent time we have seen this configuration
-     * @return most recent scanResult
-     * @hide
-     */
-    public ScanResult lastSeen() {
-        ScanResult mostRecent = null;
-
-        if (scanResultCache == null) {
-            return null;
-        }
-
-        for (ScanResult result : scanResultCache.values()) {
-            if (mostRecent == null) {
-                if (result.seen != 0)
-                   mostRecent = result;
-            } else {
-                if (result.seen > mostRecent.seen) {
-                   mostRecent = result;
-                }
-            }
-        }
-        return mostRecent;
-    }
-
     /** @hide **/
     public void setAutoJoinStatus(int status) {
         if (status < 0) status = 0;
@@ -964,75 +950,6 @@
         }
     }
 
-    /** @hide
-     *  trim the scan Result Cache
-     * @param: number of entries to keep in the cache
-     */
-    public void trimScanResultsCache(int num) {
-        if (this.scanResultCache == null) {
-            return;
-        }
-        int currenSize = this.scanResultCache.size();
-        if (currenSize <= num) {
-            return; // Nothing to trim
-        }
-        ArrayList<ScanResult> list = new ArrayList<ScanResult>(this.scanResultCache.values());
-        if (list.size() != 0) {
-            // Sort by descending timestamp
-            Collections.sort(list, new Comparator() {
-                public int compare(Object o1, Object o2) {
-                    ScanResult a = (ScanResult)o1;
-                    ScanResult b = (ScanResult)o2;
-                    if (a.seen > b.seen) {
-                        return 1;
-                    }
-                    if (a.seen < b.seen) {
-                        return -1;
-                    }
-                    return a.BSSID.compareTo(b.BSSID);
-                }
-            });
-        }
-        for (int i = 0; i < currenSize - num ; i++) {
-            // Remove oldest results from scan cache
-            ScanResult result = list.get(i);
-            this.scanResultCache.remove(result.BSSID);
-        }
-    }
-
-    /* @hide */
-    private ArrayList<ScanResult> sortScanResults() {
-        ArrayList<ScanResult> list = new ArrayList<ScanResult>(this.scanResultCache.values());
-        if (list.size() != 0) {
-            Collections.sort(list, new Comparator() {
-                public int compare(Object o1, Object o2) {
-                    ScanResult a = (ScanResult)o1;
-                    ScanResult b = (ScanResult)o2;
-                    if (a.numIpConfigFailures > b.numIpConfigFailures) {
-                        return 1;
-                    }
-                    if (a.numIpConfigFailures < b.numIpConfigFailures) {
-                        return -1;
-                    }
-                    if (a.seen > b.seen) {
-                        return -1;
-                    }
-                    if (a.seen < b.seen) {
-                        return 1;
-                    }
-                    if (a.level > b.level) {
-                        return -1;
-                    }
-                    if (a.level < b.level) {
-                        return 1;
-                    }
-                    return a.BSSID.compareTo(b.BSSID);
-                }
-            });
-        }
-        return list;
-    }
-
     @Override
     public String toString() {
         StringBuilder sbuf = new StringBuilder();
@@ -1042,8 +959,9 @@
             sbuf.append("- DSBLE ");
         }
         sbuf.append("ID: ").append(this.networkId).append(" SSID: ").append(this.SSID).
+                append(" PROVIDER-NAME: ").append(this.providerFriendlyName).
                 append(" BSSID: ").append(this.BSSID).append(" FQDN: ").append(this.FQDN).
-                append(" REALM: ").append(this.naiRealm).append(" PRIO: ").append(this.priority).
+                append(" PRIO: ").append(this.priority).
                 append('\n');
         if (this.numConnectionFailures > 0) {
             sbuf.append(" numConnectFailures ").append(this.numConnectionFailures).append("\n");
@@ -1202,42 +1120,6 @@
                 }
             }
         }
-        if (this.scanResultCache != null) {
-            sbuf.append("Scan Cache:  ").append('\n');
-            ArrayList<ScanResult> list = sortScanResults();
-            if (list.size() > 0) {
-                for (ScanResult result : list) {
-                    long milli = now_ms - result.seen;
-                    long ageSec = 0;
-                    long ageMin = 0;
-                    long ageHour = 0;
-                    long ageMilli = 0;
-                    long ageDay = 0;
-                    if (now_ms > result.seen && result.seen > 0) {
-                        ageMilli = milli % 1000;
-                        ageSec   = (milli / 1000) % 60;
-                        ageMin   = (milli / (60*1000)) % 60;
-                        ageHour  = (milli / (60*60*1000)) % 24;
-                        ageDay   = (milli / (24*60*60*1000));
-                    }
-                    sbuf.append("{").append(result.BSSID).append(",").append(result.frequency);
-                    sbuf.append(",").append(String.format("%3d", result.level));
-                    if (result.autoJoinStatus > 0) {
-                        sbuf.append(",st=").append(result.autoJoinStatus);
-                    }
-                    if (ageSec > 0 || ageMilli > 0) {
-                        sbuf.append(String.format(",%4d.%02d.%02d.%02d.%03dms", ageDay,
-                                ageHour, ageMin, ageSec, ageMilli));
-                    }
-                    if (result.numIpConfigFailures > 0) {
-                        sbuf.append(",ipfail=");
-                        sbuf.append(result.numIpConfigFailures);
-                    }
-                    sbuf.append("} ");
-                }
-                sbuf.append('\n');
-            }
-        }
         sbuf.append("triggeredLow: ").append(this.numUserTriggeredWifiDisableLowRSSI);
         sbuf.append(" triggeredBad: ").append(this.numUserTriggeredWifiDisableBadRSSI);
         sbuf.append(" triggeredNotHigh: ").append(this.numUserTriggeredWifiDisableNotHighRSSI);
@@ -1383,6 +1265,8 @@
         String key;
         if (allowCached && mCachedConfigKey != null) {
             key = mCachedConfigKey;
+        } else if (providerFriendlyName != null) {
+            key = FQDN + KeyMgmt.strings[KeyMgmt.WPA_EAP];
         } else {
             if (allowedKeyManagement.get(KeyMgmt.WPA_PSK)) {
                 key = SSID + KeyMgmt.strings[KeyMgmt.WPA_PSK];
@@ -1498,9 +1382,17 @@
             SSID = source.SSID;
             BSSID = source.BSSID;
             FQDN = source.FQDN;
-            naiRealm = source.naiRealm;
+            roamingConsortiumIds = new HashSet<Long>();
+            for (Long roamingConsortiumId : source.roamingConsortiumIds) {
+                roamingConsortiumIds.add(roamingConsortiumId);
+            }
+
+            providerFriendlyName = source.providerFriendlyName;
             preSharedKey = source.preSharedKey;
 
+            apBand = source.apBand;
+            apChannel = source.apChannel;
+
             wepKeys = new String[4];
             for (int i = 0; i < wepKeys.length; i++) {
                 wepKeys[i] = source.wepKeys[i];
@@ -1521,11 +1413,6 @@
 
             mIpConfiguration = new IpConfiguration(source.mIpConfiguration);
 
-            if ((source.scanResultCache != null) && (source.scanResultCache.size() > 0)) {
-                scanResultCache = new HashMap<String, ScanResult>();
-                scanResultCache.putAll(source.scanResultCache);
-            }
-
             if ((source.connectChoices != null) && (source.connectChoices.size() > 0)) {
                 connectChoices = new HashMap<String, Integer>();
                 connectChoices.putAll(source.connectChoices);
@@ -1593,9 +1480,15 @@
         dest.writeInt(disableReason);
         dest.writeString(SSID);
         dest.writeString(BSSID);
+        dest.writeInt(apBand);
+        dest.writeInt(apChannel);
         dest.writeString(autoJoinBSSID);
         dest.writeString(FQDN);
-        dest.writeString(naiRealm);
+        dest.writeString(providerFriendlyName);
+        dest.writeInt(roamingConsortiumIds.size());
+        for (Long roamingConsortiumId : roamingConsortiumIds) {
+            dest.writeLong(roamingConsortiumId);
+        }
         dest.writeString(preSharedKey);
         for (String wepKey : wepKeys) {
             dest.writeString(wepKey);
@@ -1658,9 +1551,15 @@
                 config.disableReason = in.readInt();
                 config.SSID = in.readString();
                 config.BSSID = in.readString();
+                config.apBand = in.readInt();
+                config.apChannel = in.readInt();
                 config.autoJoinBSSID = in.readString();
                 config.FQDN = in.readString();
-                config.naiRealm = in.readString();
+                config.providerFriendlyName = in.readString();
+                int numRoamingConsortiumIds = in.readInt();
+                for (int i = 0; i < numRoamingConsortiumIds; i++) {
+                    config.roamingConsortiumIds.add(in.readLong());
+                }
                 config.preSharedKey = in.readString();
                 for (int i = 0; i < config.wepKeys.length; i++) {
                     config.wepKeys[i] = in.readString();
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index cf3cba3..6917971 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -54,6 +54,8 @@
     /** @hide */
     public static final String SUBJECT_MATCH_KEY   = "subject_match";
     /** @hide */
+    public static final String ALTSUBJECT_MATCH_KEY = "altsubject_match";
+    /** @hide */
     public static final String OPP_KEY_CACHING     = "proactive_key_caching";
     /**
      * String representing the keystore OpenSSL ENGINE's ID.
@@ -93,6 +95,11 @@
     public static final String ENGINE_ID_KEY       = "engine_id";
     /** @hide */
     public static final String PRIVATE_KEY_ID_KEY  = "key_id";
+    /** @hide */
+    public static final String REALM_KEY           = "realm";
+    /** @hide */
+    public static final String PLMN_KEY            = "plmn";
+
 
     private HashMap<String, String> mFields = new HashMap<String, String>();
     private X509Certificate mCaCert;
@@ -228,8 +235,10 @@
         public static final int SIM     = 4;
         /** EAP-Authentication and Key Agreement */
         public static final int AKA     = 5;
+        /** EAP-Authentication and Key Agreement Prime */
+        public static final int AKA_PRIME = 6;
         /** @hide */
-        public static final String[] strings = { "PEAP", "TLS", "TTLS", "PWD", "SIM", "AKA" };
+        public static final String[] strings = { "PEAP", "TLS", "TTLS", "PWD", "SIM", "AKA", "AKA'" };
 
         /** Prevent initialization */
         private Eap() {}
@@ -279,6 +288,7 @@
             case Eap.TTLS:
             case Eap.SIM:
             case Eap.AKA:
+            case Eap.AKA_PRIME:
                 mFields.put(EAP_KEY, Eap.strings[eapMethod]);
                 mFields.put(OPP_KEY_CACHING, "1");
                 break;
@@ -530,22 +540,74 @@
     }
 
     /**
-     * Set subject match. This is the substring to be matched against the subject of the
-     * authentication server certificate.
+     * Set subject match (deprecated). This is the substring to be matched against the subject of
+     * the authentication server certificate.
      * @param subjectMatch substring to be matched
+     * @deprecated in favor of altSubjectMatch
      */
     public void setSubjectMatch(String subjectMatch) {
         setFieldValue(SUBJECT_MATCH_KEY, subjectMatch, "");
     }
 
     /**
-     * Get subject match
+     * Get subject match (deprecated)
      * @return the subject match string
+     * @deprecated in favor of altSubjectMatch
      */
     public String getSubjectMatch() {
         return getFieldValue(SUBJECT_MATCH_KEY, "");
     }
 
+    /**
+     * Set alternate subject match. This is the substring to be matched against the
+     * alternate subject of the authentication server certificate.
+     * @param altSubjectMatch substring to be matched, for example
+     *                     DNS:server.example.com;EMAIL:server@example.com
+     */
+    public void setAltSubjectMatch(String altSubjectMatch) {
+        setFieldValue(ALTSUBJECT_MATCH_KEY, altSubjectMatch, "");
+    }
+
+    /**
+     * Get alternate subject match
+     * @return the alternate subject match string
+     */
+    public String getAltSubjectMatch() {
+        return getFieldValue(ALTSUBJECT_MATCH_KEY, "");
+    }
+
+    /**
+     * Set realm for passpoint credential
+     * @param realm the realm
+     */
+    public void setRealm(String realm) {
+        setFieldValue(REALM_KEY, realm, "");
+    }
+
+    /**
+     * Get realm for passpoint credential
+     * @return the realm
+     */
+    public String getRealm() {
+        return getFieldValue(REALM_KEY, "");
+    }
+
+    /**
+     * Set plmn for passpoint credential
+     * @param plmn the plmn value derived from mcc & mnc
+     */
+    public void setPlmn(String plmn) {
+        setFieldValue(PLMN_KEY, plmn, "");
+    }
+
+    /**
+     * Get plmn for passpoint credential
+     * @return the plmn
+     */
+    public String getPlmn() {
+        return getFieldValue(PLMN_KEY, "");
+    }
+
     /** See {@link WifiConfiguration#getKeyIdForCredentials} @hide */
     String getKeyId(WifiEnterpriseConfig current) {
         String eap = mFields.get(EAP_KEY);
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 97e10dc..2058645 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -623,6 +623,20 @@
     }
 
     /**
+     * Returns a WifiConfiguration matching this ScanResult
+     * @param scanResult scanResult that represents the BSSID
+     * @return {@link WifiConfiguration} that matches this BSSID or null
+     * @hide
+     */
+    public WifiConfiguration getMatchingWifiConfig(ScanResult scanResult) {
+        try {
+            return mService.getMatchingWifiConfig(scanResult);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
      * Add a new network description to the set of configured networks.
      * The {@code networkId} field of the supplied configuration object
      * is ignored.
@@ -839,6 +853,14 @@
     public static final int WIFI_FEATURE_TDLS_OFFCHANNEL  = 0x2000;  // Support for TDLS off channel
     /** @hide */
     public static final int WIFI_FEATURE_EPR              = 0x4000;  // Enhanced power reporting
+    /** @hide */
+    public static final int WIFI_FEATURE_AP_STA            = 0x8000;  // Support for AP STA Concurrency
+    /** @hide */
+    public static final int WIFI_FEATURE_LINK_LAYER_STATS  = 0x10000; // Link layer stats collection
+    /** @hide */
+    public static final int WIFI_FEATURE_LOGGER            = 0x20000; // WiFi Logger
+    /** @hide */
+    public static final int WIFI_FEATURE_HAL_EPNO          = 0x40000; // WiFi PNO enhanced
 
     private int getSupportedFeatures() {
         try {
@@ -950,7 +972,7 @@
      * @return true if this adapter supports advanced power/performance counters
      */
     public boolean isEnhancedPowerReportingSupported() {
-        return isFeatureSupported(WIFI_FEATURE_EPR);
+        return isFeatureSupported(WIFI_FEATURE_LINK_LAYER_STATS);
     }
 
     /**
@@ -1238,6 +1260,21 @@
     }
 
     /**
+    * get the country code.
+    * @return the country code in ISO 3166 format.
+    *
+    * @hide
+    */
+    public String getCountryCode() {
+       try {
+           String country = mService.getCountryCode();
+           return(country);
+       } catch (RemoteException e) {
+           return null;
+       }
+    }
+
+    /**
      * Set the operational frequency band.
      * @param band  One of
      *     {@link #WIFI_FREQUENCY_BAND_AUTO},
@@ -1443,6 +1480,20 @@
     }
 
     /**
+     * Builds a WifiConfiguration from Hotspot 2.0 MIME file.
+     * @return AP details in WifiConfiguration
+     *
+     * @hide Dont open yet
+     */
+    public WifiConfiguration buildWifiConfig(String uriString, String mimeType, byte[] data) {
+        try {
+            return mService.buildWifiConfig(uriString, mimeType, data);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
      * Sets the Wi-Fi AP Configuration.
      * @return {@code true} if the operation succeeded, {@code false} otherwise
      *
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index aaa2f98..fea934f 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -158,6 +158,11 @@
         public int reportEvents;
         /** defines number of bssids to cache from each scan */
         public int numBssidsPerScan;
+        /**
+         * defines number of scans to cache; use it with REPORT_EVENT_AFTER_BUFFER_FULL
+         * to wake up at fixed interval
+         */
+        public int maxScansToCache;
 
         /** Implement the Parcelable interface {@hide} */
         public int describeContents() {
@@ -170,6 +175,7 @@
             dest.writeInt(periodInMs);
             dest.writeInt(reportEvents);
             dest.writeInt(numBssidsPerScan);
+            dest.writeInt(maxScansToCache);
 
             if (channels != null) {
                 dest.writeInt(channels.length);
@@ -194,6 +200,7 @@
                         settings.periodInMs = in.readInt();
                         settings.reportEvents = in.readInt();
                         settings.numBssidsPerScan = in.readInt();
+                        settings.maxScansToCache = in.readInt();
                         int num_channels = in.readInt();
                         settings.channels = new ChannelSpec[num_channels];
                         for (int i = 0; i < num_channels; i++) {
@@ -215,8 +222,143 @@
 
     }
 
-    /** @hide */
+    /**
+     * all the information garnered from a single scan
+     */
+    public static class ScanData implements Parcelable {
+        /** scan identifier */
+        private int mId;
+        /** additional information about scan
+         * 0 => no special issues encountered in the scan
+         * non-zero => scan was truncated, so results may not be complete
+         */
+        private int mFlags;
+        /** all scan results discovered in this scan, sorted by timestamp in ascending order */
+        private ScanResult mResults[];
+
+        ScanData() {}
+
+        public ScanData(int id, int flags, ScanResult[] results) {
+            mId = id;
+            mFlags = flags;
+            mResults = results;
+        }
+
+        public ScanData(ScanData s) {
+            mId = s.mId;
+            mFlags = s.mFlags;
+            mResults = new ScanResult[s.mResults.length];
+            for (int i = 0; i < s.mResults.length; i++) {
+                ScanResult result = s.mResults[i];
+                WifiSsid wifiSsid = WifiSsid.createFromAsciiEncoded(result.SSID);
+                ScanResult newResult = new ScanResult(result);
+                newResult.wifiSsid = wifiSsid;
+                mResults[i] = newResult;
+            }
+        }
+
+        public int getId() {
+            return mId;
+        }
+
+        public int getFlags() {
+            return mFlags;
+        }
+
+        public ScanResult[] getResults() {
+            return mResults;
+        }
+
+        /** Implement the Parcelable interface {@hide} */
+        public int describeContents() {
+            return 0;
+        }
+
+        /** Implement the Parcelable interface {@hide} */
+        public void writeToParcel(Parcel dest, int flags) {
+            if (mResults != null) {
+                dest.writeInt(mId);
+                dest.writeInt(mFlags);
+                dest.writeInt(mResults.length);
+                for (int i = 0; i < mResults.length; i++) {
+                    ScanResult result = mResults[i];
+                    result.writeToParcel(dest, flags);
+                }
+            } else {
+                dest.writeInt(0);
+            }
+        }
+
+        /** Implement the Parcelable interface {@hide} */
+        public static final Creator<ScanData> CREATOR =
+                new Creator<ScanData>() {
+                    public ScanData createFromParcel(Parcel in) {
+                        int id = in.readInt();
+                        int flags = in.readInt();
+                        int n = in.readInt();
+                        ScanResult results[] = new ScanResult[n];
+                        for (int i = 0; i < n; i++) {
+                            results[i] = ScanResult.CREATOR.createFromParcel(in);
+                        }
+                        return new ScanData(id, flags, results);
+                    }
+
+                    public ScanData[] newArray(int size) {
+                        return new ScanData[size];
+                    }
+                };
+    }
+
+    public static class ParcelableScanData implements Parcelable {
+
+        public ScanData mResults[];
+
+        public ParcelableScanData(ScanData[] results) {
+            mResults = results;
+        }
+
+        public ScanData[] getResults() {
+            return mResults;
+        }
+
+        /** Implement the Parcelable interface {@hide} */
+        public int describeContents() {
+            return 0;
+        }
+
+        /** Implement the Parcelable interface {@hide} */
+        public void writeToParcel(Parcel dest, int flags) {
+            if (mResults != null) {
+                dest.writeInt(mResults.length);
+                for (int i = 0; i < mResults.length; i++) {
+                    ScanData result = mResults[i];
+                    result.writeToParcel(dest, flags);
+                }
+            } else {
+                dest.writeInt(0);
+            }
+        }
+
+        /** Implement the Parcelable interface {@hide} */
+        public static final Creator<ParcelableScanData> CREATOR =
+                new Creator<ParcelableScanData>() {
+                    public ParcelableScanData createFromParcel(Parcel in) {
+                        int n = in.readInt();
+                        ScanData results[] = new ScanData[n];
+                        for (int i = 0; i < n; i++) {
+                            results[i] = ScanData.CREATOR.createFromParcel(in);
+                        }
+                        return new ParcelableScanData(results);
+                    }
+
+                    public ParcelableScanData[] newArray(int size) {
+                        return new ParcelableScanData[size];
+                    }
+                };
+    }
+
     public static class ParcelableScanResults implements Parcelable {
+
         public ScanResult mResults[];
 
         public ParcelableScanResults(ScanResult[] results) {
@@ -264,7 +406,8 @@
     }
 
     /**
-     * interface to get scan events on; specify this on {@link #startBackgroundScan}
+     * interface to get scan events on; specify this on {@link #startBackgroundScan} or
+     * {@link #startScan}
      */
     public interface ScanListener extends ActionListener {
         /**
@@ -273,10 +416,15 @@
          */
         public void onPeriodChanged(int periodInMs);
         /**
-         * reports results retrieved from background scan
+         * reports results retrieved from background scan and single shot scans
+         * @deprecated in favor of {@link #onResults(ScanData[])}
          */
         public void onResults(ScanResult[] results);
         /**
+         * reports results retrieved from background scan and single shot scans
+         */
+        public void onResults(ScanData[] results);
+        /**
          * reports full scan result for each access point found in scan
          */
         public void onFullResult(ScanResult fullScanResult);
@@ -303,13 +451,36 @@
         sAsyncChannel.sendMessage(CMD_STOP_BACKGROUND_SCAN, 0, removeListener(listener));
     }
     /**
-     * retrieves currently available scan results
+     * reports currently available scan results on appropriate listeners
      */
     public ScanResult[] getScanResults() {
         validateChannel();
         Message reply = sAsyncChannel.sendMessageSynchronously(CMD_GET_SCAN_RESULTS, 0);
-        ScanResult[] results = (ScanResult[]) reply.obj;
-        return results;
+        // return reply.what == CMD_OP_SUCCEEDED;
+        return null;
+    }
+
+    /**
+     * starts a single scan and reports results asynchronously
+     * @param settings specifies various parameters for the scan; for more information look at
+     * {@link ScanSettings}
+     * @param listener specifies the object to report events to. This object is also treated as a
+     *                 key for this scan, and must also be specified to cancel the scan. Multiple
+     *                 scans should also not share this object.
+     */
+    public void startScan(ScanSettings settings, ScanListener listener) {
+        validateChannel();
+        sAsyncChannel.sendMessage(CMD_START_SINGLE_SCAN, 0, putListener(listener), settings);
+    }
+
+    /**
+     * stops an ongoing single shot scan; only useful after {@link #startScan} if onResults()
+     * hasn't been called on the listener, ignored otherwise
+     * @param listener
+     */
+    public void stopScan(ScanListener listener) {
+        validateChannel();
+        sAsyncChannel.sendMessage(CMD_STOP_SINGLE_SCAN, 0, removeListener(listener));
     }
 
     /** specifies information about an access point of interest */
@@ -468,6 +639,10 @@
          * @param results list of scan results, one for each access point visible currently
          */
         public void onFound(ScanResult[] results);
+        /** indicates that access points were missed by on going scans
+         * @param results list of scan results, for each access point that is not visible anymore
+         */
+        public void onLost(ScanResult[] results);
     }
 
     /** @hide */
@@ -593,6 +768,12 @@
     public static final int CMD_PERIOD_CHANGED              = BASE + 19;
     /** @hide */
     public static final int CMD_FULL_SCAN_RESULT            = BASE + 20;
+    /** @hide */
+    public static final int CMD_START_SINGLE_SCAN           = BASE + 21;
+    /** @hide */
+    public static final int CMD_STOP_SINGLE_SCAN            = BASE + 22;
+    /** @hide */
+    public static final int CMD_SINGLE_SCAN_COMPLETED       = BASE + 23;
 
     private Context mContext;
     private IWifiScanner mService;
@@ -800,7 +981,7 @@
                     break;
                 case CMD_SCAN_RESULT :
                     ((ScanListener) listener).onResults(
-                            ((ParcelableScanResults) msg.obj).getResults());
+                            ((ParcelableScanData) msg.obj).getResults());
                     return;
                 case CMD_FULL_SCAN_RESULT :
                     ScanResult result = (ScanResult) msg.obj;
@@ -813,6 +994,10 @@
                     ((BssidListener) listener).onFound(
                             ((ParcelableScanResults) msg.obj).getResults());
                     return;
+                case CMD_AP_LOST:
+                    ((BssidListener) listener).onLost(
+                            ((ParcelableScanResults) msg.obj).getResults());
+                    return;
                 case CMD_WIFI_CHANGE_DETECTED:
                     ((WifiChangeListener) listener).onChanging(
                             ((ParcelableScanResults) msg.obj).getResults());
@@ -821,6 +1006,10 @@
                     ((WifiChangeListener) listener).onQuiescence(
                             ((ParcelableScanResults) msg.obj).getResults());
                     return;
+                case CMD_SINGLE_SCAN_COMPLETED:
+                    Log.d(TAG, "removing listener for single scan");
+                    removeListener(msg.arg2);
+                    break;
                 default:
                     if (DBG) Log.d(TAG, "Ignoring message " + msg.what);
                     return;
diff --git a/wifi/java/android/net/wifi/passpoint/IWifiPasspointManager.aidl b/wifi/java/android/net/wifi/passpoint/IWifiPasspointManager.aidl
new file mode 100644
index 0000000..50bec33
--- /dev/null
+++ b/wifi/java/android/net/wifi/passpoint/IWifiPasspointManager.aidl
@@ -0,0 +1,45 @@
+/**
+ * Copyright (c) 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.passpoint;
+
+import android.net.wifi.ScanResult;
+import android.net.wifi.passpoint.WifiPasspointPolicy;
+import android.net.wifi.passpoint.WifiPasspointCredential;
+import android.os.Messenger;
+
+/**
+ * Interface that allows controlling and querying Wifi Passpoint connectivity.
+ *
+ * {@hide}
+ */
+interface IWifiPasspointManager
+{
+    Messenger getMessenger();
+
+    int getPasspointState();
+
+    List<WifiPasspointPolicy> requestCredentialMatch(in List<ScanResult> requested);
+
+    List<WifiPasspointCredential> getCredentials();
+
+    boolean addCredential(in WifiPasspointCredential cred);
+
+    boolean updateCredential(in WifiPasspointCredential cred);
+
+    boolean removeCredential(in WifiPasspointCredential cred);
+}
+
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.aidl b/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.aidl
new file mode 100644
index 0000000..cfd3605
--- /dev/null
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.passpoint;
+
+parcelable WifiPasspointCredential;
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java
new file mode 100644
index 0000000..0a7230f
--- /dev/null
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointCredential.java
@@ -0,0 +1,667 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.passpoint;
+
+import android.net.wifi.WifiEnterpriseConfig;
+import android.os.Parcelable;
+import android.os.Parcel;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+
+/**
+ * A class representing a Wi-Fi Passpoint credential.
+ * @hide
+ */
+public class WifiPasspointCredential implements Parcelable {
+
+    private final static String TAG = "PasspointCredential";
+    private final static boolean DBG = true;
+
+    /** Wi-Fi nodes**/
+    private String mWifiSpFqdn;
+
+    /** PerProviderSubscription nodes **/
+    private String mCredentialName;
+
+    /** SubscriptionUpdate nodes **/
+    private String mSubscriptionUpdateInterval;
+    private String mSubscriptionUpdateMethod;
+    private String mSubscriptionUpdateRestriction;
+    private String mSubscriptionUpdateURI;
+    private String mSubscriptionUpdateUsername;
+    private String mSubscriptionUpdatePassword;
+
+    /** HomeSP nodes **/
+    private String mHomeSpFqdn;
+    private String mFriendlyName;
+    private Collection<WifiPasspointDmTree.HomeOIList> mHomeOIList;
+    private Collection<WifiPasspointDmTree.OtherHomePartners> mOtherHomePartnerList;
+
+    /** SubscriptionParameters nodes**/
+    private String mCreationDate;
+    private String mExpirationDate;
+
+    /** Credential nodes **/
+    private String mType;
+    private String mInnerMethod;
+    private String mCertType;
+    private String mCertSha256Fingerprint;
+    private String mUpdateIdentifier;
+    private String mUsername;
+    private String mPasswd;
+    private String mRealm;
+    private String mImsi;
+    private String mMcc;
+    private String mMnc;
+    private String mCaRootCert;
+    private String mClientCert;
+    private boolean mCheckAaaServerCertStatus;
+
+    /** Policy nodes **/
+    private String mPolicyUpdateUri;
+    private String mPolicyUpdateInterval;
+    private String mPolicyUpdateUsername;
+    private String mPolicyUpdatePassword;
+    private String mPolicyUpdateRestriction;
+    private String mPolicyUpdateMethod;
+    private Collection<WifiPasspointDmTree.PreferredRoamingPartnerList> mPreferredRoamingPartnerList;
+    private Collection<WifiPasspointDmTree.MinBackhaulThresholdNetwork> mMinBackhaulThresholdNetwork;
+    private Collection<WifiPasspointDmTree.SPExclusionList> mSpExclusionList;
+    private Collection<WifiPasspointDmTree.RequiredProtoPortTuple> mRequiredProtoPortTuple;
+    private String mMaxBssLoad;
+
+    /** CrednetialPriority node **/
+    private int mCrednetialPriority;
+
+    /** AAAServerTrustRoot nodes **/
+    private String mAaaCertUrl;
+    private String mAaaSha256Fingerprint;
+
+    /** Others **/
+    private boolean mIsMachineRemediation;
+    private boolean mUserPreferred = false;
+    private String mWifiTreePath;
+    private WifiEnterpriseConfig mEnterpriseConfig;
+
+    /** @hide */
+    public WifiPasspointCredential() {}
+
+    /**
+     * Constructor
+     * @param realm Realm of the passpoint credential
+     * @param fqdn Fully qualified domain name (FQDN) of the credential
+     * @param config Enterprise config, must be either EAP-TLS or EAP-TTLS
+     * @see WifiEnterpriseConfig
+     */
+    public WifiPasspointCredential(String realm, String fqdn, WifiEnterpriseConfig config) {
+        mRealm = realm;
+        switch (config.getEapMethod()) {
+            case WifiEnterpriseConfig.Eap.TLS:
+            case WifiEnterpriseConfig.Eap.TTLS:
+                mEnterpriseConfig = new WifiEnterpriseConfig(config);
+                break;
+            default:
+                // ignore
+        }
+    }
+
+    /** @hide */
+    public WifiPasspointCredential(String type,
+            String caroot,
+            String clientcert,
+            String mcc,
+            String mnc,
+            WifiPasspointDmTree.SpFqdn sp,
+            WifiPasspointDmTree.CredentialInfo credinfo) {
+
+        if (credinfo == null) {
+            return;
+        }
+
+        mType = type;
+        mCaRootCert = caroot;
+        mClientCert = clientcert;
+
+        mWifiSpFqdn = sp.nodeName;
+        mUpdateIdentifier = sp.perProviderSubscription.UpdateIdentifier;
+
+        mCredentialName = credinfo.nodeName;
+        mOtherHomePartnerList = credinfo.homeSP.otherHomePartners.values();
+
+        Set set = credinfo.aAAServerTrustRoot.entrySet();
+        Iterator i = set.iterator();
+        if (i.hasNext()) {
+            Map.Entry entry3 = (Map.Entry) i.next();
+            WifiPasspointDmTree.AAAServerTrustRoot aaa = (WifiPasspointDmTree.AAAServerTrustRoot) entry3.getValue();
+            mAaaCertUrl = aaa.CertURL;
+            mAaaSha256Fingerprint = aaa.CertSHA256Fingerprint;
+        }
+
+        mCertType = credinfo.credential.digitalCertificate.CertificateType;
+        mCertSha256Fingerprint = credinfo.credential.digitalCertificate.CertSHA256Fingerprint;
+        mUsername = credinfo.credential.usernamePassword.Username;
+        mPasswd = credinfo.credential.usernamePassword.Password;
+        mIsMachineRemediation = credinfo.credential.usernamePassword.MachineManaged;
+        mInnerMethod = credinfo.credential.usernamePassword.eAPMethod.InnerMethod;
+        mImsi = credinfo.credential.sim.IMSI;
+        mMcc = mcc;
+        mMnc = mnc;
+        mCreationDate = credinfo.credential.CreationDate;
+        mExpirationDate = credinfo.credential.ExpirationDate;
+        mRealm = credinfo.credential.Realm;
+
+        if (credinfo.credentialPriority == null) {
+            mCrednetialPriority = 128;
+        } else {
+            mCrednetialPriority = Integer.parseInt(credinfo.credentialPriority);
+        }
+
+        mHomeSpFqdn = credinfo.homeSP.FQDN;
+
+        mSubscriptionUpdateInterval = credinfo.subscriptionUpdate.UpdateInterval;
+        mSubscriptionUpdateMethod = credinfo.subscriptionUpdate.UpdateMethod;
+        mSubscriptionUpdateRestriction = credinfo.subscriptionUpdate.Restriction;
+        mSubscriptionUpdateURI = credinfo.subscriptionUpdate.URI;
+        mSubscriptionUpdateUsername = credinfo.subscriptionUpdate.usernamePassword.Username;
+        mSubscriptionUpdatePassword = credinfo.subscriptionUpdate.usernamePassword.Password;
+
+        mPolicyUpdateUri = credinfo.policy.policyUpdate.URI;
+        mPolicyUpdateInterval = credinfo.policy.policyUpdate.UpdateInterval;
+        mPolicyUpdateUsername = credinfo.policy.policyUpdate.usernamePassword.Username;
+        mPolicyUpdatePassword = credinfo.policy.policyUpdate.usernamePassword.Password;
+        mPolicyUpdateRestriction = credinfo.policy.policyUpdate.Restriction;
+        mPolicyUpdateMethod = credinfo.policy.policyUpdate.UpdateMethod;
+        mPreferredRoamingPartnerList = credinfo.policy.preferredRoamingPartnerList.values();
+        mMinBackhaulThresholdNetwork = credinfo.policy.minBackhaulThreshold.values();
+        mRequiredProtoPortTuple = credinfo.policy.requiredProtoPortTuple.values();
+        mMaxBssLoad = credinfo.policy.maximumBSSLoadValue;
+        mSpExclusionList = credinfo.policy.sPExclusionList.values();
+
+        mHomeOIList = credinfo.homeSP.homeOIList.values();
+        mFriendlyName = credinfo.homeSP.FriendlyName;
+        mCheckAaaServerCertStatus = credinfo.credential.CheckAAAServerCertStatus;
+    }
+
+    /** @hide */
+    public String getUpdateIdentifier() {
+        return mUpdateIdentifier;
+    }
+
+    /** @hide */
+    public String getUpdateMethod() {
+        return mSubscriptionUpdateMethod;
+    }
+
+    /** @hide */
+    public void setUpdateMethod(String method) {
+        mSubscriptionUpdateMethod = method;
+    }
+
+    /** @hide */
+    public String getWifiSpFqdn() {
+        return mWifiSpFqdn;
+    }
+
+    /** @hide */
+    public String getCredName() {
+        return mCredentialName;
+    }
+
+    /** @hide */
+    public String getType() {
+        return mType;
+    }
+
+    /**
+     * Get enterprise config of this Passpoint credential.
+     * @return Enterprise config
+     * @see WifiEnterpriseConfig
+     */
+    public WifiEnterpriseConfig getEnterpriseConfig() {
+        return new WifiEnterpriseConfig(mEnterpriseConfig);
+    }
+
+    /**
+     * Set enterprise config of this Passpoint credential.
+     * @param config Enterprise config, must be either EAP-TLS or EAP-TTLS
+     * @see WifiEnterpriseConfig
+     */
+    public void setEnterpriseConfig(WifiEnterpriseConfig config) {
+        // TODO
+    }
+
+    /** @hide */
+    public String getCertType() {
+        return mCertType;
+    }
+
+    /** @hide */
+    public String getCertSha256Fingerprint() {
+        return mCertSha256Fingerprint;
+    }
+
+    /** @hide */
+    public String getUserName() {
+        return mUsername;
+    }
+
+    /** @hide */
+    public String getPassword() {
+        // TODO: guarded by connectivity internal
+        return mPasswd;
+    }
+
+    /** @hide */
+    public String getImsi() {
+        return mImsi;
+    }
+
+    /** @hide */
+    public String getMcc() {
+        return mMcc;
+    }
+
+    /** @hide */
+    public String getMnc() {
+        return mMnc;
+    }
+
+    /** @hide */
+    public String getCaRootCertPath() {
+        return mCaRootCert;
+    }
+
+    /** @hide */
+    public String getClientCertPath() {
+        return mClientCert;
+    }
+
+    /**
+     * Get the realm of this Passpoint credential.
+     * @return Realm
+     */
+    public String getRealm() {
+        return mRealm;
+    }
+
+    /**
+     * Set the ream of this Passpoint credential.
+     * @param realm Realm
+     */
+    public void setRealm(String realm) {
+        mRealm = realm;
+    }
+
+    /** @hide */
+    public int getPriority() {
+        if (mUserPreferred) {
+            return 0;
+        }
+
+        return mCrednetialPriority;
+    }
+
+    /**
+     * Get the fully qualified domain name (FQDN) of this Passpoint credential.
+     * @return FQDN
+     */
+    public String getHomeSpFqdn() {
+        return mHomeSpFqdn;
+    }
+
+    /**
+     * Set the fully qualified domain name (FQDN) of this Passpoint credential.
+     * @param fqdn FQDN
+     */
+    public void setHomeFqdn(String fqdn) {
+        mHomeSpFqdn = fqdn;
+    }
+
+
+    /** @hide */
+    public Collection<WifiPasspointDmTree.OtherHomePartners> getOtherHomePartnerList() {
+        return mOtherHomePartnerList;
+    }
+
+    /** @hide */
+    public String getSubscriptionUpdateUsername() {
+        return mSubscriptionUpdateUsername;
+    }
+
+    /** @hide */
+    public String getSubscriptionUpdatePassword() {
+        return mSubscriptionUpdatePassword;
+    }
+
+    /** @hide */
+    public String getPolicyUpdateUri() {
+        return mPolicyUpdateUri;
+    }
+
+    /** @hide */
+    public String getPolicyUpdateInterval() {
+        return mPolicyUpdateInterval;
+    }
+
+    /** @hide */
+    public String getPolicyUpdateUsername() {
+        return mPolicyUpdateUsername;
+    }
+
+    /** @hide */
+    public String getPolicyUpdatePassword() {
+        return mPolicyUpdatePassword;
+    }
+
+    /** @hide */
+    public String getPolicyUpdateRestriction() {
+        return mPolicyUpdateRestriction;
+    }
+
+    /** @hide */
+    public String getPolicyUpdateMethod() {
+        return mPolicyUpdateMethod;
+    }
+
+    /** @hide */
+    public String getCreationDate() {
+        return mCreationDate;
+    }
+
+    /** @hide */
+    public String getExpirationDate() {
+        return mExpirationDate;
+    }
+
+    /** @hide */
+    public void setExpirationDate(String expirationdate) {
+        mExpirationDate = expirationdate;
+    }
+
+    /** @hide */
+    public Collection<WifiPasspointDmTree.PreferredRoamingPartnerList> getPreferredRoamingPartnerList() {
+        return mPreferredRoamingPartnerList;
+    }
+
+    /** @hide */
+    public Collection<WifiPasspointDmTree.HomeOIList> getHomeOiList() {
+        return mHomeOIList;
+    }
+
+    /** @hide */
+    public Collection<WifiPasspointDmTree.MinBackhaulThresholdNetwork> getBackhaulThresholdList() {
+        return mMinBackhaulThresholdNetwork;
+    }
+
+    /** @hide */
+    public Collection<WifiPasspointDmTree.RequiredProtoPortTuple> getRequiredProtoPortList() {
+        return mRequiredProtoPortTuple;
+    }
+
+    /** @hide */
+    public Collection<WifiPasspointDmTree.SPExclusionList> getSPExclusionList() {
+        return mSpExclusionList;
+    }
+
+    /** @hide */
+    public boolean getIsMachineRemediation() {
+        return mIsMachineRemediation;
+    }
+
+    /** @hide */
+    public String getAaaCertUrl() {
+        return mAaaCertUrl;
+    }
+
+    /** @hide */
+    public String getAaaSha256Fingerprint() {
+        return mAaaSha256Fingerprint;
+    }
+
+    /** @hide */
+    public String getSubscriptionUpdateRestriction() {
+        return mSubscriptionUpdateRestriction;
+    }
+
+    /** @hide */
+    public String getSubscriptionUpdateURI() {
+        return mSubscriptionUpdateURI;
+    }
+
+    /** @hide */
+    public String getSubscriptionUpdateInterval() {
+        return mSubscriptionUpdateInterval;
+    }
+
+    /** @hide */
+    public String getFriendlyName() {
+        return mFriendlyName;
+    }
+
+    /** @hide */
+    public String getMaxBssLoad() {
+        return mMaxBssLoad;
+    }
+
+    /** @hide */
+    public boolean getUserPreference() {
+        return mUserPreferred;
+    }
+
+    /** @hide */
+    public boolean getCheckAaaServerCertStatus() {
+        return mCheckAaaServerCertStatus;
+    }
+
+    /** @hide */
+    public void setUserPreference(boolean value) {
+        mUserPreferred = value;
+    }
+
+    @Override
+    /** @hide */
+    public boolean equals(Object obj) {
+        boolean result = false;
+        if (obj instanceof WifiPasspointCredential) {
+            final WifiPasspointCredential other = (WifiPasspointCredential) obj;
+            if (this.mType.equals(other.mType)) {
+                if (this.mType.equals("TTLS")) {
+                    result = this.mUsername.equals(other.mUsername) &&
+                            this.mPasswd.equals(other.mPasswd) &&
+                            this.mRealm.equals(other.mRealm) &&
+                            this.mHomeSpFqdn.equals(other.mHomeSpFqdn);
+                }
+                if (this.mType.equals("TLS")) {
+                    result = this.mRealm.equals(other.mRealm) &&
+                            this.mHomeSpFqdn.equals(other.mHomeSpFqdn) &&
+                            this.mClientCert.equals(other.mClientCert);
+                }
+                if (this.mType.equals("SIM")) {
+                    result = this.mMcc.equals(other.mMcc) &&
+                            this.mMnc.equals(other.mMnc) &&
+                            this.mImsi.equals(other.mImsi) &&
+                            this.mHomeSpFqdn.equals(other.mHomeSpFqdn);
+                }
+            }
+        }
+        return result;
+    }
+
+    @Override
+    /** @hide */
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+        String none = "<none>";
+
+        if (!DBG) {
+            sb.append(none);
+        } else {
+            sb.append(", UpdateIdentifier: ")
+            .append(mUpdateIdentifier == null ? none : mUpdateIdentifier)
+            .append(", SubscriptionUpdateMethod: ")
+            .append(mSubscriptionUpdateMethod == null ? none : mSubscriptionUpdateMethod)
+            .append(", Type: ").append(mType == null ? none : mType)
+            .append(", Username: ").append(mUsername == null ? none : mUsername)
+            .append(", Passwd: ").append(mPasswd == null ? none : mPasswd)
+            .append(", SubDMAccUsername: ")
+            .append(mSubscriptionUpdateUsername == null ? none : mSubscriptionUpdateUsername)
+            .append(", SubDMAccPassword: ")
+            .append(mSubscriptionUpdatePassword == null ? none : mSubscriptionUpdatePassword)
+            .append(", PolDMAccUsername: ")
+            .append(mPolicyUpdateUsername == null ? none : mPolicyUpdateUsername)
+            .append(", PolDMAccPassword: ")
+            .append(mPolicyUpdatePassword == null ? none : mPolicyUpdatePassword)
+            .append(", Imsi: ").append(mImsi == null ? none : mImsi)
+            .append(", Mcc: ").append(mMcc == null ? none : mMcc)
+            .append(", Mnc: ").append(mMnc == null ? none : mMnc)
+            .append(", CaRootCert: ").append(mCaRootCert == null ? none : mCaRootCert)
+            .append(", Realm: ").append(mRealm == null ? none : mRealm)
+            .append(", Priority: ").append(mCrednetialPriority)
+            .append(", Fqdn: ").append(mHomeSpFqdn == null ? none : mHomeSpFqdn)
+            .append(", Otherhomepartners: ")
+            .append(mOtherHomePartnerList == null ? none : mOtherHomePartnerList)
+            .append(", ExpirationDate: ")
+            .append(mExpirationDate == null ? none : mExpirationDate)
+            .append(", MaxBssLoad: ").append(mMaxBssLoad == null ? none : mMaxBssLoad)
+            .append(", SPExclusionList: ").append(mSpExclusionList);
+
+            if (mPreferredRoamingPartnerList != null) {
+                sb.append("PreferredRoamingPartnerList:");
+                for (WifiPasspointDmTree.PreferredRoamingPartnerList prpListItem : mPreferredRoamingPartnerList) {
+                    sb.append("[fqdnmatch:").append(prpListItem.FQDN_Match).
+                            append(", priority:").append(prpListItem.Priority).
+                            append(", country:").append(prpListItem.Country).append("]");
+                }
+            }
+
+            if (mHomeOIList != null) {
+                sb.append("HomeOIList:");
+                for (WifiPasspointDmTree.HomeOIList HomeOIListItem : mHomeOIList) {
+                    sb.append("[HomeOI:").append(HomeOIListItem.HomeOI).
+                            append(", HomeOIRequired:").append(HomeOIListItem.HomeOIRequired).
+                            append("]");
+                }
+            }
+
+            if (mMinBackhaulThresholdNetwork != null) {
+                sb.append("BackHaulThreshold:");
+                for (WifiPasspointDmTree.MinBackhaulThresholdNetwork BhtListItem : mMinBackhaulThresholdNetwork) {
+                    sb.append("[networkType:").append(BhtListItem.NetworkType).
+                            append(", dlBandwidth:").append(BhtListItem.DLBandwidth).
+                            append(", ulBandwidth:").append(BhtListItem.ULBandwidth).
+                            append("]");
+                }
+            }
+
+            if (mRequiredProtoPortTuple != null) {
+                sb.append("WifiMORequiredProtoPortTupleList:");
+                for (WifiPasspointDmTree.RequiredProtoPortTuple RpptListItem : mRequiredProtoPortTuple) {
+                    sb.append("[IPProtocol:").append(RpptListItem.IPProtocol).
+                            append(", PortNumber:").append(RpptListItem.PortNumber).
+                            append("]");
+                }
+            }
+        }
+        return sb.toString();
+    }
+
+    /** Implement the Parcelable interface {@hide} */
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Implement the Parcelable interface {@hide} */
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mWifiSpFqdn);
+        dest.writeString(mCredentialName);
+        dest.writeString(mType);
+        dest.writeInt(mCrednetialPriority);
+        dest.writeString(mHomeSpFqdn);
+        dest.writeString(mRealm);
+    }
+
+    /** Implement the Parcelable interface {@hide} */
+    public void readFromParcel(Parcel in) {
+        mWifiSpFqdn = in.readString();
+        mCredentialName = in.readString();
+        mType = in.readString();
+        mCrednetialPriority = in.readInt();
+        mHomeSpFqdn = in.readString();
+        mRealm = in.readString();
+    }
+
+    /** Implement the Parcelable interface {@hide} */
+    public static final Creator<WifiPasspointCredential> CREATOR =
+            new Creator<WifiPasspointCredential>() {
+                public WifiPasspointCredential createFromParcel(Parcel in) {
+                    WifiPasspointCredential pc = new WifiPasspointCredential();
+                    pc.mWifiSpFqdn = in.readString();
+                    pc.mCredentialName = in.readString();
+                    pc.mType = in.readString();
+                    pc.mCrednetialPriority = in.readInt();
+                    pc.mHomeSpFqdn = in.readString();
+                    pc.mRealm = in.readString();
+                    return pc;
+                }
+
+                public WifiPasspointCredential[] newArray(int size) {
+                    return new WifiPasspointCredential[size];
+                }
+            };
+
+    /** @hide */
+    public int compareTo(WifiPasspointCredential another) {
+
+        //The smaller the higher
+        if (mCrednetialPriority < another.mCrednetialPriority) {
+            return -1;
+        } else if (mCrednetialPriority == another.mCrednetialPriority) {
+            return this.mType.compareTo(another.mType);
+        } else {
+            return 1;
+        }
+    }
+
+    @Override
+    /** @hide */
+    public int hashCode() {
+        int hash = 208;
+        if (mType != null) {
+            hash += mType.hashCode();
+        }
+        if (mRealm != null) {
+            hash += mRealm.hashCode();
+        }
+        if (mHomeSpFqdn != null) {
+            hash += mHomeSpFqdn.hashCode();
+        }
+        if (mUsername != null) {
+            hash += mUsername.hashCode();
+        }
+        if (mPasswd != null) {
+            hash += mPasswd.hashCode();
+        }
+
+        return hash;
+    }
+}
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointDmTree.aidl b/wifi/java/android/net/wifi/passpoint/WifiPasspointDmTree.aidl
new file mode 100644
index 0000000..6a88b2e
--- /dev/null
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointDmTree.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.passpoint;
+
+parcelable WifiPasspointDmTree;
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointDmTree.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointDmTree.java
new file mode 100644
index 0000000..bbf5fc6
--- /dev/null
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointDmTree.java
@@ -0,0 +1,1377 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.passpoint;
+
+import android.os.Parcelable;
+import android.os.Parcel;
+import android.util.Log;
+
+import java.util.HashMap;
+
+/**
+ * Required Mobile Device Management Tree Structure
+ *
+ *                   +----------+
+ *                   | ./(Root) |
+ *                   +----+-----+
+ *                        |
+ *  +---------+           |         +---------+  +---------+
+ *  | DevInfo |-----------+---------|  Wi-Fi  |--|SP FQDN* |
+ *  +---------+           |         +---------+  +---------+
+ *  +---------+           |                           |
+ *  |DevDetail|-----------+                      +-----------------------+
+ *  +---------+                                  |PerproviderSubscription|--<X>+
+ *                                               +-----------------------+
+ *
+ * This class contains all nodes start from Wi-Fi
+ * @hide
+ **/
+public class WifiPasspointDmTree implements Parcelable {
+    private final static String TAG = "WifiTree";
+    public int PpsMoId;//plugfest used only
+    public HashMap<String, SpFqdn> spFqdn = new HashMap<String, SpFqdn>();//Maps.newHashMap();
+
+    public SpFqdn createSpFqdn(String name) {
+        SpFqdn obj = new SpFqdn(name);
+        spFqdn.put(name, obj);
+        return obj;
+    }
+
+    public static class SpFqdn implements Parcelable {
+        public String nodeName;
+        public PerProviderSubscription perProviderSubscription = new PerProviderSubscription();
+
+        public SpFqdn(String name) {
+            nodeName = name;
+        }
+
+        public SpFqdn() {
+        }
+
+        public SpFqdn(Parcel in) {
+            readFromParcel(in);
+        }
+
+        public int describeContents() {
+            return 0;
+        }
+
+        public void writeToParcel(Parcel out, int flags) {
+            out.writeString(nodeName);
+            out.writeParcelable(perProviderSubscription, flags);
+        }
+
+        public void readFromParcel(Parcel in) {
+            if (in == null) {
+                //log here
+            } else {
+                nodeName = in.readString();
+                perProviderSubscription = in.readParcelable(PerProviderSubscription.class
+                        .getClassLoader());
+            }
+        }
+
+        public static final Parcelable.Creator<SpFqdn> CREATOR = new Parcelable.Creator<SpFqdn>() {
+            public SpFqdn createFromParcel(Parcel in) {
+                return new SpFqdn(in);
+            }
+
+            public SpFqdn[] newArray(int size) {
+                return new SpFqdn[size];
+            }
+        };
+    }
+
+    /**
+     * PerProviderSubscription
+     **/
+    public static class PerProviderSubscription implements Parcelable {
+        /**
+         * PerProviderSubscription/UpdateIdentifier
+         **/
+        public String UpdateIdentifier;
+        public HashMap<String, CredentialInfo> credentialInfo = new HashMap<String, CredentialInfo>();
+
+        public CredentialInfo createCredentialInfo(String name) {
+            CredentialInfo obj = new CredentialInfo(name);
+            credentialInfo.put(name, obj);
+            return obj;
+        }
+
+        public PerProviderSubscription() {
+        }
+
+        public PerProviderSubscription(Parcel in) {
+            readFromParcel(in);
+        }
+
+        public int describeContents() {
+            return 0;
+        }
+
+        public void writeToParcel(Parcel out, int flags) {
+            out.writeString(UpdateIdentifier);
+            out.writeMap(credentialInfo);
+        }
+
+        public void readFromParcel(Parcel in) {
+            if (in == null) {
+                //log here
+            } else {
+                UpdateIdentifier = in.readString();
+                in.readMap(credentialInfo, CredentialInfo.class.getClassLoader());
+            }
+        }
+
+        public static final Parcelable.Creator<PerProviderSubscription> CREATOR = new Parcelable.Creator<PerProviderSubscription>() {
+            public PerProviderSubscription createFromParcel(Parcel in) {
+                return new PerProviderSubscription(in);
+            }
+
+            public PerProviderSubscription[] newArray(int size) {
+                return new PerProviderSubscription[size];
+            }
+        };
+
+    }
+
+    /**
+     * PerProviderSubscription/<X+>
+     * This interior node contains the Home SP information, subscription policy, management and credential information.
+     **/
+    public static class CredentialInfo implements Parcelable {
+        public String nodeName;
+        public Policy policy = new Policy();
+        public String credentialPriority;
+        public HashMap<String, AAAServerTrustRoot> aAAServerTrustRoot = new HashMap<String, AAAServerTrustRoot>();
+        public SubscriptionUpdate subscriptionUpdate = new SubscriptionUpdate();
+        public HomeSP homeSP = new HomeSP();
+        public SubscriptionParameters subscriptionParameters = new SubscriptionParameters();
+        public Credential credential = new Credential();
+        public Extension extension = new Extension();
+
+        public CredentialInfo(String nn) {
+            nodeName = nn;
+        }
+
+        public AAAServerTrustRoot createAAAServerTrustRoot(String name, String url, String fp) {
+            AAAServerTrustRoot obj = new AAAServerTrustRoot(name, url, fp);
+            aAAServerTrustRoot.put(name, obj);
+            return obj;
+        }
+
+        public CredentialInfo() {
+        }
+
+        public CredentialInfo(Parcel in) {
+            readFromParcel(in);
+        }
+
+        public int describeContents() {
+            return 0;
+        }
+
+        public void writeToParcel(Parcel out, int flags) {
+            out.writeString(nodeName);
+            out.writeParcelable(policy, flags);
+            out.writeString(credentialPriority);
+            out.writeMap(aAAServerTrustRoot);
+            out.writeParcelable(subscriptionUpdate, flags);
+            out.writeParcelable(homeSP, flags);
+            out.writeParcelable(subscriptionParameters, flags);
+            out.writeParcelable(credential, flags);
+            //out.writeParcelable(extension, flags);
+        }
+
+        public void readFromParcel(Parcel in) {
+            if (in == null) {
+                //log here
+            } else {
+                nodeName = in.readString();
+                policy = in.readParcelable(Policy.class.getClassLoader());
+                credentialPriority = in.readString();
+                in.readMap(aAAServerTrustRoot, AAAServerTrustRoot.class.getClassLoader());
+                subscriptionUpdate = in.readParcelable(SubscriptionUpdate.class.getClassLoader());
+                homeSP = in.readParcelable(HomeSP.class.getClassLoader());
+                subscriptionParameters = in.readParcelable(SubscriptionParameters.class
+                        .getClassLoader());
+                credential = in.readParcelable(Credential.class.getClassLoader());
+                //extension = in.readParcelable(Extension.class.getClassLoader());
+            }
+        }
+
+        public static final Parcelable.Creator<CredentialInfo> CREATOR = new Parcelable.Creator<CredentialInfo>() {
+            public CredentialInfo createFromParcel(Parcel in) {
+                return new CredentialInfo(in);
+            }
+
+            public CredentialInfo[] newArray(int size) {
+                return new CredentialInfo[size];
+            }
+        };
+
+    }
+
+    /**
+     * PerProviderSubscription/<X+>/Policy
+     **/
+    public static class Policy implements Parcelable {
+        public HashMap<String, PreferredRoamingPartnerList> preferredRoamingPartnerList = new HashMap<String, PreferredRoamingPartnerList>();
+        public HashMap<String, MinBackhaulThresholdNetwork> minBackhaulThreshold = new HashMap<String, MinBackhaulThresholdNetwork>();
+        public PolicyUpdate policyUpdate = new PolicyUpdate();
+        public HashMap<String, SPExclusionList> sPExclusionList = new HashMap<String, SPExclusionList>();
+        public HashMap<String, RequiredProtoPortTuple> requiredProtoPortTuple = new HashMap<String, RequiredProtoPortTuple>();
+        public String maximumBSSLoadValue;
+
+        public PreferredRoamingPartnerList createPreferredRoamingPartnerList(String name,
+                String fqdn, String priority, String country) {
+            PreferredRoamingPartnerList obj = new PreferredRoamingPartnerList(name, fqdn, priority,
+                    country);
+            preferredRoamingPartnerList.put(name, obj);
+            return obj;
+        }
+
+        public MinBackhaulThresholdNetwork createMinBackhaulThreshold(String name, String type,
+                String dl, String ul) {
+            MinBackhaulThresholdNetwork obj = new MinBackhaulThresholdNetwork(name, type, dl, ul);
+            minBackhaulThreshold.put(name, obj);
+            return obj;
+        }
+
+        public SPExclusionList createSPExclusionList(String name, String ssid) {
+            SPExclusionList obj = new SPExclusionList(name, ssid);
+            sPExclusionList.put(name, obj);
+            return obj;
+        }
+
+        public RequiredProtoPortTuple createRequiredProtoPortTuple(String name, String proto,
+                String port) {
+            RequiredProtoPortTuple obj = new RequiredProtoPortTuple(name, proto, port);
+            requiredProtoPortTuple.put(name, obj);
+            return obj;
+        }
+
+        public Policy() {
+        }
+
+        public Policy(Parcel in) {
+            readFromParcel(in);
+        }
+
+        public int describeContents() {
+            return 0;
+        }
+
+        public void writeToParcel(Parcel out, int flags) {
+            out.writeMap(preferredRoamingPartnerList);
+            out.writeMap(minBackhaulThreshold);
+            out.writeParcelable(policyUpdate, flags);
+            out.writeMap(sPExclusionList);
+            out.writeMap(requiredProtoPortTuple);
+            out.writeString(maximumBSSLoadValue);
+        }
+
+        public void readFromParcel(Parcel in) {
+            if (in == null) {
+                //log here
+            } else {
+                in.readMap(preferredRoamingPartnerList,
+                        PreferredRoamingPartnerList.class.getClassLoader());
+                in.readMap(minBackhaulThreshold, MinBackhaulThresholdNetwork.class.getClassLoader());
+                policyUpdate = in.readParcelable(PolicyUpdate.class.getClassLoader());
+                in.readMap(sPExclusionList, SPExclusionList.class.getClassLoader());
+                in.readMap(requiredProtoPortTuple, RequiredProtoPortTuple.class.getClassLoader());
+                maximumBSSLoadValue = in.readString();
+
+            }
+        }
+
+        public static final Parcelable.Creator<Policy> CREATOR = new Parcelable.Creator<Policy>() {
+            public Policy createFromParcel(Parcel in) {
+                return new Policy(in);
+            }
+
+            public Policy[] newArray(int size) {
+                return new Policy[size];
+            }
+        };
+
+    }
+
+    /**
+     * PerProviderSubscription/<X+>/Policy/PreferredRoamingPartnerList/<X+>
+     **/
+    public static class PreferredRoamingPartnerList implements Parcelable {
+        public String nodeName;
+        public String FQDN_Match; //maximum 255 + ",includeSubdomains", equals 273
+        public String Priority;
+        public String Country; // maximum 600 octets
+
+        public PreferredRoamingPartnerList(String nn, String f, String p, String c) {
+            nodeName = nn;
+            FQDN_Match = f;
+            Priority = p;
+            Country = c;
+        }
+
+        public PreferredRoamingPartnerList() {
+        }
+
+        public PreferredRoamingPartnerList(Parcel in) {
+            readFromParcel(in);
+        }
+
+        public int describeContents() {
+            return 0;
+        }
+
+        public void writeToParcel(Parcel out, int flags) {
+            out.writeString(nodeName);
+            out.writeString(FQDN_Match);
+            out.writeString(Priority);
+            out.writeString(Country);
+        }
+
+        public void readFromParcel(Parcel in) {
+            if (in == null) {
+                //log here
+            } else {
+                nodeName = in.readString();
+                FQDN_Match = in.readString();
+                Priority = in.readString();
+                Country = in.readString();
+            }
+        }
+
+        public static final Parcelable.Creator<PreferredRoamingPartnerList> CREATOR = new Parcelable.Creator<PreferredRoamingPartnerList>() {
+            public PreferredRoamingPartnerList createFromParcel(Parcel in) {
+                return new PreferredRoamingPartnerList(in);
+            }
+
+            public PreferredRoamingPartnerList[] newArray(int size) {
+                return new PreferredRoamingPartnerList[size];
+            }
+        };
+    }
+
+    /**
+     * PerProviderSubscription/<X+>/Policy/MinBackhaulThreshold
+     **/
+    public static class MinBackhaulThresholdNetwork implements Parcelable {
+        public String nodeName;
+        public String NetworkType;
+        public String DLBandwidth;
+        public String ULBandwidth;
+
+        public MinBackhaulThresholdNetwork(String nn, String nt, String d, String u) {
+            nodeName = nn;
+            NetworkType = nt;
+            DLBandwidth = d;
+            ULBandwidth = u;
+        }
+
+        public MinBackhaulThresholdNetwork() {
+        }
+
+        public MinBackhaulThresholdNetwork(Parcel in) {
+            readFromParcel(in);
+        }
+
+        public int describeContents() {
+            return 0;
+        }
+
+        public void writeToParcel(Parcel out, int flags) {
+            out.writeString(nodeName);
+            out.writeString(NetworkType);
+            out.writeString(DLBandwidth);
+            out.writeString(ULBandwidth);
+        }
+
+        public void readFromParcel(Parcel in) {
+            if (in == null) {
+                //log here
+            } else {
+                nodeName = in.readString();
+                NetworkType = in.readString();
+                DLBandwidth = in.readString();
+                ULBandwidth = in.readString();
+            }
+        }
+
+        public static final Parcelable.Creator<MinBackhaulThresholdNetwork> CREATOR = new Parcelable.Creator<MinBackhaulThresholdNetwork>() {
+            public MinBackhaulThresholdNetwork createFromParcel(Parcel in) {
+                return new MinBackhaulThresholdNetwork(in);
+            }
+
+            public MinBackhaulThresholdNetwork[] newArray(int size) {
+                return new MinBackhaulThresholdNetwork[size];
+            }
+        };
+
+    }
+
+    /**
+     * PerProviderSubscription/<X+>/Policy/PolicyUpdate
+     **/
+    public static class PolicyUpdate implements Parcelable {
+        public String UpdateInterval;
+        public String UpdateMethod;
+        public String Restriction;
+        public String URI;
+        public UsernamePassword usernamePassword = new UsernamePassword();
+        public String Other;
+        public TrustRoot trustRoot = new TrustRoot();
+
+        public PolicyUpdate() {
+        }
+
+        public PolicyUpdate(Parcel in) {
+            readFromParcel(in);
+        }
+
+        public int describeContents() {
+            return 0;
+        }
+
+        public void writeToParcel(Parcel out, int flags) {
+            out.writeString(UpdateInterval);
+            out.writeString(UpdateMethod);
+            out.writeString(Restriction);
+            out.writeString(URI);
+            out.writeParcelable(usernamePassword, flags);
+            out.writeString(Other);
+            out.writeParcelable(trustRoot, flags);
+
+        }
+
+        public void readFromParcel(Parcel in) {
+            if (in == null) {
+                //log here
+            } else {
+                UpdateInterval = in.readString();
+                UpdateMethod = in.readString();
+                Restriction = in.readString();
+                URI = in.readString();
+                usernamePassword = in.readParcelable(UsernamePassword.class.getClassLoader());
+                Other = in.readString();
+                trustRoot = in.readParcelable(TrustRoot.class.getClassLoader());
+            }
+        }
+
+        public static final Parcelable.Creator<PolicyUpdate> CREATOR = new Parcelable.Creator<PolicyUpdate>() {
+            public PolicyUpdate createFromParcel(Parcel in) {
+                return new PolicyUpdate(in);
+            }
+
+            public PolicyUpdate[] newArray(int size) {
+                return new PolicyUpdate[size];
+            }
+        };
+    }
+
+    /**
+     * PerProviderSubscription/<X+>/Policy/SPExclusionList
+     **/
+    public static class SPExclusionList implements Parcelable {
+        public String nodeName;
+        public String SSID;
+
+        public SPExclusionList(String nn, String s) {
+            nodeName = nn;
+            SSID = s;
+        }
+
+        public SPExclusionList() {
+        }
+
+        public SPExclusionList(Parcel in) {
+            readFromParcel(in);
+        }
+
+        public int describeContents() {
+            return 0;
+        }
+
+        public void writeToParcel(Parcel out, int flags) {
+            out.writeString(nodeName);
+            out.writeString(SSID);
+        }
+
+        public void readFromParcel(Parcel in) {
+            if (in == null) {
+                //log here
+            } else {
+                nodeName = in.readString();
+                SSID = in.readString();
+            }
+        }
+
+        public static final Parcelable.Creator<SPExclusionList> CREATOR = new Parcelable.Creator<SPExclusionList>() {
+            public SPExclusionList createFromParcel(Parcel in) {
+                return new SPExclusionList(in);
+            }
+
+            public SPExclusionList[] newArray(int size) {
+                return new SPExclusionList[size];
+            }
+        };
+    }
+
+    /**
+     * PerProviderSubscription/<X+>/Policy/RequiredProtoPortTuple
+     **/
+    public static class RequiredProtoPortTuple implements Parcelable {
+        public String nodeName;
+        public String IPProtocol;
+        public String PortNumber;
+
+        public RequiredProtoPortTuple() {
+        }
+
+        public RequiredProtoPortTuple(String nn, String protocol, String port) {
+            nodeName = nn;
+            IPProtocol = protocol;
+            PortNumber = port;
+        }
+
+        public RequiredProtoPortTuple(Parcel in) {
+            readFromParcel(in);
+        }
+
+        public int describeContents() {
+            return 0;
+        }
+
+        public void writeToParcel(Parcel out, int flags) {
+            out.writeString(nodeName);
+            out.writeString(IPProtocol);
+            out.writeString(PortNumber);
+        }
+
+        public void readFromParcel(Parcel in) {
+            if (in == null) {
+                //log here
+            } else {
+                nodeName = in.readString();
+                IPProtocol = in.readString();
+                PortNumber = in.readString();
+            }
+        }
+
+        public static final Parcelable.Creator<RequiredProtoPortTuple> CREATOR = new Parcelable.Creator<RequiredProtoPortTuple>() {
+            public RequiredProtoPortTuple createFromParcel(Parcel in) {
+                return new RequiredProtoPortTuple(in);
+            }
+
+            public RequiredProtoPortTuple[] newArray(int size) {
+                return new RequiredProtoPortTuple[size];
+            }
+        };
+    }
+
+    /**
+     * PerProviderSubscription/<X+>/AAAServerTrustRoot
+     **/
+    public static class AAAServerTrustRoot implements Parcelable {
+        public String nodeName;
+        public String CertURL;
+        public String CertSHA256Fingerprint;
+
+        public AAAServerTrustRoot(String nn, String url, String fp) {
+            nodeName = nn;
+            CertURL = url;
+            CertSHA256Fingerprint = fp;
+        }
+
+        public AAAServerTrustRoot() {
+        }
+
+        public AAAServerTrustRoot(Parcel in) {
+            readFromParcel(in);
+        }
+
+        public int describeContents() {
+            return 0;
+        }
+
+        public void writeToParcel(Parcel out, int flags) {
+            out.writeString(nodeName);
+            out.writeString(CertURL);
+            out.writeString(CertSHA256Fingerprint);
+        }
+
+        public void readFromParcel(Parcel in) {
+            if (in == null) {
+                //log here
+            } else {
+                nodeName = in.readString();
+                CertURL = in.readString();
+                CertSHA256Fingerprint = in.readString();
+            }
+        }
+
+        public static final Parcelable.Creator<AAAServerTrustRoot> CREATOR = new Parcelable.Creator<AAAServerTrustRoot>() {
+            public AAAServerTrustRoot createFromParcel(Parcel in) {
+                return new AAAServerTrustRoot(in);
+            }
+
+            public AAAServerTrustRoot[] newArray(int size) {
+                return new AAAServerTrustRoot[size];
+            }
+        };
+    }
+
+    /**
+     * PerProviderSubscription/<X+>/SubscriptionUpdate
+     **/
+    public static class SubscriptionUpdate implements Parcelable {
+        public String UpdateInterval;
+        public String UpdateMethod;
+        public String Restriction;
+        public String URI;
+        public UsernamePassword usernamePassword = new UsernamePassword();
+        public String Other;
+        public TrustRoot trustRoot = new TrustRoot();
+
+        public SubscriptionUpdate() {
+        }
+
+        public SubscriptionUpdate(Parcel in) {
+            readFromParcel(in);
+        }
+
+        public int describeContents() {
+            return 0;
+        }
+
+        public void writeToParcel(Parcel out, int flags) {
+            out.writeString(UpdateInterval);
+            out.writeString(UpdateMethod);
+            out.writeString(Restriction);
+            out.writeString(URI);
+            out.writeParcelable(usernamePassword, flags);
+            out.writeString(Other);
+            out.writeParcelable(trustRoot, flags);
+        }
+
+        public void readFromParcel(Parcel in) {
+            if (in == null) {
+                //log here
+            } else {
+                UpdateInterval = in.readString();
+                UpdateMethod = in.readString();
+                Restriction = in.readString();
+                URI = in.readString();
+                usernamePassword = in.readParcelable(UsernamePassword.class.getClassLoader());
+                Other = in.readString();
+                trustRoot = in.readParcelable(TrustRoot.class.getClassLoader());
+            }
+        }
+
+        public static final Parcelable.Creator<SubscriptionUpdate> CREATOR = new Parcelable.Creator<SubscriptionUpdate>() {
+            public SubscriptionUpdate createFromParcel(Parcel in) {
+                return new SubscriptionUpdate(in);
+            }
+
+            public SubscriptionUpdate[] newArray(int size) {
+                return new SubscriptionUpdate[size];
+            }
+        };
+
+    }
+
+    /**
+     * PerProviderSubscription/<X+>/Policy/PolicyUpdate/TrustRoot
+     * PerProviderSubscription/<X+>/SubscriptionUpdate/TrustRoot
+     * PerProviderSubscription/<X+>/AAAServerTrustRoot/<X+>
+     **/
+    public static class TrustRoot implements Parcelable {
+        public String CertURL;
+        public String CertSHA256Fingerprint;
+
+        public TrustRoot() {
+        }
+
+        public TrustRoot(Parcel in) {
+            readFromParcel(in);
+        }
+
+        public int describeContents() {
+            return 0;
+        }
+
+        public void writeToParcel(Parcel out, int flags) {
+            out.writeString(CertURL);
+            out.writeString(CertSHA256Fingerprint);
+        }
+
+        public void readFromParcel(Parcel in) {
+            if (in == null) {
+                //log here
+            } else {
+                CertURL = in.readString();
+                CertSHA256Fingerprint = in.readString();
+            }
+        }
+
+        public static final Parcelable.Creator<TrustRoot> CREATOR = new Parcelable.Creator<TrustRoot>() {
+            public TrustRoot createFromParcel(Parcel in) {
+                return new TrustRoot(in);
+            }
+
+            public TrustRoot[] newArray(int size) {
+                return new TrustRoot[size];
+            }
+        };
+    }
+
+    /**
+     * PerProviderSubscription/<X+>/Policy/PolicyUpdate/UsernamePassword
+     * PerProviderSubscription/<X+>/SubscriptionUpdate/UsernamePassword
+     * PerProviderSubscription/<X+>/Credential/UsernamePassword
+     **/
+    public static class UsernamePassword implements Parcelable {
+        public String Username;
+        public String Password;
+        //following are Credential node used only
+        public boolean MachineManaged;
+        public String SoftTokenApp;
+        public String AbleToShare;
+        public EAPMethod eAPMethod = new EAPMethod();
+
+        public UsernamePassword() {
+        }
+
+        public UsernamePassword(Parcel in) {
+            readFromParcel(in);
+        }
+
+        public int describeContents() {
+            return 0;
+        }
+
+        public void writeToParcel(Parcel out, int flags) {
+            out.writeString(Username);
+            out.writeString(Password);
+            out.writeInt(MachineManaged ? 1 : 0);
+            out.writeString(SoftTokenApp);
+            out.writeString(AbleToShare);
+            out.writeParcelable(eAPMethod, flags);
+        }
+
+        public void readFromParcel(Parcel in) {
+            if (in == null) {
+                //log here
+            } else {
+                Username = in.readString();
+                Password = in.readString();
+                MachineManaged = (in.readInt() == 1) ? true : false;
+                SoftTokenApp = in.readString();
+                AbleToShare = in.readString();
+                eAPMethod = in.readParcelable(EAPMethod.class.getClassLoader());
+            }
+        }
+
+        public static final Parcelable.Creator<UsernamePassword> CREATOR = new Parcelable.Creator<UsernamePassword>() {
+            public UsernamePassword createFromParcel(Parcel in) {
+                return new UsernamePassword(in);
+            }
+
+            public UsernamePassword[] newArray(int size) {
+                return new UsernamePassword[size];
+            }
+        };
+
+    }
+
+    /**
+     * PerProviderSubscription/<X+>/Credential/UsernamePassword/EAPMethod
+     **/
+    public static class EAPMethod implements Parcelable {
+        public String EAPType;
+        public String VendorId;
+        public String VendorType;
+        public String InnerEAPType;
+        public String InnerVendorId;
+        public String InnerVendorType;
+        public String InnerMethod;
+
+        public EAPMethod() {
+        }
+
+        public EAPMethod(Parcel in) {
+            readFromParcel(in);
+        }
+
+        public int describeContents() {
+            return 0;
+        }
+
+        public void writeToParcel(Parcel out, int flags) {
+            out.writeString(EAPType);
+            out.writeString(VendorId);
+            out.writeString(VendorType);
+            out.writeString(InnerEAPType);
+            out.writeString(InnerVendorId);
+            out.writeString(InnerVendorType);
+            out.writeString(InnerMethod);
+        }
+
+        public void readFromParcel(Parcel in) {
+            if (in == null) {
+                //log here
+            } else {
+                EAPType = in.readString();
+                VendorId = in.readString();
+                VendorType = in.readString();
+                InnerEAPType = in.readString();
+                InnerVendorId = in.readString();
+                InnerVendorType = in.readString();
+                InnerMethod = in.readString();
+            }
+        }
+
+        public static final Parcelable.Creator<EAPMethod> CREATOR = new Parcelable.Creator<EAPMethod>() {
+            public EAPMethod createFromParcel(Parcel in) {
+                return new EAPMethod(in);
+            }
+
+            public EAPMethod[] newArray(int size) {
+                return new EAPMethod[size];
+            }
+        };
+    }
+
+    /**
+     * PerProviderSubscription/<X+>/HomeSP
+     **/
+    public static class HomeSP implements Parcelable {
+        public HashMap<String, NetworkID> networkID = new HashMap<String, NetworkID>();
+        public String FriendlyName;
+        public String IconURL;
+        public String FQDN;
+        public HashMap<String, HomeOIList> homeOIList = new HashMap<String, HomeOIList>();
+        public HashMap<String, OtherHomePartners> otherHomePartners = new HashMap<String, OtherHomePartners>();
+        public String RoamingConsortiumOI;
+
+        public NetworkID createNetworkID(String name, String ssid, String hessid) {
+            NetworkID obj = new NetworkID(name, ssid, hessid);
+            networkID.put(name, obj);
+            return obj;
+        }
+
+        public HomeOIList createHomeOIList(String name, String homeoi, boolean required) {
+            HomeOIList obj = new HomeOIList(name, homeoi, required);
+            homeOIList.put(name, obj);
+            return obj;
+        }
+
+        public OtherHomePartners createOtherHomePartners(String name, String fqdn) {
+            OtherHomePartners obj = new OtherHomePartners(name, fqdn);
+            otherHomePartners.put(name, obj);
+            return obj;
+        }
+
+        public HomeSP() {
+        }
+
+        public HomeSP(Parcel in) {
+            readFromParcel(in);
+        }
+
+        public int describeContents() {
+            return 0;
+        }
+
+        public void writeToParcel(Parcel out, int flags) {
+            out.writeMap(networkID);
+            out.writeString(FriendlyName);
+            out.writeString(IconURL);
+            out.writeString(FQDN);
+            out.writeMap(homeOIList);
+            out.writeMap(otherHomePartners);
+            out.writeString(RoamingConsortiumOI);
+        }
+
+        public void readFromParcel(Parcel in) {
+            if (in == null) {
+                //log here
+            } else {
+                in.readMap(networkID, NetworkID.class.getClassLoader());
+                FriendlyName = in.readString();
+                IconURL = in.readString();
+                FQDN = in.readString();
+                in.readMap(homeOIList, HomeOIList.class.getClassLoader());
+                in.readMap(otherHomePartners, OtherHomePartners.class.getClassLoader());
+                RoamingConsortiumOI = in.readString();
+            }
+        }
+
+        public static final Parcelable.Creator<HomeSP> CREATOR = new Parcelable.Creator<HomeSP>() {
+            public HomeSP createFromParcel(Parcel in) {
+                return new HomeSP(in);
+            }
+
+            public HomeSP[] newArray(int size) {
+                return new HomeSP[size];
+            }
+        };
+
+    }
+
+    /**
+     * PerProviderSubscription/<X+>/HomeSP/NetworkID
+     **/
+    public static class NetworkID implements Parcelable {
+        public String nodeName;
+        public String SSID;
+        public String HESSID;
+
+        public NetworkID(String nn, String s, String h) {
+            nodeName = nn;
+            SSID = s;
+            HESSID = h;
+        }
+
+        public NetworkID() {
+        }
+
+        public NetworkID(Parcel in) {
+            readFromParcel(in);
+        }
+
+        public int describeContents() {
+            return 0;
+        }
+
+        public void writeToParcel(Parcel out, int flags) {
+            out.writeString(nodeName);
+            out.writeString(SSID);
+            out.writeString(HESSID);
+        }
+
+        public void readFromParcel(Parcel in) {
+            if (in == null) {
+                //log here
+            } else {
+                nodeName = in.readString();
+                SSID = in.readString();
+                HESSID = in.readString();
+            }
+        }
+
+        public static final Parcelable.Creator<NetworkID> CREATOR = new Parcelable.Creator<NetworkID>() {
+            public NetworkID createFromParcel(Parcel in) {
+                return new NetworkID(in);
+            }
+
+            public NetworkID[] newArray(int size) {
+                return new NetworkID[size];
+            }
+        };
+
+    }
+
+    /**
+     * PerProviderSubscription/<X+>/HomeSP/HomeOIList
+     **/
+    public static class HomeOIList implements Parcelable {
+        public String nodeName;
+        public String HomeOI;
+        public boolean HomeOIRequired;
+
+        public HomeOIList(String nn, String h, boolean r) {
+            nodeName = nn;
+            HomeOI = h;
+            HomeOIRequired = r;
+        }
+
+        public HomeOIList() {
+        }
+
+        public HomeOIList(Parcel in) {
+            readFromParcel(in);
+        }
+
+        public int describeContents() {
+            return 0;
+        }
+
+        public void writeToParcel(Parcel out, int flags) {
+            out.writeString(nodeName);
+            out.writeString(HomeOI);
+            out.writeInt(HomeOIRequired ? 1 : 0);
+        }
+
+        public void readFromParcel(Parcel in) {
+            if (in == null) {
+                //log here
+            } else {
+                nodeName = in.readString();
+                HomeOI = in.readString();
+                HomeOIRequired = (in.readInt() == 1) ? true : false;
+            }
+        }
+
+        public static final Parcelable.Creator<HomeOIList> CREATOR = new Parcelable.Creator<HomeOIList>() {
+            public HomeOIList createFromParcel(Parcel in) {
+                return new HomeOIList(in);
+            }
+
+            public HomeOIList[] newArray(int size) {
+                return new HomeOIList[size];
+            }
+        };
+
+    }
+
+    /**
+     * PerProviderSubscription/<X+>/HomeSP/OtherHomePartners
+     **/
+    public static class OtherHomePartners implements Parcelable {
+        public String nodeName;
+        public String FQDN;
+
+        public OtherHomePartners(String nn, String f) {
+            nodeName = nn;
+            FQDN = f;
+        }
+
+        public OtherHomePartners() {
+        }
+
+        public OtherHomePartners(Parcel in) {
+            readFromParcel(in);
+        }
+
+        public int describeContents() {
+            return 0;
+        }
+
+        public void writeToParcel(Parcel out, int flags) {
+            out.writeString(nodeName);
+            out.writeString(FQDN);
+        }
+
+        public void readFromParcel(Parcel in) {
+            if (in == null) {
+                //log here
+            } else {
+                nodeName = in.readString();
+                FQDN = in.readString();
+            }
+        }
+
+        public static final Parcelable.Creator<OtherHomePartners> CREATOR = new Parcelable.Creator<OtherHomePartners>() {
+            public OtherHomePartners createFromParcel(Parcel in) {
+                return new OtherHomePartners(in);
+            }
+
+            public OtherHomePartners[] newArray(int size) {
+                return new OtherHomePartners[size];
+            }
+        };
+
+    }
+
+    /**
+     * PerProviderSubscription/<X+>/SubscriptionParameters
+     **/
+    public static class SubscriptionParameters implements Parcelable {
+        public String CreationDate;
+        public String ExpirationDate;
+        public String TypeOfSubscription;
+        public UsageLimits usageLimits = new UsageLimits();
+
+        public SubscriptionParameters() {
+        }
+
+        public SubscriptionParameters(Parcel in) {
+            readFromParcel(in);
+        }
+
+        public int describeContents() {
+            return 0;
+        }
+
+        public void writeToParcel(Parcel out, int flags) {
+            out.writeString(CreationDate);
+            out.writeString(ExpirationDate);
+            out.writeString(TypeOfSubscription);
+            out.writeParcelable(usageLimits, flags);
+        }
+
+        public void readFromParcel(Parcel in) {
+            if (in == null) {
+                //log here
+            } else {
+                CreationDate = in.readString();
+                ExpirationDate = in.readString();
+                TypeOfSubscription = in.readString();
+                usageLimits = in.readParcelable(UsageLimits.class.getClassLoader());
+            }
+        }
+
+        public static final Parcelable.Creator<SubscriptionParameters> CREATOR = new Parcelable.Creator<SubscriptionParameters>() {
+            public SubscriptionParameters createFromParcel(Parcel in) {
+                return new SubscriptionParameters(in);
+            }
+
+            public SubscriptionParameters[] newArray(int size) {
+                return new SubscriptionParameters[size];
+            }
+        };
+
+    }
+
+    /**
+     * PerProviderSubscription/<X+>/SubscriptionParameters/UsageLimits
+     **/
+    public static class UsageLimits implements Parcelable {
+        public String DataLimit;
+        public String StartDate;
+        public String TimeLimit;
+        public String UsageTimePeriod;
+
+        public UsageLimits() {
+        }
+
+        public UsageLimits(Parcel in) {
+            readFromParcel(in);
+        }
+
+        public int describeContents() {
+            return 0;
+        }
+
+        public void writeToParcel(Parcel out, int flags) {
+            out.writeString(DataLimit);
+            out.writeString(StartDate);
+            out.writeString(TimeLimit);
+            out.writeString(UsageTimePeriod);
+        }
+
+        public void readFromParcel(Parcel in) {
+            if (in == null) {
+                //log here
+            } else {
+                DataLimit = in.readString();
+                StartDate = in.readString();
+                TimeLimit = in.readString();
+                UsageTimePeriod = in.readString();
+            }
+        }
+
+        public static final Parcelable.Creator<UsageLimits> CREATOR = new Parcelable.Creator<UsageLimits>() {
+            public UsageLimits createFromParcel(Parcel in) {
+                return new UsageLimits(in);
+            }
+
+            public UsageLimits[] newArray(int size) {
+                return new UsageLimits[size];
+            }
+        };
+    }
+
+    /**
+     * PerProviderSubscription/<X+>/Credential
+     **/
+    public static class Credential implements Parcelable {
+        public String CreationDate;
+        public String ExpirationDate;
+        public UsernamePassword usernamePassword = new UsernamePassword();
+        public DigitalCertificate digitalCertificate = new DigitalCertificate();
+        public String Realm;
+        public boolean CheckAAAServerCertStatus;
+        public SIM sim = new SIM();
+
+        public Credential() {
+        }
+
+        public Credential(Parcel in) {
+            readFromParcel(in);
+        }
+
+        public int describeContents() {
+            return 0;
+        }
+
+        public void writeToParcel(Parcel out, int flags) {
+            out.writeString(CreationDate);
+            out.writeString(ExpirationDate);
+            out.writeParcelable(usernamePassword, flags);
+            out.writeParcelable(digitalCertificate, flags);
+            out.writeString(Realm);
+            out.writeInt(CheckAAAServerCertStatus ? 1 : 0);
+            out.writeParcelable(sim, flags);
+        }
+
+        public void readFromParcel(Parcel in) {
+            if (in == null) {
+                //log here
+            } else {
+                CreationDate = in.readString();
+                ExpirationDate = in.readString();
+                usernamePassword = in.readParcelable(UsernamePassword.class.getClassLoader());
+                digitalCertificate = in.readParcelable(DigitalCertificate.class.getClassLoader());
+                Realm = in.readString();
+                CheckAAAServerCertStatus = (in.readInt() == 1) ? true : false;
+                sim = in.readParcelable(SIM.class.getClassLoader());
+            }
+        }
+
+        public static final Parcelable.Creator<Credential> CREATOR = new Parcelable.Creator<Credential>() {
+            public Credential createFromParcel(Parcel in) {
+                return new Credential(in);
+            }
+
+            public Credential[] newArray(int size) {
+                return new Credential[size];
+            }
+        };
+    }
+
+    /**
+     * PerProviderSubscription/<X+>/Credential/DigitalCertificate
+     **/
+    public static class DigitalCertificate implements Parcelable {
+        public String CertificateType;
+        public String CertSHA256Fingerprint;
+
+        public DigitalCertificate() {
+        }
+
+        public DigitalCertificate(Parcel in) {
+            readFromParcel(in);
+        }
+
+        public int describeContents() {
+            return 0;
+        }
+
+        public void writeToParcel(Parcel out, int flags) {
+            out.writeString(CertificateType);
+            out.writeString(CertSHA256Fingerprint);
+        }
+
+        public void readFromParcel(Parcel in) {
+            if (in == null) {
+                //log here
+            } else {
+                CertificateType = in.readString();
+                CertSHA256Fingerprint = in.readString();
+            }
+        }
+
+        public static final Parcelable.Creator<DigitalCertificate> CREATOR = new Parcelable.Creator<DigitalCertificate>() {
+            public DigitalCertificate createFromParcel(Parcel in) {
+                return new DigitalCertificate(in);
+            }
+
+            public DigitalCertificate[] newArray(int size) {
+                return new DigitalCertificate[size];
+            }
+        };
+
+    }
+
+    /**
+     * PerProviderSubscription/<X+>/Credential/SIM
+     **/
+    public static class SIM implements Parcelable {
+        public String IMSI;
+        public String EAPType;
+
+        public SIM() {
+        }
+
+        public SIM(Parcel in) {
+            readFromParcel(in);
+        }
+
+        public int describeContents() {
+            return 0;
+        }
+
+        public void writeToParcel(Parcel out, int flags) {
+            out.writeString(IMSI);
+            out.writeString(EAPType);
+        }
+
+        public void readFromParcel(Parcel in) {
+            if (in == null) {
+                //log here
+            } else {
+                IMSI = in.readString();
+                EAPType = in.readString();
+            }
+        }
+
+        public static final Parcelable.Creator<SIM> CREATOR = new Parcelable.Creator<SIM>() {
+            public SIM createFromParcel(Parcel in) {
+                return new SIM(in);
+            }
+
+            public SIM[] newArray(int size) {
+                return new SIM[size];
+            }
+        };
+
+    }
+
+    /**
+     * PerProviderSubscription/<X+>/Extension
+     **/
+    public static class Extension {
+        public String empty;
+    }
+
+    public WifiPasspointDmTree() {
+    }
+
+    public WifiPasspointDmTree(Parcel in) {
+        readFromParcel(in);
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeMap(spFqdn);
+    }
+
+    public void readFromParcel(Parcel in) {
+        if (in == null) {
+            //log here
+        } else {
+            in.readMap(spFqdn, SpFqdn.class.getClassLoader());
+        }
+    }
+
+    public static final Parcelable.Creator<WifiPasspointDmTree> CREATOR = new Parcelable.Creator<WifiPasspointDmTree>() {
+        public WifiPasspointDmTree createFromParcel(Parcel in) {
+            return new WifiPasspointDmTree(in);
+        }
+
+        public WifiPasspointDmTree[] newArray(int size) {
+            return new WifiPasspointDmTree[size];
+        }
+    };
+
+}
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.aidl b/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.aidl
new file mode 100644
index 0000000..27f23bc
--- /dev/null
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.passpoint;
+
+parcelable WifiPasspointInfo;
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java
new file mode 100644
index 0000000..33db3f5
--- /dev/null
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointInfo.java
@@ -0,0 +1,559 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.passpoint;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** @hide */
+public class WifiPasspointInfo implements Parcelable {
+
+    /** TODO doc */
+    public static final int ANQP_CAPABILITY = 1 << 0;
+
+    /** TODO doc */
+    public static final int VENUE_NAME = 1 << 1;
+
+    /** TODO doc */
+    public static final int NETWORK_AUTH_TYPE = 1 << 2;
+
+    /** TODO doc */
+    public static final int ROAMING_CONSORTIUM = 1 << 3;
+
+    /** TODO doc */
+    public static final int IP_ADDR_TYPE_AVAILABILITY = 1 << 4;
+
+    /** TODO doc */
+    public static final int NAI_REALM = 1 << 5;
+
+    /** TODO doc */
+    public static final int CELLULAR_NETWORK = 1 << 6;
+
+    /** TODO doc */
+    public static final int DOMAIN_NAME = 1 << 7;
+
+    /** TODO doc */
+    public static final int HOTSPOT_CAPABILITY = 1 << 8;
+
+    /** TODO doc */
+    public static final int OPERATOR_FRIENDLY_NAME = 1 << 9;
+
+    /** TODO doc */
+    public static final int WAN_METRICS = 1 << 10;
+
+    /** TODO doc */
+    public static final int CONNECTION_CAPABILITY = 1 << 11;
+
+    /** TODO doc */
+    public static final int OSU_PROVIDER = 1 << 12;
+
+    /** TODO doc */
+    public static final int PRESET_CRED_MATCH =
+            ANQP_CAPABILITY |
+                    HOTSPOT_CAPABILITY |
+                    NAI_REALM |
+                    CELLULAR_NETWORK |
+                    DOMAIN_NAME;
+
+    /** TODO doc */
+    public static final int PRESET_ALL =
+            ANQP_CAPABILITY |
+                    VENUE_NAME |
+                    NETWORK_AUTH_TYPE |
+                    ROAMING_CONSORTIUM |
+                    IP_ADDR_TYPE_AVAILABILITY |
+                    NAI_REALM |
+                    CELLULAR_NETWORK |
+                    DOMAIN_NAME |
+                    HOTSPOT_CAPABILITY |
+                    OPERATOR_FRIENDLY_NAME |
+                    WAN_METRICS |
+                    CONNECTION_CAPABILITY |
+                    OSU_PROVIDER;
+
+
+    public static class WanMetrics {
+        public static final int STATUS_RESERVED = 0;
+        public static final int STATUS_UP = 1;
+        public static final int STATUS_DOWN = 2;
+        public static final int STATUS_TEST = 3;
+
+        public int wanInfo;
+        public long downlinkSpeed;
+        public long uplinkSpeed;
+        public int downlinkLoad;
+        public int uplinkLoad;
+        public int lmd;
+
+        public int getLinkStatus() {
+            return wanInfo & 0x3;
+        }
+
+        public boolean getSymmetricLink() {
+            return (wanInfo & (1 << 2)) != 0;
+        }
+
+        public boolean getAtCapacity() {
+            return (wanInfo & (1 << 3)) != 0;
+        }
+
+        @Override
+        public String toString() {
+            return wanInfo + "," + downlinkSpeed + "," + uplinkSpeed + "," +
+                    downlinkLoad + "," + uplinkLoad + "," + lmd;
+        }
+    }
+
+    public static class IpProtoPort {
+        public static final int STATUS_CLOSED = 0;
+        public static final int STATUS_OPEN = 1;
+        public static final int STATUS_UNKNOWN = 2;
+
+        public int proto;
+        public int port;
+        public int status;
+
+        @Override
+        public String toString() {
+            return proto + "," + port + "," + status;
+        }
+    }
+
+    public static class NetworkAuthType {
+        public static final int TYPE_TERMS_AND_CONDITION = 0;
+        public static final int TYPE_ONLINE_ENROLLMENT = 1;
+        public static final int TYPE_HTTP_REDIRECTION = 2;
+        public static final int TYPE_DNS_REDIRECTION = 3;
+
+        public int type;
+        public String redirectUrl;
+
+        @Override
+        public String toString() {
+            return type + "," + redirectUrl;
+        }
+    }
+
+    public static class IpAddressType {
+        public static final int IPV6_NOT_AVAILABLE = 0;
+        public static final int IPV6_AVAILABLE = 1;
+        public static final int IPV6_UNKNOWN = 2;
+
+        public static final int IPV4_NOT_AVAILABLE = 0;
+        public static final int IPV4_PUBLIC = 1;
+        public static final int IPV4_PORT_RESTRICTED = 2;
+        public static final int IPV4_SINGLE_NAT = 3;
+        public static final int IPV4_DOUBLE_NAT = 4;
+        public static final int IPV4_PORT_RESTRICTED_SINGLE_NAT = 5;
+        public static final int IPV4_PORT_RESTRICTED_DOUBLE_NAT = 6;
+        public static final int IPV4_PORT_UNKNOWN = 7;
+
+        private static final int NULL_VALUE = -1;
+
+        public int availability;
+
+        public int getIpv6Availability() {
+            return availability & 0x3;
+        }
+
+        public int getIpv4Availability() {
+            return (availability & 0xFF) >> 2;
+        }
+
+        @Override
+        public String toString() {
+            return getIpv6Availability() + "," + getIpv4Availability();
+        }
+    }
+
+    public static class NaiRealm {
+        public static final int ENCODING_RFC4282 = 0;
+        public static final int ENCODING_UTF8 = 1;
+
+        public int encoding;
+        public String realm;
+
+        @Override
+        public String toString() {
+            return encoding + "," + realm;
+        }
+    }
+
+    public static class CellularNetwork {
+        public String mcc;
+        public String mnc;
+
+        @Override
+        public String toString() {
+            return mcc + "," + mnc;
+        }
+    }
+
+    /** BSSID */
+    public String bssid;
+
+    /** venue name */
+    public String venueName;
+
+    /** list of network authentication types */
+    public List<NetworkAuthType> networkAuthTypeList;
+
+    /** list of roaming consortium OIs */
+    public List<String> roamingConsortiumList;
+
+    /** IP address availability */
+    public IpAddressType ipAddrTypeAvailability;
+
+    /** list of NAI realm */
+    public List<NaiRealm> naiRealmList;
+
+    /** list of 3GPP cellular network */
+    public List<CellularNetwork> cellularNetworkList;
+
+    /** list of fully qualified domain name (FQDN) */
+    public List<String> domainNameList;
+
+    /** HS 2.0 operator friendly name */
+    public String operatorFriendlyName;
+
+    /** HS 2.0 wan metrics */
+    public WanMetrics wanMetrics;
+
+    /** list of HS 2.0 IP proto port */
+    public List<IpProtoPort> connectionCapabilityList;
+
+    /** list of HS 2.0 OSU providers */
+    public List<WifiPasspointOsuProvider> osuProviderList;
+
+    /**
+     * Convert mask to ANQP subtypes, for supplicant command use.
+     *
+     * @param mask The ANQP subtypes mask.
+     * @return String of ANQP subtypes, good for supplicant command use
+     * @hide
+     */
+    public static String toAnqpSubtypes(int mask) {
+        StringBuilder sb = new StringBuilder();
+        if ((mask & ANQP_CAPABILITY) != 0)
+            sb.append("257,");
+        if ((mask & VENUE_NAME) != 0)
+            sb.append("258,");
+        if ((mask & NETWORK_AUTH_TYPE) != 0)
+            sb.append("260,");
+        if ((mask & ROAMING_CONSORTIUM) != 0)
+            sb.append("261,");
+        if ((mask & IP_ADDR_TYPE_AVAILABILITY) != 0)
+            sb.append("262,");
+        if ((mask & NAI_REALM) != 0)
+            sb.append("263,");
+        if ((mask & CELLULAR_NETWORK) != 0)
+            sb.append("264,");
+        if ((mask & DOMAIN_NAME) != 0)
+            sb.append("268,");
+        if ((mask & HOTSPOT_CAPABILITY) != 0)
+            sb.append("hs20:2,");
+        if ((mask & OPERATOR_FRIENDLY_NAME) != 0)
+            sb.append("hs20:3,");
+        if ((mask & WAN_METRICS) != 0)
+            sb.append("hs20:4,");
+        if ((mask & CONNECTION_CAPABILITY) != 0)
+            sb.append("hs20:5,");
+        if ((mask & OSU_PROVIDER) != 0)
+            sb.append("hs20:8,");
+        if (sb.length() > 0)
+            sb.deleteCharAt(sb.length() - 1);
+        return sb.toString();
+    }
+
+    @Override
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+
+        sb.append("BSSID: ").append("(").append(bssid).append(")");
+
+        if (venueName != null)
+            sb.append(" venueName: ").append("(")
+              .append(venueName.replace("\n", "\\n")).append(")");
+
+        if (networkAuthTypeList != null) {
+            sb.append(" networkAuthType: ");
+            for (NetworkAuthType auth : networkAuthTypeList)
+                sb.append("(").append(auth.toString()).append(")");
+        }
+
+        if (roamingConsortiumList != null) {
+            sb.append(" roamingConsortium: ");
+            for (String oi : roamingConsortiumList)
+                sb.append("(").append(oi).append(")");
+        }
+
+        if (ipAddrTypeAvailability != null) {
+            sb.append(" ipAddrTypeAvaibility: ").append("(")
+              .append(ipAddrTypeAvailability.toString()).append(")");
+        }
+
+        if (naiRealmList != null) {
+            sb.append(" naiRealm: ");
+            for (NaiRealm realm : naiRealmList)
+                sb.append("(").append(realm.toString()).append(")");
+        }
+
+        if (cellularNetworkList != null) {
+            sb.append(" cellularNetwork: ");
+            for (CellularNetwork plmn : cellularNetworkList)
+                sb.append("(").append(plmn.toString()).append(")");
+        }
+
+        if (domainNameList != null) {
+            sb.append(" domainName: ");
+            for (String fqdn : domainNameList)
+                sb.append("(").append(fqdn).append(")");
+        }
+
+        if (operatorFriendlyName != null)
+            sb.append(" operatorFriendlyName: ").append("(")
+              .append(operatorFriendlyName).append(")");
+
+        if (wanMetrics != null)
+            sb.append(" wanMetrics: ").append("(")
+              .append(wanMetrics.toString()).append(")");
+
+        if (connectionCapabilityList != null) {
+            sb.append(" connectionCapability: ");
+            for (IpProtoPort ip : connectionCapabilityList)
+                sb.append("(").append(ip.toString()).append(")");
+        }
+
+        if (osuProviderList != null) {
+            sb.append(" osuProviderList: ");
+            for (WifiPasspointOsuProvider osu : osuProviderList)
+                sb.append("(").append(osu.toString()).append(")");
+        }
+
+        return sb.toString();
+    }
+
+    /** Implement the Parcelable interface {@hide} */
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeString(bssid);
+        out.writeString(venueName);
+
+        if (networkAuthTypeList == null) {
+            out.writeInt(0);
+        } else {
+            out.writeInt(networkAuthTypeList.size());
+            for (NetworkAuthType auth : networkAuthTypeList) {
+                out.writeInt(auth.type);
+                out.writeString(auth.redirectUrl);
+            }
+        }
+
+        if (roamingConsortiumList == null) {
+            out.writeInt(0);
+        } else {
+            out.writeInt(roamingConsortiumList.size());
+            for (String oi : roamingConsortiumList)
+                out.writeString(oi);
+        }
+
+        if (ipAddrTypeAvailability == null) {
+            out.writeInt(IpAddressType.NULL_VALUE);
+        } else {
+            out.writeInt(ipAddrTypeAvailability.availability);
+        }
+
+        if (naiRealmList == null) {
+            out.writeInt(0);
+        } else {
+            out.writeInt(naiRealmList.size());
+            for (NaiRealm realm : naiRealmList) {
+                out.writeInt(realm.encoding);
+                out.writeString(realm.realm);
+            }
+        }
+
+        if (cellularNetworkList == null) {
+            out.writeInt(0);
+        } else {
+            out.writeInt(cellularNetworkList.size());
+            for (CellularNetwork plmn : cellularNetworkList) {
+                out.writeString(plmn.mcc);
+                out.writeString(plmn.mnc);
+            }
+        }
+
+
+        if (domainNameList == null) {
+            out.writeInt(0);
+        } else {
+            out.writeInt(domainNameList.size());
+            for (String fqdn : domainNameList)
+                out.writeString(fqdn);
+        }
+
+        out.writeString(operatorFriendlyName);
+
+        if (wanMetrics == null) {
+            out.writeInt(0);
+        } else {
+            out.writeInt(1);
+            out.writeInt(wanMetrics.wanInfo);
+            out.writeLong(wanMetrics.downlinkSpeed);
+            out.writeLong(wanMetrics.uplinkSpeed);
+            out.writeInt(wanMetrics.downlinkLoad);
+            out.writeInt(wanMetrics.uplinkLoad);
+            out.writeInt(wanMetrics.lmd);
+        }
+
+        if (connectionCapabilityList == null) {
+            out.writeInt(0);
+        } else {
+            out.writeInt(connectionCapabilityList.size());
+            for (IpProtoPort ip : connectionCapabilityList) {
+                out.writeInt(ip.proto);
+                out.writeInt(ip.port);
+                out.writeInt(ip.status);
+            }
+        }
+
+        if (osuProviderList == null) {
+            out.writeInt(0);
+        } else {
+            out.writeInt(osuProviderList.size());
+            for (WifiPasspointOsuProvider osu : osuProviderList)
+                osu.writeToParcel(out, flags);
+        }
+    }
+
+    /** Implement the Parcelable interface {@hide} */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Implement the Parcelable interface {@hide} */
+    public static final Parcelable.Creator<WifiPasspointInfo> CREATOR =
+            new Parcelable.Creator<WifiPasspointInfo>() {
+                @Override
+                public WifiPasspointInfo createFromParcel(Parcel in) {
+                    WifiPasspointInfo p = new WifiPasspointInfo();
+                    int n;
+
+                    p.bssid = in.readString();
+                    p.venueName = in.readString();
+
+                    n = in.readInt();
+                    if (n > 0) {
+                        p.networkAuthTypeList = new ArrayList<NetworkAuthType>();
+                        for (int i = 0; i < n; i++) {
+                            NetworkAuthType auth = new NetworkAuthType();
+                            auth.type = in.readInt();
+                            auth.redirectUrl = in.readString();
+                            p.networkAuthTypeList.add(auth);
+                        }
+                    }
+
+                    n = in.readInt();
+                    if (n > 0) {
+                        p.roamingConsortiumList = new ArrayList<String>();
+                        for (int i = 0; i < n; i++)
+                            p.roamingConsortiumList.add(in.readString());
+                    }
+
+                    n = in.readInt();
+                    if (n != IpAddressType.NULL_VALUE) {
+                        p.ipAddrTypeAvailability = new IpAddressType();
+                        p.ipAddrTypeAvailability.availability = n;
+                    }
+
+                    n = in.readInt();
+                    if (n > 0) {
+                        p.naiRealmList = new ArrayList<NaiRealm>();
+                        for (int i = 0; i < n; i++) {
+                            NaiRealm realm = new NaiRealm();
+                            realm.encoding = in.readInt();
+                            realm.realm = in.readString();
+                            p.naiRealmList.add(realm);
+                        }
+                    }
+
+                    n = in.readInt();
+                    if (n > 0) {
+                        p.cellularNetworkList = new ArrayList<CellularNetwork>();
+                        for (int i = 0; i < n; i++) {
+                            CellularNetwork plmn = new CellularNetwork();
+                            plmn.mcc = in.readString();
+                            plmn.mnc = in.readString();
+                            p.cellularNetworkList.add(plmn);
+                        }
+                    }
+
+                    n = in.readInt();
+                    if (n > 0) {
+                        p.domainNameList = new ArrayList<String>();
+                        for (int i = 0; i < n; i++)
+                            p.domainNameList.add(in.readString());
+                    }
+
+                    p.operatorFriendlyName = in.readString();
+
+                    n = in.readInt();
+                    if (n > 0) {
+                        p.wanMetrics = new WanMetrics();
+                        p.wanMetrics.wanInfo = in.readInt();
+                        p.wanMetrics.downlinkSpeed = in.readLong();
+                        p.wanMetrics.uplinkSpeed = in.readLong();
+                        p.wanMetrics.downlinkLoad = in.readInt();
+                        p.wanMetrics.uplinkLoad = in.readInt();
+                        p.wanMetrics.lmd = in.readInt();
+                    }
+
+                    n = in.readInt();
+                    if (n > 0) {
+                        p.connectionCapabilityList = new ArrayList<IpProtoPort>();
+                        for (int i = 0; i < n; i++) {
+                            IpProtoPort ip = new IpProtoPort();
+                            ip.proto = in.readInt();
+                            ip.port = in.readInt();
+                            ip.status = in.readInt();
+                            p.connectionCapabilityList.add(ip);
+                        }
+                    }
+
+                    n = in.readInt();
+                    if (n > 0) {
+                        p.osuProviderList = new ArrayList<WifiPasspointOsuProvider>();
+                        for (int i = 0; i < n; i++) {
+                            WifiPasspointOsuProvider osu = WifiPasspointOsuProvider.CREATOR
+                                    .createFromParcel(in);
+                            p.osuProviderList.add(osu);
+                        }
+                    }
+
+                    return p;
+                }
+
+                @Override
+                public WifiPasspointInfo[] newArray(int size) {
+                    return new WifiPasspointInfo[size];
+                }
+            };
+}
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
new file mode 100644
index 0000000..0245a3d
--- /dev/null
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointManager.java
@@ -0,0 +1,567 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.passpoint;
+
+import android.content.Context;
+import android.net.wifi.ScanResult;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.util.AsyncChannel;
+import com.android.internal.util.Protocol;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Provides APIs for managing Wifi Passpoint credentials.
+ * @hide
+ */
+public class WifiPasspointManager {
+
+    private static final String TAG = "PasspointManager";
+
+    private static final boolean DBG = true;
+
+    /* Passpoint states values */
+
+    /** Passpoint is in an unknown state. This should only occur in boot time */
+    public static final int PASSPOINT_STATE_UNKNOWN = 0;
+
+    /** Passpoint is disabled. This occurs when wifi is disabled */
+    public static final int PASSPOINT_STATE_DISABLED = 1;
+
+    /** Passpoint is enabled and in discovery state */
+    public static final int PASSPOINT_STATE_DISCOVERY = 2;
+
+    /** Passpoint is enabled and in access state */
+    public static final int PASSPOINT_STATE_ACCESS = 3;
+
+    /** Passpoint is enabled and in provisioning state */
+    public static final int PASSPOINT_STATE_PROVISION = 4;
+
+    /* Passpoint callback error codes */
+
+    /** Indicates that the operation failed due to an internal error */
+    public static final int REASON_ERROR = 0;
+
+    /** Indicates that the operation failed because wifi is disabled */
+    public static final int REASON_WIFI_DISABLED = 1;
+
+    /** Indicates that the operation failed because the framework is busy */
+    public static final int REASON_BUSY = 2;
+
+    /** Indicates that the operation failed because parameter is invalid */
+    public static final int REASON_INVALID_PARAMETER = 3;
+
+    /** Indicates that the operation failed because the server is not trusted */
+    public static final int REASON_NOT_TRUSTED = 4;
+
+    /**
+     * protocol supported for Passpoint
+     */
+    public static final String PROTOCOL_DM = "OMA-DM-ClientInitiated";
+
+    /**
+     * protocol supported for Passpoint
+     */
+    public static final String PROTOCOL_SOAP = "SPP-ClientInitiated";
+
+    /* Passpoint broadcasts */
+
+    /**
+     * Broadcast intent action indicating that the state of Passpoint
+     * connectivity has changed
+     */
+    public static final String PASSPOINT_STATE_CHANGED_ACTION =
+            "android.net.wifi.passpoint.STATE_CHANGE";
+
+    /**
+     * Broadcast intent action indicating that the saved Passpoint credential
+     * list has changed
+     */
+    public static final String PASSPOINT_CRED_CHANGED_ACTION =
+            "android.net.wifi.passpoint.CRED_CHANGE";
+
+    /**
+     * Broadcast intent action indicating that Passpoint online sign up is
+     * avaiable.
+     */
+    public static final String PASSPOINT_OSU_AVAILABLE_ACTION =
+            "android.net.wifi.passpoint.OSU_AVAILABLE";
+
+    /**
+     * Broadcast intent action indicating that user remediation is required
+     */
+    public static final String PASSPOINT_USER_REM_REQ_ACTION =
+            "android.net.wifi.passpoint.USER_REM_REQ";
+
+    /**
+     * Interface for callback invocation when framework channel is lost
+     */
+    public interface ChannelListener {
+        /**
+         * The channel to the framework has been disconnected. Application could
+         * try re-initializing using {@link #initialize}
+         */
+        public void onChannelDisconnected();
+    }
+
+    /**
+     * Interface for callback invocation on an application action
+     */
+    public interface ActionListener {
+        /** The operation succeeded */
+        public void onSuccess();
+
+        /**
+         * The operation failed
+         *
+         * @param reason The reason for failure could be one of
+         *            {@link #WIFI_DISABLED}, {@link #ERROR} or {@link #BUSY}
+         */
+        public void onFailure(int reason);
+    }
+
+    /**
+     * Interface for callback invocation when doing OSU or user remediation
+     */
+    public interface OsuRemListener {
+        /** The operation succeeded */
+        public void onSuccess();
+
+        /**
+         * The operation failed
+         *
+         * @param reason The reason for failure could be one of
+         *            {@link #WIFI_DISABLED}, {@link #ERROR} or {@link #BUSY}
+         */
+        public void onFailure(int reason);
+
+        /**
+         * Browser launch is requried for user interaction. When this callback
+         * is called, app should launch browser / webview to the given URI.
+         *
+         * @param uri URI for browser launch
+         */
+        public void onBrowserLaunch(String uri);
+
+        /**
+         * When this is called, app should dismiss the previously lanched browser.
+         */
+        public void onBrowserDismiss();
+    }
+
+    /**
+     * A channel that connects the application to the wifi passpoint framework.
+     * Most passpoint operations require a Channel as an argument.
+     * An instance of Channel is obtained by doing a call on {@link #initialize}
+     */
+    public static class Channel {
+        private final static int INVALID_LISTENER_KEY = 0;
+
+        private ChannelListener mChannelListener;
+
+        private HashMap<Integer, Object> mListenerMap = new HashMap<Integer, Object>();
+        private HashMap<Integer, Integer> mListenerMapCount = new HashMap<Integer, Integer>();
+        private Object mListenerMapLock = new Object();
+        private int mListenerKey = 0;
+
+        private List<ScanResult> mAnqpRequest = new LinkedList<ScanResult>();
+        private Object mAnqpRequestLock = new Object();
+
+        private AsyncChannel mAsyncChannel;
+        private PasspointHandler mHandler;
+        Context mContext;
+
+        Channel(Context context, Looper looper, ChannelListener l) {
+            mAsyncChannel = new AsyncChannel();
+            mHandler = new PasspointHandler(looper);
+            mChannelListener = l;
+            mContext = context;
+        }
+
+        private int putListener(Object listener) {
+            return putListener(listener, 1);
+        }
+
+        private int putListener(Object listener, int count) {
+            if (listener == null || count <= 0)
+                return INVALID_LISTENER_KEY;
+            int key;
+            synchronized (mListenerMapLock) {
+                do {
+                    key = mListenerKey++;
+                } while (key == INVALID_LISTENER_KEY);
+                mListenerMap.put(key, listener);
+                mListenerMapCount.put(key, count);
+            }
+            return key;
+        }
+
+        private Object peekListener(int key) {
+            Log.d(TAG, "peekListener() key=" + key);
+            if (key == INVALID_LISTENER_KEY)
+                return null;
+            synchronized (mListenerMapLock) {
+                return mListenerMap.get(key);
+            }
+        }
+
+
+        private Object getListener(int key, boolean forceRemove) {
+            Log.d(TAG, "getListener() key=" + key + " force=" + forceRemove);
+            if (key == INVALID_LISTENER_KEY)
+                return null;
+            synchronized (mListenerMapLock) {
+                if (!forceRemove) {
+                    int count = mListenerMapCount.get(key);
+                    Log.d(TAG, "count=" + count);
+                    mListenerMapCount.put(key, --count);
+                    if (count > 0)
+                        return null;
+                }
+                Log.d(TAG, "remove key");
+                mListenerMapCount.remove(key);
+                return mListenerMap.remove(key);
+            }
+        }
+
+        private void anqpRequestStart(ScanResult sr) {
+            Log.d(TAG, "anqpRequestStart sr.bssid=" + sr.BSSID);
+            synchronized (mAnqpRequestLock) {
+                mAnqpRequest.add(sr);
+            }
+        }
+
+        private void anqpRequestFinish(WifiPasspointInfo result) {
+            Log.d(TAG, "anqpRequestFinish pi.bssid=" + result.bssid);
+            synchronized (mAnqpRequestLock) {
+                for (ScanResult sr : mAnqpRequest)
+                    if (sr.BSSID.equals(result.bssid)) {
+                        Log.d(TAG, "find hit " + result.bssid);
+                        /* sr.passpoint = result; */
+                        mAnqpRequest.remove(sr);
+                        Log.d(TAG, "mAnqpRequest.len=" + mAnqpRequest.size());
+                        break;
+                    }
+            }
+        }
+
+        private void anqpRequestFinish(ScanResult sr) {
+            Log.d(TAG, "anqpRequestFinish sr.bssid=" + sr.BSSID);
+            synchronized (mAnqpRequestLock) {
+                for (ScanResult sr1 : mAnqpRequest)
+                    if (sr1.BSSID.equals(sr.BSSID)) {
+                        mAnqpRequest.remove(sr1);
+                        break;
+                    }
+            }
+        }
+
+        class PasspointHandler extends Handler {
+            PasspointHandler(Looper looper) {
+                super(looper);
+            }
+
+            @Override
+            public void handleMessage(Message message) {
+                Object listener = null;
+
+                switch (message.what) {
+                    case AsyncChannel.CMD_CHANNEL_DISCONNECTED:
+                        if (mChannelListener != null) {
+                            mChannelListener.onChannelDisconnected();
+                            mChannelListener = null;
+                        }
+                        break;
+
+                    case REQUEST_ANQP_INFO_SUCCEEDED:
+                        WifiPasspointInfo result = (WifiPasspointInfo) message.obj;
+                        anqpRequestFinish(result);
+                        listener = getListener(message.arg2, false);
+                        if (listener != null) {
+                            ((ActionListener) listener).onSuccess();
+                        }
+                        break;
+
+                    case REQUEST_ANQP_INFO_FAILED:
+                        anqpRequestFinish((ScanResult) message.obj);
+                        listener = getListener(message.arg2, false);
+                        if (listener == null)
+                            getListener(message.arg2, true);
+                        if (listener != null) {
+                            ((ActionListener) listener).onFailure(message.arg1);
+                        }
+                        break;
+
+                    case START_OSU_SUCCEEDED:
+                        listener = getListener(message.arg2, true);
+                        if (listener != null) {
+                            ((OsuRemListener) listener).onSuccess();
+                        }
+                        break;
+
+                    case START_OSU_FAILED:
+                        listener = getListener(message.arg2, true);
+                        if (listener != null) {
+                            ((OsuRemListener) listener).onFailure(message.arg1);
+                        }
+                        break;
+
+                    case START_OSU_BROWSER:
+                        listener = peekListener(message.arg2);
+                        if (listener != null) {
+                            ParcelableString str = (ParcelableString) message.obj;
+                            if (str == null || str.string == null)
+                                ((OsuRemListener) listener).onBrowserDismiss();
+                            else
+                                ((OsuRemListener) listener).onBrowserLaunch(str.string);
+                        }
+                        break;
+
+                    default:
+                        Log.d(TAG, "Ignored " + message);
+                        break;
+                }
+            }
+        }
+
+    }
+
+    public static class ParcelableString implements Parcelable {
+        public String string;
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(Parcel out, int flags) {
+            out.writeString(string);
+        }
+
+        public static final Parcelable.Creator<ParcelableString> CREATOR =
+                new Parcelable.Creator<ParcelableString>() {
+                    @Override
+                    public ParcelableString createFromParcel(Parcel in) {
+                        ParcelableString ret = new ParcelableString();
+                        ret.string = in.readString();
+                        return ret;
+                    }
+                    @Override
+                    public ParcelableString[] newArray(int size) {
+                        return new ParcelableString[size];
+                    }
+        };
+    }
+
+    private static final int BASE = Protocol.BASE_WIFI_PASSPOINT_MANAGER;
+
+    public static final int REQUEST_ANQP_INFO                   = BASE + 1;
+    public static final int REQUEST_ANQP_INFO_FAILED            = BASE + 2;
+    public static final int REQUEST_ANQP_INFO_SUCCEEDED         = BASE + 3;
+    public static final int REQUEST_OSU_ICON                    = BASE + 4;
+    public static final int REQUEST_OSU_ICON_FAILED             = BASE + 5;
+    public static final int REQUEST_OSU_ICON_SUCCEEDED          = BASE + 6;
+    public static final int START_OSU                           = BASE + 7;
+    public static final int START_OSU_BROWSER                   = BASE + 8;
+    public static final int START_OSU_FAILED                    = BASE + 9;
+    public static final int START_OSU_SUCCEEDED                 = BASE + 10;
+
+    private Context mContext;
+    IWifiPasspointManager mService;
+
+    /**
+     * TODO: doc
+     * @param context
+     * @param service
+     */
+    public WifiPasspointManager(Context context, IWifiPasspointManager service) {
+        mContext = context;
+        mService = service;
+    }
+
+    /**
+     * Registers the application with the framework. This function must be the
+     * first to be called before any async passpoint operations are performed.
+     *
+     * @param srcContext is the context of the source
+     * @param srcLooper is the Looper on which the callbacks are receivied
+     * @param listener for callback at loss of framework communication. Can be
+     *            null.
+     * @return Channel instance that is necessary for performing any further
+     *         passpoint operations
+     *
+     */
+    public Channel initialize(Context srcContext, Looper srcLooper, ChannelListener listener) {
+        Messenger messenger = getMessenger();
+        if (messenger == null)
+            return null;
+
+        Channel c = new Channel(srcContext, srcLooper, listener);
+        if (c.mAsyncChannel.connectSync(srcContext, c.mHandler, messenger)
+                == AsyncChannel.STATUS_SUCCESSFUL) {
+            return c;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * STOPSHIP: temp solution, should use supplicant manager instead, check
+     * with b/13931972
+     */
+    public Messenger getMessenger() {
+        try {
+            return mService.getMessenger();
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    public int getPasspointState() {
+        try {
+            return mService.getPasspointState();
+        } catch (RemoteException e) {
+            return PASSPOINT_STATE_UNKNOWN;
+        }
+    }
+
+    public void requestAnqpInfo(Channel c, List<ScanResult> requested, int mask,
+            ActionListener listener) {
+        Log.d(TAG, "requestAnqpInfo start");
+        Log.d(TAG, "requested.size=" + requested.size());
+        checkChannel(c);
+        List<ScanResult> list = new ArrayList<ScanResult>();
+        for (ScanResult sr : requested)
+            if (sr.capabilities.contains("[HS20]")) {
+                list.add(sr);
+                c.anqpRequestStart(sr);
+                Log.d(TAG, "adding " + sr.BSSID);
+            }
+        int count = list.size();
+        Log.d(TAG, "after filter, count=" + count);
+        if (count == 0) {
+            if (DBG)
+                Log.d(TAG, "ANQP info request contains no HS20 APs, skipped");
+            listener.onSuccess();
+            return;
+        }
+        int key = c.putListener(listener, count);
+        for (ScanResult sr : list)
+            c.mAsyncChannel.sendMessage(REQUEST_ANQP_INFO, mask, key, sr);
+        Log.d(TAG, "requestAnqpInfo end");
+    }
+
+    public void requestOsuIcons(Channel c, List<WifiPasspointOsuProvider> requested,
+            int resolution, ActionListener listener) {
+    }
+
+    public List<WifiPasspointPolicy> requestCredentialMatch(List<ScanResult> requested) {
+        try {
+            return mService.requestCredentialMatch(requested);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Get a list of saved Passpoint credentials. Only those credentials owned
+     * by the caller will be returned.
+     *
+     * @return The list of credentials
+     */
+    public List<WifiPasspointCredential> getCredentials() {
+        try {
+            return mService.getCredentials();
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Add a new Passpoint credential.
+     *
+     * @param cred The credential to be added
+     * @return {@code true} if the operation succeeds, {@code false} otherwise
+     */
+    public boolean addCredential(WifiPasspointCredential cred) {
+        try {
+            return mService.addCredential(cred);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Update an existing Passpoint credential. Only system or the owner of this
+     * credential has the permission to do this.
+     *
+     * @param cred The credential to be updated
+     * @return {@code true} if the operation succeeds, {@code false} otherwise
+     */
+    public boolean updateCredential(WifiPasspointCredential cred) {
+        try {
+            return mService.updateCredential(cred);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Remove an existing Passpoint credential. Only system or the owner of this
+     * credential has the permission to do this.
+     *
+     * @param cred The credential to be removed
+     * @return {@code true} if the operation succeeds, {@code false} otherwise
+     */
+    public boolean removeCredential(WifiPasspointCredential cred) {
+        try {
+            return mService.removeCredential(cred);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    public void startOsu(Channel c, WifiPasspointOsuProvider osu, OsuRemListener listener) {
+        Log.d(TAG, "startOsu start");
+        checkChannel(c);
+        int key = c.putListener(listener);
+        c.mAsyncChannel.sendMessage(START_OSU, 0, key, osu);
+        Log.d(TAG, "startOsu end");
+    }
+
+    public void startRemediation(Channel c, OsuRemListener listener) {
+    }
+
+    public void connect(WifiPasspointPolicy policy) {
+    }
+
+    private static void checkChannel(Channel c) {
+        if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
+    }
+}
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.aidl b/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.aidl
new file mode 100644
index 0000000..088136f
--- /dev/null
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.passpoint;
+
+parcelable WifiPasspointOsuProvider;
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.java
new file mode 100644
index 0000000..b54b70c
--- /dev/null
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointOsuProvider.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.passpoint;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/** @hide */
+public class WifiPasspointOsuProvider implements Parcelable {
+
+    /** TODO: doc
+     * @hide
+     */
+    public static final int OSU_METHOD_UNKNOWN = -1;
+
+    /** TODO: doc
+     * @hide
+     */
+    public static final int OSU_METHOD_OMADM = 0;
+
+    /** TODO: doc
+     * @hide
+     */
+    public static final int OSU_METHOD_SOAP = 1;
+
+    /** TODO: doc */
+    public String ssid;
+
+    /** TODO: doc */
+    public String friendlyName;
+
+    /** TODO: doc
+     * @hide
+     */
+    public String serverUri;
+
+    /** TODO: doc
+     * @hide
+     */
+    public int osuMethod = OSU_METHOD_UNKNOWN;
+
+    /** TODO: doc */
+    public int iconWidth;
+
+    /** TODO: doc */
+    public int iconHeight;
+
+    /** TODO: doc */
+    public String iconType;
+
+    /** TODO: doc */
+    public String iconFileName;
+
+    /** TODO: doc */
+    public Object icon; // TODO: should change to image format
+
+    /** TODO: doc */
+    public String osuNai;
+
+    /** TODO: doc */
+    public String osuService;
+
+    /** default constructor @hide */
+    public WifiPasspointOsuProvider() {
+        // TODO
+    }
+
+    /** copy constructor @hide */
+    public WifiPasspointOsuProvider(WifiPasspointOsuProvider source) {
+        // TODO
+    }
+
+    @Override
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+        sb.append("SSID: ").append("<").append(ssid).append(">");
+        if (friendlyName != null)
+            sb.append(" friendlyName: ").append("<").append(friendlyName).append(">");
+        if (serverUri != null)
+            sb.append(" serverUri: ").append("<").append(serverUri).append(">");
+        sb.append(" osuMethod: ").append("<").append(osuMethod).append(">");
+        if (iconFileName != null) {
+            sb.append(" icon: <").append(iconWidth).append("x")
+                    .append(iconHeight).append(" ")
+                    .append(iconType).append(" ")
+                    .append(iconFileName).append(">");
+        }
+        if (osuNai != null)
+            sb.append(" osuNai: ").append("<").append(osuNai).append(">");
+        if (osuService != null)
+            sb.append(" osuService: ").append("<").append(osuService).append(">");
+        return sb.toString();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeString(ssid);
+        out.writeString(friendlyName);
+        out.writeString(serverUri);
+        out.writeInt(osuMethod);
+        out.writeInt(iconWidth);
+        out.writeInt(iconHeight);
+        out.writeString(iconType);
+        out.writeString(iconFileName);
+        out.writeString(osuNai);
+        out.writeString(osuService);
+        // TODO: icon image?
+    }
+
+    public static final Parcelable.Creator<WifiPasspointOsuProvider> CREATOR =
+            new Parcelable.Creator<WifiPasspointOsuProvider>() {
+                @Override
+                public WifiPasspointOsuProvider createFromParcel(Parcel in) {
+                    WifiPasspointOsuProvider osu = new WifiPasspointOsuProvider();
+                    osu.ssid = in.readString();
+                    osu.friendlyName = in.readString();
+                    osu.serverUri = in.readString();
+                    osu.osuMethod = in.readInt();
+                    osu.iconWidth = in.readInt();
+                    osu.iconHeight = in.readInt();
+                    osu.iconType = in.readString();
+                    osu.iconFileName = in.readString();
+                    osu.osuNai = in.readString();
+                    osu.osuService = in.readString();
+                    return osu;
+                }
+
+                @Override
+                public WifiPasspointOsuProvider[] newArray(int size) {
+                    return new WifiPasspointOsuProvider[size];
+                }
+            };
+}
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.aidl b/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.aidl
new file mode 100644
index 0000000..1d61da0
--- /dev/null
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.passpoint;
+
+parcelable WifiPasspointPolicy;
diff --git a/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.java b/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.java
new file mode 100644
index 0000000..f84ac88
--- /dev/null
+++ b/wifi/java/android/net/wifi/passpoint/WifiPasspointPolicy.java
@@ -0,0 +1,386 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.passpoint;
+
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.ScanResult;
+import android.os.Parcelable;
+import android.os.Parcel;
+import android.security.Credentials;
+import android.util.Log;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.List;
+
+
+/** @hide */
+public class WifiPasspointPolicy implements Parcelable {
+
+    private final static String TAG = "PasspointPolicy";
+
+    /** @hide */
+    public static final int HOME_SP = 0;
+
+    /** @hide */
+    public static final int ROAMING_PARTNER = 1;
+
+    /** @hide */
+    public static final int UNRESTRICTED = 2;
+
+    private String mName;
+    private int mCredentialPriority;
+    private int mRoamingPriority;
+    private String mBssid;
+    private String mSsid;
+    private WifiPasspointCredential mCredential;
+    private int mRestriction;// Permitted values are "HomeSP", "RoamingPartner", or "Unrestricted"
+    private boolean mIsHomeSp;
+
+    private final String INT_PRIVATE_KEY = "private_key";
+    private final String INT_PHASE2 = "phase2";
+    private final String INT_PASSWORD = "password";
+    private final String INT_IDENTITY = "identity";
+    private final String INT_EAP = "eap";
+    private final String INT_CLIENT_CERT = "client_cert";
+    private final String INT_CA_CERT = "ca_cert";
+    private final String INT_ANONYMOUS_IDENTITY = "anonymous_identity";
+    private final String INT_SIM_SLOT = "sim_slot";
+    private final String INT_ENTERPRISEFIELD_NAME ="android.net.wifi.WifiConfiguration$EnterpriseField";
+    private final String ISO8601DATEFORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
+    private final String ENTERPRISE_PHASE2_MSCHAPV2 = "auth=MSCHAPV2";
+    private final String ENTERPRISE_PHASE2_MSCHAP = "auth=MSCHAP";
+
+    /** @hide */
+    public WifiPasspointPolicy(String name, String ssid,
+            String bssid, WifiPasspointCredential pc,
+            int restriction, boolean ishomesp) {
+        mName = name;
+        if (pc != null) {
+            mCredentialPriority = pc.getPriority();
+        }
+        //PerProviderSubscription/<X+>/Policy/PreferredRoamingPartnerList/<X+>/Priority
+        mRoamingPriority = 128; //default priority value of 128
+        mSsid = ssid;
+        mCredential = pc;
+        mBssid = bssid;
+        mRestriction = restriction;
+        mIsHomeSp = ishomesp;
+    }
+
+    public String getSsid() {
+        return mSsid;
+    }
+
+    /** @hide */
+    public void setBssid(String bssid) {
+        mBssid = bssid;
+    }
+
+    public String getBssid() {
+        return mBssid;
+    }
+
+    /** @hide */
+    public void setRestriction(int r) {
+        mRestriction = r;
+    }
+
+    /** @hide */
+    public int getRestriction() {
+        return mRestriction;
+    }
+
+    /** @hide */
+    public void setHomeSp(boolean b) {
+        mIsHomeSp = b;
+    }
+
+    /** @hide */
+    public boolean isHomeSp() {
+        return mIsHomeSp;
+    }
+
+    /** @hide */
+    public void setCredential(WifiPasspointCredential newCredential) {
+        mCredential = newCredential;
+    }
+
+    public WifiPasspointCredential getCredential() {
+        // TODO: return a copy
+        return mCredential;
+    }
+
+    /** @hide */
+    public void setCredentialPriority(int priority) {
+        mCredentialPriority = priority;
+    }
+
+    /** @hide */
+    public void setRoamingPriority(int priority) {
+        mRoamingPriority = priority;
+    }
+
+    public int getCredentialPriority() {
+        return mCredentialPriority;
+    }
+
+    public int getRoamingPriority() {
+        return mRoamingPriority;
+    }
+
+    public WifiConfiguration createWifiConfiguration() {
+        WifiConfiguration wfg = new WifiConfiguration();
+        if (mBssid != null) {
+            Log.d(TAG, "create bssid:" + mBssid);
+            wfg.BSSID = mBssid;
+        }
+
+        if (mSsid != null) {
+            Log.d(TAG, "create ssid:" + mSsid);
+            wfg.SSID = mSsid;
+        }
+        //TODO: 1. add pmf configuration
+        //      2. add ocsp configuration
+        //      3. add eap-sim configuration
+        /*Key management*/
+        wfg.status = WifiConfiguration.Status.ENABLED;
+        wfg.allowedKeyManagement.clear();
+        wfg.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_EAP);
+        wfg.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.IEEE8021X);
+
+        /*Group Ciphers*/
+        wfg.allowedGroupCiphers.clear();
+        wfg.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
+        wfg.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
+
+        /*Protocols*/
+        wfg.allowedProtocols.clear();
+        wfg.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
+        wfg.allowedProtocols.set(WifiConfiguration.Protocol.WPA);
+
+        Class[] enterpriseFieldArray  = WifiConfiguration.class.getClasses();
+        Class<?> enterpriseFieldClass = null;
+
+
+        for(Class<?> myClass : enterpriseFieldArray) {
+            if(myClass.getName().equals(INT_ENTERPRISEFIELD_NAME)) {
+                enterpriseFieldClass = myClass;
+                break;
+            }
+        }
+        Log.d(TAG, "class chosen " + enterpriseFieldClass.getName() );
+
+
+        Field anonymousId = null, caCert = null, clientCert = null,
+              eap = null, identity = null, password = null,
+              phase2 = null, privateKey =  null;
+
+        Field[] fields = WifiConfiguration.class.getFields();
+
+
+        for (Field tempField : fields) {
+            if (tempField.getName().trim().equals(INT_ANONYMOUS_IDENTITY)) {
+                anonymousId = tempField;
+                Log.d(TAG, "field " + anonymousId.getName() );
+            } else if (tempField.getName().trim().equals(INT_CA_CERT)) {
+                caCert = tempField;
+            } else if (tempField.getName().trim().equals(INT_CLIENT_CERT)) {
+                clientCert = tempField;
+                Log.d(TAG, "field " + clientCert.getName() );
+            } else if (tempField.getName().trim().equals(INT_EAP)) {
+                eap = tempField;
+                Log.d(TAG, "field " + eap.getName() );
+            } else if (tempField.getName().trim().equals(INT_IDENTITY)) {
+                identity = tempField;
+                Log.d(TAG, "field " + identity.getName() );
+            } else if (tempField.getName().trim().equals(INT_PASSWORD)) {
+                password = tempField;
+                Log.d(TAG, "field " + password.getName() );
+            } else if (tempField.getName().trim().equals(INT_PHASE2)) {
+                phase2 = tempField;
+                Log.d(TAG, "field " + phase2.getName() );
+
+            } else if (tempField.getName().trim().equals(INT_PRIVATE_KEY)) {
+                privateKey = tempField;
+            }
+        }
+
+
+        Method setValue = null;
+
+        for(Method m: enterpriseFieldClass.getMethods()) {
+            if(m.getName().trim().equals("setValue")) {
+                Log.d(TAG, "method " + m.getName() );
+                setValue = m;
+                break;
+            }
+        }
+
+        try {
+            // EAP
+            String eapmethod = mCredential.getType();
+            Log.d(TAG, "eapmethod:" + eapmethod);
+            setValue.invoke(eap.get(wfg), eapmethod);
+
+            // Username, password, EAP Phase 2
+            if ("TTLS".equals(eapmethod)) {
+                setValue.invoke(phase2.get(wfg), ENTERPRISE_PHASE2_MSCHAPV2);
+                setValue.invoke(identity.get(wfg), mCredential.getUserName());
+                setValue.invoke(password.get(wfg), mCredential.getPassword());
+                setValue.invoke(anonymousId.get(wfg), "anonymous@" + mCredential.getRealm());
+            }
+
+            // EAP CA Certificate
+            String cacertificate = null;
+            String rootCA = mCredential.getCaRootCertPath();
+            if (rootCA == null){
+                cacertificate = null;
+            } else {
+                cacertificate = "keystore://" + Credentials.WIFI + "HS20" + Credentials.CA_CERTIFICATE + rootCA;
+            }
+            Log.d(TAG, "cacertificate:" + cacertificate);
+            setValue.invoke(caCert.get(wfg), cacertificate);
+
+            //User certificate
+            if ("TLS".equals(eapmethod)) {
+                String usercertificate = null;
+                String privatekey = null;
+                String clientCertPath = mCredential.getClientCertPath();
+                if (clientCertPath != null){
+                    privatekey = "keystore://" + Credentials.WIFI + "HS20" + Credentials.USER_PRIVATE_KEY + clientCertPath;
+                    usercertificate = "keystore://" + Credentials.WIFI + "HS20" + Credentials.USER_CERTIFICATE + clientCertPath;
+                }
+                Log.d(TAG, "privatekey:" + privatekey);
+                Log.d(TAG, "usercertificate:" + usercertificate);
+                if (privatekey != null && usercertificate != null) {
+                    setValue.invoke(privateKey.get(wfg), privatekey);
+                    setValue.invoke(clientCert.get(wfg), usercertificate);
+                }
+            }
+        } catch (Exception e) {
+            Log.d(TAG, "createWifiConfiguration err:" + e);
+        }
+
+        return wfg;
+    }
+
+    /** {@inheritDoc} @hide */
+    public int compareTo(WifiPasspointPolicy another) {
+        Log.d(TAG, "this:" + this);
+        Log.d(TAG, "another:" + another);
+
+        if (another == null) {
+            return -1;
+        } else if (this.mIsHomeSp == true && another.isHomeSp() == false) {
+            //home sp priority is higher then roaming
+            Log.d(TAG, "compare HomeSP  first, this is HomeSP, another isn't");
+            return -1;
+        } else if ((this.mIsHomeSp == true && another.isHomeSp() == true)) {
+            Log.d(TAG, "both HomeSP");
+            //if both home sp, compare credential priority
+            if (this.mCredentialPriority < another.getCredentialPriority()) {
+                Log.d(TAG, "this priority is higher");
+                return -1;
+            } else if (this.mCredentialPriority == another.getCredentialPriority()) {
+                Log.d(TAG, "both priorities equal");
+                //if priority still the same, compare name(ssid)
+                if (this.mName.compareTo(another.mName) != 0) {
+                    Log.d(TAG, "compare mName return:" + this.mName.compareTo(another.mName));
+                    return this.mName.compareTo(another.mName);
+                }
+                /**
+                 *if name still the same, compare credential
+                 *the device may has two more credentials(TLS,SIM..etc)
+                 *it can associate to one AP(same ssid). so we should compare by credential
+                 */
+                if (this.mCredential != null && another.mCredential != null) {
+                    if (this.mCredential.compareTo(another.mCredential) != 0) {
+                        Log.d(TAG,
+                                "compare mCredential return:" + this.mName.compareTo(another.mName));
+                        return this.mCredential.compareTo(another.mCredential);
+                    }
+                }
+            } else {
+                return 1;
+            }
+        } else if ((this.mIsHomeSp == false && another.isHomeSp() == false)) {
+            Log.d(TAG, "both RoamingSp");
+            //if both roaming sp, compare roaming priority(preferredRoamingPartnerList/<X+>/priority)
+            if (this.mRoamingPriority < another.getRoamingPriority()) {
+                Log.d(TAG, "this priority is higher");
+                return -1;
+            } else if (this.mRoamingPriority == another.getRoamingPriority()) {//priority equals, compare name
+                Log.d(TAG, "both priorities equal");
+                //if priority still the same, compare name(ssid)
+                if (this.mName.compareTo(another.mName) != 0) {
+                    Log.d(TAG, "compare mName return:" + this.mName.compareTo(another.mName));
+                    return this.mName.compareTo(another.mName);
+                }
+                //if name still the same, compare credential
+                if (this.mCredential != null && another.mCredential != null) {
+                    if (this.mCredential.compareTo(another.mCredential) != 0) {
+                        Log.d(TAG,
+                                "compare mCredential return:"
+                                        + this.mCredential.compareTo(another.mCredential));
+                        return this.mCredential.compareTo(another.mCredential);
+                    }
+                }
+            } else {
+                return 1;
+            }
+        }
+
+        Log.d(TAG, "both policies equal");
+        return 0;
+    }
+
+    @Override
+    /** @hide */
+    public String toString() {
+        return "PasspointPolicy: name=" + mName + " CredentialPriority=" + mCredentialPriority +
+                " mRoamingPriority" + mRoamingPriority +
+                " ssid=" + mSsid + " restriction=" + mRestriction +
+                " ishomesp=" + mIsHomeSp + " Credential=" + mCredential;
+    }
+
+    /** Implement the Parcelable interface {@hide} */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** Implement the Parcelable interface {@hide} */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        // TODO
+    }
+
+    /** Implement the Parcelable interface {@hide} */
+    public static final Creator<WifiPasspointPolicy> CREATOR =
+            new Creator<WifiPasspointPolicy>() {
+                @Override
+                public WifiPasspointPolicy createFromParcel(Parcel in) {
+                    return null;
+                }
+
+                @Override
+                public WifiPasspointPolicy[] newArray(int size) {
+                    return new WifiPasspointPolicy[size];
+                }
+            };
+}