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—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>
+ * >= 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 >= 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>
+ * >= 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];
+ }
+ };
+}