Merge "Minor formatting cleanup to remove overlong line"
diff --git a/Android.mk b/Android.mk
index a798a31..a07fd01 100644
--- a/Android.mk
+++ b/Android.mk
@@ -128,9 +128,11 @@
core/java/android/bluetooth/IBluetoothInputHost.aidl \
core/java/android/bluetooth/IBluetoothHidDeviceCallback.aidl \
core/java/android/bluetooth/IBluetoothGatt.aidl \
- core/java/android/bluetooth/IBluetoothGattCallback.aidl \
- core/java/android/bluetooth/IBluetoothGattServerCallback.aidl \
+ core/java/android/bluetooth/IBluetoothGattCallbackExt.aidl \
+ core/java/android/bluetooth/IBluetoothGattServerCallbackExt.aidl \
core/java/android/bluetooth/le/IAdvertiserCallback.aidl \
+ core/java/android/bluetooth/le/IAdvertisingSetCallback.aidl \
+ core/java/android/bluetooth/le/IPeriodicAdvertisingCallback.aidl \
core/java/android/bluetooth/le/IScannerCallback.aidl \
core/java/android/content/IClipboard.aidl \
core/java/android/content/IContentService.aidl \
@@ -435,6 +437,7 @@
telephony/java/com/android/ims/internal/IImsEcbm.aidl \
telephony/java/com/android/ims/internal/IImsEcbmListener.aidl \
telephony/java/com/android/ims/internal/IImsExternalCallStateListener.aidl \
+ telephony/java/com/android/ims/internal/IImsFeatureStatusCallback.aidl \
telephony/java/com/android/ims/internal/IImsMultiEndpoint.aidl \
telephony/java/com/android/ims/internal/IImsService.aidl \
telephony/java/com/android/ims/internal/IImsServiceController.aidl \
@@ -513,6 +516,7 @@
LOCAL_MODULE := framework
+LOCAL_DX_FLAGS := --core-library --multi-dex
LOCAL_JACK_FLAGS := --multi-dex native
LOCAL_RMTYPEDEFS := true
@@ -1348,6 +1352,8 @@
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := ext
+LOCAL_DX_FLAGS := --core-library
+
ifneq ($(INCREMENTAL_BUILDS),)
LOCAL_PROGUARD_ENABLED := disabled
LOCAL_JACK_ENABLED := incremental
diff --git a/api/current.txt b/api/current.txt
index 1b61e88..ad4dc0b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -6695,6 +6695,7 @@
method public java.util.Set<android.bluetooth.BluetoothDevice> getBondedDevices();
method public static synchronized android.bluetooth.BluetoothAdapter getDefaultAdapter();
method public java.lang.String getName();
+ method public android.bluetooth.le.PeriodicAdvertisingManager getPeriodicAdvertisingManager();
method public int getProfileConnectionState(int);
method public boolean getProfileProxy(android.content.Context, android.bluetooth.BluetoothProfile.ServiceListener, int);
method public android.bluetooth.BluetoothDevice getRemoteDevice(java.lang.String);
@@ -6703,6 +6704,10 @@
method public int getState();
method public boolean isDiscovering();
method public boolean isEnabled();
+ method public boolean isLe2MPhySupported();
+ method public boolean isLeCodedPhySupported();
+ method public boolean isLeExtendedAdvertisingSupported();
+ method public boolean isLePeriodicAdvertisingSupported();
method public boolean isMultipleAdvertisementSupported();
method public boolean isOffloadedFilteringSupported();
method public boolean isOffloadedScanBatchingSupported();
@@ -7071,6 +7076,9 @@
public final class BluetoothDevice implements android.os.Parcelable {
method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback);
method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback, int);
+ method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallbackExt);
+ method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallbackExt, int);
+ method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallbackExt, int, int);
method public boolean createBond();
method public android.bluetooth.BluetoothSocket createInsecureRfcommSocketToServiceRecord(java.util.UUID) throws java.io.IOException;
method public android.bluetooth.BluetoothSocket createRfcommSocketToServiceRecord(java.util.UUID) throws java.io.IOException;
@@ -7114,6 +7122,13 @@
field public static final java.lang.String EXTRA_UUID = "android.bluetooth.device.extra.UUID";
field public static final int PAIRING_VARIANT_PASSKEY_CONFIRMATION = 2; // 0x2
field public static final int PAIRING_VARIANT_PIN = 0; // 0x0
+ field public static final int PHY_LE_1M = 1; // 0x1
+ field public static final int PHY_LE_2M = 2; // 0x2
+ field public static final int PHY_LE_ANY = 7; // 0x7
+ field public static final int PHY_LE_CODED = 4; // 0x4
+ field public static final int PHY_OPTION_NO_PREFERRED = 0; // 0x0
+ field public static final int PHY_OPTION_S2 = 1; // 0x1
+ field public static final int PHY_OPTION_S8 = 2; // 0x2
field public static final int TRANSPORT_AUTO = 0; // 0x0
field public static final int TRANSPORT_BREDR = 1; // 0x1
field public static final int TRANSPORT_LE = 2; // 0x2
@@ -7136,10 +7151,12 @@
method public java.util.List<android.bluetooth.BluetoothGattService> getServices();
method public boolean readCharacteristic(android.bluetooth.BluetoothGattCharacteristic);
method public boolean readDescriptor(android.bluetooth.BluetoothGattDescriptor);
+ method public void readPhy();
method public boolean readRemoteRssi();
method public boolean requestConnectionPriority(int);
method public boolean requestMtu(int);
method public boolean setCharacteristicNotification(android.bluetooth.BluetoothGattCharacteristic, boolean);
+ method public void setPreferredPhy(int, int, int);
method public boolean writeCharacteristic(android.bluetooth.BluetoothGattCharacteristic);
method public boolean writeDescriptor(android.bluetooth.BluetoothGattDescriptor);
field public static final int CONNECTION_PRIORITY_BALANCED = 0; // 0x0
@@ -7157,8 +7174,12 @@
field public static final int GATT_WRITE_NOT_PERMITTED = 3; // 0x3
}
- public abstract class BluetoothGattCallback {
+ public abstract deprecated class BluetoothGattCallback extends android.bluetooth.BluetoothGattCallbackExt {
ctor public BluetoothGattCallback();
+ }
+
+ public abstract class BluetoothGattCallbackExt {
+ ctor public BluetoothGattCallbackExt();
method public void onCharacteristicChanged(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic);
method public void onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int);
method public void onCharacteristicWrite(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int);
@@ -7166,6 +7187,8 @@
method public void onDescriptorRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattDescriptor, int);
method public void onDescriptorWrite(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattDescriptor, int);
method public void onMtuChanged(android.bluetooth.BluetoothGatt, int, int);
+ method public void onPhyRead(android.bluetooth.BluetoothGatt, int, int, int);
+ method public void onPhyUpdate(android.bluetooth.BluetoothGatt, int, int, int);
method public void onReadRemoteRssi(android.bluetooth.BluetoothGatt, int, int);
method public void onReliableWriteCompleted(android.bluetooth.BluetoothGatt, int);
method public void onServicesDiscovered(android.bluetooth.BluetoothGatt, int);
@@ -7259,12 +7282,18 @@
method public android.bluetooth.BluetoothGattService getService(java.util.UUID);
method public java.util.List<android.bluetooth.BluetoothGattService> getServices();
method public boolean notifyCharacteristicChanged(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothGattCharacteristic, boolean);
+ method public void readPhy(android.bluetooth.BluetoothDevice);
method public boolean removeService(android.bluetooth.BluetoothGattService);
method public boolean sendResponse(android.bluetooth.BluetoothDevice, int, int, int, byte[]);
+ method public void setPreferredPhy(android.bluetooth.BluetoothDevice, int, int, int);
}
- public abstract class BluetoothGattServerCallback {
+ public abstract deprecated class BluetoothGattServerCallback extends android.bluetooth.BluetoothGattServerCallbackExt {
ctor public BluetoothGattServerCallback();
+ }
+
+ public abstract class BluetoothGattServerCallbackExt {
+ ctor public BluetoothGattServerCallbackExt();
method public void onCharacteristicReadRequest(android.bluetooth.BluetoothDevice, int, int, android.bluetooth.BluetoothGattCharacteristic);
method public void onCharacteristicWriteRequest(android.bluetooth.BluetoothDevice, int, android.bluetooth.BluetoothGattCharacteristic, boolean, boolean, int, byte[]);
method public void onConnectionStateChange(android.bluetooth.BluetoothDevice, int, int);
@@ -7273,6 +7302,8 @@
method public void onExecuteWrite(android.bluetooth.BluetoothDevice, int, boolean);
method public void onMtuChanged(android.bluetooth.BluetoothDevice, int);
method public void onNotificationSent(android.bluetooth.BluetoothDevice, int);
+ method public void onPhyRead(android.bluetooth.BluetoothDevice, int, int, int);
+ method public void onPhyUpdate(android.bluetooth.BluetoothDevice, int, int, int);
method public void onServiceAdded(int, android.bluetooth.BluetoothGattService);
}
@@ -7473,10 +7504,85 @@
method public android.bluetooth.le.AdvertiseSettings.Builder setTxPowerLevel(int);
}
+ public final class AdvertisingSet {
+ method public void enableAdvertising(boolean);
+ method public void periodicAdvertisingEnable(boolean);
+ method public void setAdvertisingData(android.bluetooth.le.AdvertiseData);
+ method public void setAdvertisingParameters(android.bluetooth.le.AdvertisingSetParameters);
+ method public void setPeriodicAdvertisingData(android.bluetooth.le.AdvertiseData);
+ method public void setPeriodicAdvertisingParameters(android.bluetooth.le.PeriodicAdvertisingParameters);
+ method public void setScanResponseData(android.bluetooth.le.AdvertiseData);
+ }
+
+ public abstract class AdvertisingSetCallback {
+ ctor public AdvertisingSetCallback();
+ method public void onAdvertisingDataSet(android.bluetooth.le.AdvertisingSet, int);
+ method public void onAdvertisingEnabled(android.bluetooth.le.AdvertisingSet, boolean, int);
+ method public void onAdvertisingParametersUpdated(android.bluetooth.le.AdvertisingSet, int);
+ method public void onAdvertisingSetStarted(android.bluetooth.le.AdvertisingSet, int);
+ method public void onAdvertisingSetStopped(android.bluetooth.le.AdvertisingSet);
+ method public void onPeriodicAdvertisingDataSet(android.bluetooth.le.AdvertisingSet, int);
+ method public void onPeriodicAdvertisingEnable(android.bluetooth.le.AdvertisingSet, boolean, int);
+ method public void onPeriodicAdvertisingParametersUpdated(android.bluetooth.le.AdvertisingSet, int);
+ method public void onScanResponseDataSet(android.bluetooth.le.AdvertisingSet, int);
+ field public static final int ADVERTISE_FAILED_ALREADY_STARTED = 3; // 0x3
+ field public static final int ADVERTISE_FAILED_DATA_TOO_LARGE = 1; // 0x1
+ field public static final int ADVERTISE_FAILED_FEATURE_UNSUPPORTED = 5; // 0x5
+ field public static final int ADVERTISE_FAILED_INTERNAL_ERROR = 4; // 0x4
+ field public static final int ADVERTISE_FAILED_TOO_MANY_ADVERTISERS = 2; // 0x2
+ field public static final int ADVERTISE_SUCCESS = 0; // 0x0
+ }
+
+ public final class AdvertisingSetParameters implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getInterval();
+ method public int getPrimaryPhy();
+ method public int getSecondaryPhy();
+ method public int getTimeout();
+ method public int getTxPowerLevel();
+ method public boolean includeTxPower();
+ method public boolean isAnonymous();
+ method public boolean isConnectable();
+ method public boolean isLegacy();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.bluetooth.le.AdvertisingSetParameters> CREATOR;
+ field public static final int INTERVAL_HIGH = 160; // 0xa0
+ field public static final int INTERVAL_LOW = 1600; // 0x640
+ field public static final int INTERVAL_MAX = 16777215; // 0xffffff
+ field public static final int INTERVAL_MEDIUM = 400; // 0x190
+ field public static final int INTERVAL_MIN = 160; // 0xa0
+ field public static final int PHY_LE_1M = 1; // 0x1
+ field public static final int PHY_LE_2M = 2; // 0x2
+ field public static final int PHY_LE_CODED = 3; // 0x3
+ field public static final int TX_POWER_HIGH = 1; // 0x1
+ field public static final int TX_POWER_LOW = -15; // 0xfffffff1
+ field public static final int TX_POWER_MAX = 1; // 0x1
+ field public static final int TX_POWER_MEDIUM = -7; // 0xfffffff9
+ field public static final int TX_POWER_MIN = -127; // 0xffffff81
+ field public static final int TX_POWER_ULTRA_LOW = -21; // 0xffffffeb
+ }
+
+ public static final class AdvertisingSetParameters.Builder {
+ ctor public AdvertisingSetParameters.Builder();
+ method public android.bluetooth.le.AdvertisingSetParameters build();
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setAnonymouus(boolean);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setConnectable(boolean);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setIncludeTxPower(boolean);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setInterval(int);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setLegacyMode(boolean);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setPrimaryPhy(int);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setSecondaryPhy(int);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setTimeout(int);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setTxPowerLevel(int);
+ }
+
public final class BluetoothLeAdvertiser {
method public void startAdvertising(android.bluetooth.le.AdvertiseSettings, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseCallback);
method public void startAdvertising(android.bluetooth.le.AdvertiseSettings, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseCallback);
+ method public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertisingSetCallback);
+ method public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertisingSetCallback, android.os.Handler);
method public void stopAdvertising(android.bluetooth.le.AdvertiseCallback);
+ method public void stopAdvertisingSet(android.bluetooth.le.AdvertisingSetCallback);
}
public final class BluetoothLeScanner {
@@ -7486,6 +7592,53 @@
method public void stopScan(android.bluetooth.le.ScanCallback);
}
+ public abstract class PeriodicAdvertisingCallback {
+ ctor public PeriodicAdvertisingCallback();
+ method public void onPeriodicAdvertisingReport(android.bluetooth.le.PeriodicAdvertisingReport);
+ method public void onSyncEstablished(int, android.bluetooth.BluetoothDevice, int, int, int, int);
+ method public void onSyncLost(int);
+ field public static final int SYNC_NO_RESOURCES = 2; // 0x2
+ field public static final int SYNC_NO_RESPONSE = 1; // 0x1
+ }
+
+ public final class PeriodicAdvertisingManager {
+ method public void registerSync(android.bluetooth.le.ScanResult, int, int, android.bluetooth.le.PeriodicAdvertisingCallback);
+ method public void registerSync(android.bluetooth.le.ScanResult, int, int, android.bluetooth.le.PeriodicAdvertisingCallback, android.os.Handler);
+ method public void unregisterSync(android.bluetooth.le.PeriodicAdvertisingCallback);
+ }
+
+ public final class PeriodicAdvertisingParameters implements android.os.Parcelable {
+ method public int describeContents();
+ method public boolean getEnable();
+ method public boolean getIncludeTxPower();
+ method public int getInterval();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.bluetooth.le.PeriodicAdvertisingParameters> CREATOR;
+ }
+
+ public static final class PeriodicAdvertisingParameters.Builder {
+ ctor public PeriodicAdvertisingParameters.Builder();
+ method public android.bluetooth.le.PeriodicAdvertisingParameters build();
+ method public android.bluetooth.le.PeriodicAdvertisingParameters.Builder setEnable(boolean);
+ method public android.bluetooth.le.PeriodicAdvertisingParameters.Builder setIncludeTxPower(boolean);
+ method public android.bluetooth.le.PeriodicAdvertisingParameters.Builder setInterval(int);
+ }
+
+ public final class PeriodicAdvertisingReport implements android.os.Parcelable {
+ ctor public PeriodicAdvertisingReport(int, int, int, int, android.bluetooth.le.ScanRecord);
+ method public int describeContents();
+ method public android.bluetooth.le.ScanRecord getData();
+ method public int getDataStatus();
+ method public int getRssi();
+ method public int getSyncHandle();
+ method public long getTimestampNanos();
+ method public int getTxPower();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.bluetooth.le.PeriodicAdvertisingReport> CREATOR;
+ field public static final int DATA_COMPLETE = 0; // 0x0
+ field public static final int DATA_INCOMPLETE_TRUNCATED = 2; // 0x2
+ }
+
public abstract class ScanCallback {
ctor public ScanCallback();
method public void onBatchScanResults(java.util.List<android.bluetooth.le.ScanResult>);
@@ -7540,19 +7693,37 @@
}
public final class ScanResult implements android.os.Parcelable {
- ctor public ScanResult(android.bluetooth.BluetoothDevice, android.bluetooth.le.ScanRecord, int, long);
+ ctor public deprecated ScanResult(android.bluetooth.BluetoothDevice, android.bluetooth.le.ScanRecord, int, long);
+ ctor public ScanResult(android.bluetooth.BluetoothDevice, int, int, int, int, int, int, int, android.bluetooth.le.ScanRecord, long);
method public int describeContents();
+ method public int getAdvertisingSid();
+ method public int getDataStatus();
method public android.bluetooth.BluetoothDevice getDevice();
+ method public int getPeriodicAdvertisingInterval();
+ method public int getPrimaryPhy();
method public int getRssi();
method public android.bluetooth.le.ScanRecord getScanRecord();
+ method public int getSecondaryPhy();
method public long getTimestampNanos();
+ method public int getTxPower();
+ method public boolean isConnectable();
+ method public boolean isLegacy();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.bluetooth.le.ScanResult> CREATOR;
+ field public static final int DATA_COMPLETE = 0; // 0x0
+ field public static final int DATA_TRUNCATED = 2; // 0x2
+ field public static final int PHY_LE_1M = 1; // 0x1
+ field public static final int PHY_LE_2M = 2; // 0x2
+ field public static final int PHY_LE_CODED = 3; // 0x3
+ field public static final int PHY_UNUSED = 0; // 0x0
+ field public static final int SID_NOT_PRESENT = 255; // 0xff
}
public final class ScanSettings implements android.os.Parcelable {
method public int describeContents();
method public int getCallbackType();
+ method public boolean getLegacy();
+ method public int getPhy();
method public long getReportDelayMillis();
method public int getScanMode();
method public int getScanResultType();
@@ -7566,6 +7737,9 @@
field public static final int MATCH_NUM_FEW_ADVERTISEMENT = 2; // 0x2
field public static final int MATCH_NUM_MAX_ADVERTISEMENT = 3; // 0x3
field public static final int MATCH_NUM_ONE_ADVERTISEMENT = 1; // 0x1
+ field public static final int PHY_LE_1M = 1; // 0x1
+ field public static final int PHY_LE_ALL_SUPPORTED = 255; // 0xff
+ field public static final int PHY_LE_CODED = 3; // 0x3
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
@@ -7576,8 +7750,10 @@
ctor public ScanSettings.Builder();
method public android.bluetooth.le.ScanSettings build();
method public android.bluetooth.le.ScanSettings.Builder setCallbackType(int);
+ method public android.bluetooth.le.ScanSettings.Builder setLegacy(boolean);
method public android.bluetooth.le.ScanSettings.Builder setMatchMode(int);
method public android.bluetooth.le.ScanSettings.Builder setNumOfMatches(int);
+ method public android.bluetooth.le.ScanSettings.Builder setPhy(int);
method public android.bluetooth.le.ScanSettings.Builder setReportDelay(long);
method public android.bluetooth.le.ScanSettings.Builder setScanMode(int);
}
@@ -8188,6 +8364,7 @@
field public static final java.lang.String HARDWARE_PROPERTIES_SERVICE = "hardware_properties";
field public static final java.lang.String INPUT_METHOD_SERVICE = "input_method";
field public static final java.lang.String INPUT_SERVICE = "input";
+ field public static final java.lang.String IPSEC_SERVICE = "ipsec";
field public static final java.lang.String JOB_SCHEDULER_SERVICE = "jobscheduler";
field public static final java.lang.String KEYGUARD_SERVICE = "keyguard";
field public static final java.lang.String LAUNCHER_APPS_SERVICE = "launcherapps";
@@ -8553,6 +8730,7 @@
field public static final java.lang.String ACTION_CALL = "android.intent.action.CALL";
field public static final java.lang.String ACTION_CALL_BUTTON = "android.intent.action.CALL_BUTTON";
field public static final java.lang.String ACTION_CAMERA_BUTTON = "android.intent.action.CAMERA_BUTTON";
+ field public static final java.lang.String ACTION_CARRIER_SETUP = "android.intent.action.CARRIER_SETUP";
field public static final java.lang.String ACTION_CHOOSER = "android.intent.action.CHOOSER";
field public static final java.lang.String ACTION_CLOSE_SYSTEM_DIALOGS = "android.intent.action.CLOSE_SYSTEM_DIALOGS";
field public static final java.lang.String ACTION_CONFIGURATION_CHANGED = "android.intent.action.CONFIGURATION_CHANGED";
@@ -18723,6 +18901,8 @@
field public static final int SHORT_COMMONLY_USED = 6; // 0x6
field public static final int SHORT_GENERIC = 2; // 0x2
field public static final int SHORT_GMT = 4; // 0x4
+ field public static final int TIMEZONE_ICU = 0; // 0x0
+ field public static final int TIMEZONE_JDK = 1; // 0x1
field public static final android.icu.util.TimeZone UNKNOWN_ZONE;
field public static final java.lang.String UNKNOWN_ZONE_ID = "Etc/Unknown";
}
@@ -23632,7 +23812,9 @@
method public boolean isDefaultNetworkActive();
method public static deprecated boolean isNetworkTypeValid(int);
method public void registerDefaultNetworkCallback(android.net.ConnectivityManager.NetworkCallback);
+ method public void registerDefaultNetworkCallback(android.net.ConnectivityManager.NetworkCallback, android.os.Handler);
method public void registerNetworkCallback(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
+ method public void registerNetworkCallback(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback, android.os.Handler);
method public void registerNetworkCallback(android.net.NetworkRequest, android.app.PendingIntent);
method public void releaseNetworkRequest(android.app.PendingIntent);
method public void removeDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener);
@@ -23640,6 +23822,9 @@
method public void reportNetworkConnectivity(android.net.Network, boolean);
method public boolean requestBandwidthUpdate(android.net.Network);
method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
+ method public void requestNetwork(android.net.NetworkRequest, int, android.net.ConnectivityManager.NetworkCallback);
+ method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback, android.os.Handler);
+ method public void requestNetwork(android.net.NetworkRequest, int, android.net.ConnectivityManager.NetworkCallback, android.os.Handler);
method public void requestNetwork(android.net.NetworkRequest, android.app.PendingIntent);
method public deprecated boolean requestRouteToHost(int, int);
method public deprecated void setNetworkPreference(int);
@@ -23687,6 +23872,7 @@
method public void onLinkPropertiesChanged(android.net.Network, android.net.LinkProperties);
method public void onLosing(android.net.Network, int);
method public void onLost(android.net.Network);
+ method public void onUnavailable();
}
public static abstract interface ConnectivityManager.OnNetworkActiveListener {
@@ -23723,6 +23909,68 @@
field public static final android.os.Parcelable.Creator<android.net.IpPrefix> CREATOR;
}
+ public final class IpSecAlgorithm implements android.os.Parcelable {
+ ctor public IpSecAlgorithm(java.lang.String, byte[]);
+ ctor public IpSecAlgorithm(java.lang.String, byte[], int);
+ method public int describeContents();
+ method public byte[] getKey();
+ method public java.lang.String getName();
+ method public int getTruncationLengthBits();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final java.lang.String ALGO_AUTH_HMAC_MD5 = "hmac(md5)";
+ field public static final java.lang.String ALGO_AUTH_HMAC_SHA1 = "hmac(sha1)";
+ field public static final java.lang.String ALGO_AUTH_HMAC_SHA256 = "hmac(sha256)";
+ field public static final java.lang.String ALGO_AUTH_HMAC_SHA384 = "hmac(sha384)";
+ field public static final java.lang.String ALGO_AUTH_HMAC_SHA512 = "hmac(sha512)";
+ field public static final java.lang.String ALGO_CRYPT_AES_CBC = "cbc(aes)";
+ field public static final android.os.Parcelable.Creator<android.net.IpSecAlgorithm> CREATOR;
+ }
+
+ public final class IpSecManager {
+ method public void applyTransportModeTransform(java.net.Socket, android.net.IpSecTransform) throws java.io.IOException;
+ method public void applyTransportModeTransform(java.net.DatagramSocket, android.net.IpSecTransform) throws java.io.IOException;
+ method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket(int) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
+ method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket() throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
+ method public void removeTransportModeTransform(java.net.Socket, android.net.IpSecTransform);
+ method public void removeTransportModeTransform(java.net.DatagramSocket, android.net.IpSecTransform);
+ method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
+ field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0
+ }
+
+ public static final class IpSecManager.ResourceUnavailableException extends android.util.AndroidException {
+ }
+
+ public static final class IpSecManager.SecurityParameterIndex implements java.lang.AutoCloseable {
+ method public void close();
+ method public int getSpi();
+ }
+
+ public static final class IpSecManager.SpiUnavailableException extends android.util.AndroidException {
+ method public int getSpi();
+ }
+
+ public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable {
+ method public void close();
+ method public int getPort();
+ method public java.io.FileDescriptor getSocket();
+ }
+
+ public final class IpSecTransform implements java.lang.AutoCloseable {
+ method public void close();
+ field public static final int DIRECTION_IN = 0; // 0x0
+ field public static final int DIRECTION_OUT = 1; // 0x1
+ }
+
+ public static class IpSecTransform.Builder {
+ ctor public IpSecTransform.Builder(android.content.Context);
+ method public android.net.IpSecTransform buildTransportModeTransform(java.net.InetAddress) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
+ method public android.net.IpSecTransform.Builder setAuthentication(int, android.net.IpSecAlgorithm);
+ method public android.net.IpSecTransform.Builder setEncryption(int, android.net.IpSecAlgorithm);
+ method public android.net.IpSecTransform.Builder setIpv4Encapsulation(android.net.IpSecManager.UdpEncapsulationSocket, int);
+ method public android.net.IpSecTransform.Builder setSpi(int, int);
+ method public android.net.IpSecTransform.Builder setSpi(int, android.net.IpSecManager.SecurityParameterIndex);
+ }
+
public class LinkAddress implements android.os.Parcelable {
method public int describeContents();
method public java.net.InetAddress getAddress();
@@ -24913,10 +25161,9 @@
}
public class DiscoverySession {
- method public java.lang.String createNetworkSpecifier(android.net.wifi.aware.PeerHandle, byte[]);
+ method public java.lang.String createNetworkSpecifierOpen(android.net.wifi.aware.PeerHandle);
+ method public java.lang.String createNetworkSpecifierPassphrase(android.net.wifi.aware.PeerHandle, java.lang.String);
method public void destroy();
- method public static int getMaxSendRetryCount();
- method public void sendMessage(android.net.wifi.aware.PeerHandle, int, byte[], int);
method public void sendMessage(android.net.wifi.aware.PeerHandle, int, byte[]);
}
@@ -25003,7 +25250,8 @@
}
public class WifiAwareSession {
- method public java.lang.String createNetworkSpecifier(int, byte[], byte[]);
+ method public java.lang.String createNetworkSpecifierOpen(int, byte[]);
+ method public java.lang.String createNetworkSpecifierPassphrase(int, byte[], java.lang.String);
method public void destroy();
method public void publish(android.net.wifi.aware.PublishConfig, android.net.wifi.aware.DiscoverySessionCallback, android.os.Handler);
method public void subscribe(android.net.wifi.aware.SubscribeConfig, android.net.wifi.aware.DiscoverySessionCallback, android.os.Handler);
@@ -36581,9 +36829,11 @@
method public android.telecom.Call.Details getDetails();
method public android.telecom.Call getParent();
method public java.lang.String getRemainingPostDialSequence();
+ method public android.telecom.Call.RttCall getRttCall();
method public int getState();
method public android.telecom.InCallService.VideoCall getVideoCall();
method public void hold();
+ method public boolean isRttActive();
method public void mergeConference();
method public void phoneAccountSelected(android.telecom.PhoneAccountHandle, boolean);
method public void playDtmfTone(char);
@@ -36595,9 +36845,12 @@
method public void reject(boolean, java.lang.String);
method public final void removeExtras(java.util.List<java.lang.String>);
method public final void removeExtras(java.lang.String...);
+ method public void respondToRttRequest(int, boolean);
method public void sendCallEvent(java.lang.String, android.os.Bundle);
+ method public void sendRttRequest();
method public void splitFromConference();
method public void stopDtmfTone();
+ method public void stopRtt();
method public void swapConference();
method public void unhold();
method public void unregisterCallback(android.telecom.Call.Callback);
@@ -36624,6 +36877,10 @@
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 onRttInitiationFailure(android.telecom.Call, int);
+ method public void onRttModeChanged(android.telecom.Call, int);
+ method public void onRttRequest(android.telecom.Call, int);
+ method public void onRttStatusChanged(android.telecom.Call, boolean, android.telecom.Call.RttCall);
method public void onStateChanged(android.telecom.Call, int);
method public void onVideoCallChanged(android.telecom.Call, android.telecom.InCallService.VideoCall);
}
@@ -36674,9 +36931,20 @@
field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 128; // 0x80
field public static final int PROPERTY_HIGH_DEF_AUDIO = 16; // 0x10
field public static final int PROPERTY_IS_EXTERNAL_CALL = 64; // 0x40
+ field public static final int PROPERTY_SELF_MANAGED = 256; // 0x100
field public static final int PROPERTY_WIFI = 8; // 0x8
}
+ public static final class Call.RttCall {
+ method public int getRttAudioMode();
+ method public java.lang.String read();
+ method public void setRttMode(int);
+ method public void write(java.lang.String) throws java.io.IOException;
+ field public static final int RTT_MODE_FULL = 1; // 0x1
+ field public static final int RTT_MODE_HCO = 2; // 0x2
+ field public static final int RTT_MODE_VCO = 3; // 0x3
+ }
+
public final class CallAudioState implements android.os.Parcelable {
ctor public CallAudioState(boolean, int, int);
method public static java.lang.String audioRouteToString(int);
@@ -36876,6 +37144,15 @@
field public static final int STATE_RINGING = 2; // 0x2
}
+ public static final class Connection.RttModifyStatus {
+ ctor public Connection.RttModifyStatus();
+ 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 static abstract class Connection.VideoProvider {
ctor public Connection.VideoProvider();
method public void changeCameraCapabilities(android.telecom.VideoProfile.CameraCapabilities);
@@ -36933,9 +37210,9 @@
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 void onCreateIncomingConnectionFailed(android.telecom.ConnectionRequest);
+ method public void onCreateIncomingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
method public android.telecom.Connection onCreateOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
- method public void onCreateOutgoingConnectionFailed(android.telecom.ConnectionRequest);
+ method public void onCreateOutgoingConnectionFailed(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";
@@ -37048,6 +37325,7 @@
field public static final int CAPABILITY_CALL_SUBJECT = 64; // 0x40
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_RTT = 4096; // 0x1000
field public static final int CAPABILITY_SELF_MANAGED = 2048; // 0x800
field public static final int CAPABILITY_SIM_SUBSCRIPTION = 4; // 0x4
field public static final int CAPABILITY_SUPPORTS_VIDEO_CALLING = 1024; // 0x400
@@ -37230,6 +37508,7 @@
method public boolean handleMmi(java.lang.String);
method public boolean handleMmi(java.lang.String, android.telecom.PhoneAccountHandle);
method public boolean isInCall();
+ method public boolean isInManagedCall();
method public boolean isIncomingCallPermitted(android.telecom.PhoneAccountHandle);
method public boolean isOutgoingCallPermitted(android.telecom.PhoneAccountHandle);
method public boolean isVoiceMailNumber(android.telecom.PhoneAccountHandle, java.lang.String);
@@ -37260,11 +37539,13 @@
field public static final java.lang.String EXTRA_NOTIFICATION_PHONE_NUMBER = "android.telecom.extra.NOTIFICATION_PHONE_NUMBER";
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_RTT = "android.telecom.extra.START_CALL_WITH_RTT";
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 java.lang.String METADATA_INCLUDE_EXTERNAL_CALLS = "android.telecom.INCLUDE_EXTERNAL_CALLS";
+ field public static final java.lang.String METADATA_INCLUDE_SELF_MANAGED_CALLS = "android.telecom.INCLUDE_SELF_MANAGED_CALLS";
field public static final java.lang.String METADATA_IN_CALL_SERVICE_RINGING = "android.telecom.IN_CALL_SERVICE_RINGING";
field public static final java.lang.String METADATA_IN_CALL_SERVICE_UI = "android.telecom.IN_CALL_SERVICE_UI";
field public static final int PRESENTATION_ALLOWED = 1; // 0x1
@@ -37413,6 +37694,7 @@
field public static final java.lang.String KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL = "show_onscreen_dial_button_bool";
field public static final java.lang.String KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL = "simplified_network_settings_bool";
field public static final java.lang.String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool";
+ field public static final java.lang.String KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL = "support_3gpp_call_forwarding_while_roaming_bool";
field public static final java.lang.String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool";
field public static final java.lang.String KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL = "support_pause_ims_video_calls_bool";
field public static final java.lang.String KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL = "support_swap_after_merge_bool";
@@ -37932,6 +38214,7 @@
method public java.lang.String getDeviceId();
method public java.lang.String getDeviceId(int);
method public java.lang.String getDeviceSoftwareVersion();
+ method public java.lang.String[] getForbiddenPlmns();
method public java.lang.String getGroupIdLevel1();
method public java.lang.String getIccAuthentication(int, int, java.lang.String);
method public java.lang.String getLine1Number();
@@ -38737,7 +39020,7 @@
package android.test.suitebuilder {
- public class TestMethod {
+ public deprecated class TestMethod {
ctor public TestMethod(java.lang.reflect.Method, java.lang.Class<? extends junit.framework.TestCase>);
ctor public TestMethod(java.lang.String, java.lang.Class<? extends junit.framework.TestCase>);
ctor public TestMethod(junit.framework.TestCase);
@@ -38748,7 +39031,7 @@
method public java.lang.String getName();
}
- public class TestSuiteBuilder {
+ public deprecated class TestSuiteBuilder {
ctor public TestSuiteBuilder(java.lang.Class);
ctor public TestSuiteBuilder(java.lang.String, java.lang.ClassLoader);
method public android.test.suitebuilder.TestSuiteBuilder addRequirements(java.util.List<com.android.internal.util.Predicate<android.test.suitebuilder.TestMethod>>);
@@ -38761,7 +39044,7 @@
method public android.test.suitebuilder.TestSuiteBuilder named(java.lang.String);
}
- public static class TestSuiteBuilder.FailedToCreateTests extends junit.framework.TestCase {
+ public static deprecated class TestSuiteBuilder.FailedToCreateTests extends junit.framework.TestCase {
ctor public TestSuiteBuilder.FailedToCreateTests(java.lang.Exception);
method public void testSuiteConstructionFailed();
}
@@ -49131,7 +49414,7 @@
package com.android.internal.util {
- public abstract interface Predicate<T> {
+ public abstract deprecated interface Predicate<T> {
method public abstract boolean apply(T);
}
@@ -49269,6 +49552,8 @@
field public static final int OP_INT_TO_FLOAT = 130; // 0x82
field public static final int OP_INT_TO_LONG = 129; // 0x81
field public static final int OP_INT_TO_SHORT = 143; // 0x8f
+ field public static final int OP_INVOKE_CUSTOM = 252; // 0xfc
+ field public static final int OP_INVOKE_CUSTOM_RANGE = 253; // 0xfd
field public static final int OP_INVOKE_DIRECT = 112; // 0x70
field public static final deprecated int OP_INVOKE_DIRECT_EMPTY = 240; // 0xf0
field public static final int OP_INVOKE_DIRECT_JUMBO = 9471; // 0x24ff
@@ -49459,7 +49744,7 @@
method public static dalvik.system.DexFile loadDex(java.lang.String, java.lang.String, int) throws java.io.IOException;
}
- public final class InMemoryDexClassLoader extends java.lang.ClassLoader {
+ public final class InMemoryDexClassLoader extends dalvik.system.BaseDexClassLoader {
ctor public InMemoryDexClassLoader(java.nio.ByteBuffer, java.lang.ClassLoader);
}
@@ -52670,7 +52955,6 @@
method public java.lang.Class<?> lookupClass();
method public int lookupModes();
method public java.lang.invoke.MethodHandleInfo revealDirect(java.lang.invoke.MethodHandle);
- method public void throwMakeAccessException(java.lang.String, java.lang.Object) throws java.lang.IllegalAccessException;
method public java.lang.invoke.MethodHandle unreflect(java.lang.reflect.Method) throws java.lang.IllegalAccessException;
method public java.lang.invoke.MethodHandle unreflectConstructor(java.lang.reflect.Constructor<?>) throws java.lang.IllegalAccessException;
method public java.lang.invoke.MethodHandle unreflectGetter(java.lang.reflect.Field) throws java.lang.IllegalAccessException;
diff --git a/api/system-current.txt b/api/system-current.txt
index aa8577a..a89d3ca 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6993,6 +6993,7 @@
method public java.util.Set<android.bluetooth.BluetoothDevice> getBondedDevices();
method public static synchronized android.bluetooth.BluetoothAdapter getDefaultAdapter();
method public java.lang.String getName();
+ method public android.bluetooth.le.PeriodicAdvertisingManager getPeriodicAdvertisingManager();
method public int getProfileConnectionState(int);
method public boolean getProfileProxy(android.content.Context, android.bluetooth.BluetoothProfile.ServiceListener, int);
method public android.bluetooth.BluetoothDevice getRemoteDevice(java.lang.String);
@@ -7002,7 +7003,11 @@
method public boolean isBleScanAlwaysAvailable();
method public boolean isDiscovering();
method public boolean isEnabled();
+ method public boolean isLe2MPhySupported();
+ method public boolean isLeCodedPhySupported();
method public boolean isLeEnabled();
+ method public boolean isLeExtendedAdvertisingSupported();
+ method public boolean isLePeriodicAdvertisingSupported();
method public boolean isMultipleAdvertisementSupported();
method public boolean isOffloadedFilteringSupported();
method public boolean isOffloadedScanBatchingSupported();
@@ -7373,6 +7378,9 @@
public final class BluetoothDevice implements android.os.Parcelable {
method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback);
method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback, int);
+ method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallbackExt);
+ method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallbackExt, int);
+ method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallbackExt, int, int);
method public boolean createBond();
method public android.bluetooth.BluetoothSocket createInsecureRfcommSocketToServiceRecord(java.util.UUID) throws java.io.IOException;
method public android.bluetooth.BluetoothSocket createRfcommSocketToServiceRecord(java.util.UUID) throws java.io.IOException;
@@ -7418,6 +7426,13 @@
field public static final java.lang.String EXTRA_UUID = "android.bluetooth.device.extra.UUID";
field public static final int PAIRING_VARIANT_PASSKEY_CONFIRMATION = 2; // 0x2
field public static final int PAIRING_VARIANT_PIN = 0; // 0x0
+ field public static final int PHY_LE_1M = 1; // 0x1
+ field public static final int PHY_LE_2M = 2; // 0x2
+ field public static final int PHY_LE_ANY = 7; // 0x7
+ field public static final int PHY_LE_CODED = 4; // 0x4
+ field public static final int PHY_OPTION_NO_PREFERRED = 0; // 0x0
+ field public static final int PHY_OPTION_S2 = 1; // 0x1
+ field public static final int PHY_OPTION_S8 = 2; // 0x2
field public static final int TRANSPORT_AUTO = 0; // 0x0
field public static final int TRANSPORT_BREDR = 1; // 0x1
field public static final int TRANSPORT_LE = 2; // 0x2
@@ -7440,10 +7455,12 @@
method public java.util.List<android.bluetooth.BluetoothGattService> getServices();
method public boolean readCharacteristic(android.bluetooth.BluetoothGattCharacteristic);
method public boolean readDescriptor(android.bluetooth.BluetoothGattDescriptor);
+ method public void readPhy();
method public boolean readRemoteRssi();
method public boolean requestConnectionPriority(int);
method public boolean requestMtu(int);
method public boolean setCharacteristicNotification(android.bluetooth.BluetoothGattCharacteristic, boolean);
+ method public void setPreferredPhy(int, int, int);
method public boolean writeCharacteristic(android.bluetooth.BluetoothGattCharacteristic);
method public boolean writeDescriptor(android.bluetooth.BluetoothGattDescriptor);
field public static final int CONNECTION_PRIORITY_BALANCED = 0; // 0x0
@@ -7461,8 +7478,12 @@
field public static final int GATT_WRITE_NOT_PERMITTED = 3; // 0x3
}
- public abstract class BluetoothGattCallback {
+ public abstract deprecated class BluetoothGattCallback extends android.bluetooth.BluetoothGattCallbackExt {
ctor public BluetoothGattCallback();
+ }
+
+ public abstract class BluetoothGattCallbackExt {
+ ctor public BluetoothGattCallbackExt();
method public void onCharacteristicChanged(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic);
method public void onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int);
method public void onCharacteristicWrite(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int);
@@ -7470,6 +7491,8 @@
method public void onDescriptorRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattDescriptor, int);
method public void onDescriptorWrite(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattDescriptor, int);
method public void onMtuChanged(android.bluetooth.BluetoothGatt, int, int);
+ method public void onPhyRead(android.bluetooth.BluetoothGatt, int, int, int);
+ method public void onPhyUpdate(android.bluetooth.BluetoothGatt, int, int, int);
method public void onReadRemoteRssi(android.bluetooth.BluetoothGatt, int, int);
method public void onReliableWriteCompleted(android.bluetooth.BluetoothGatt, int);
method public void onServicesDiscovered(android.bluetooth.BluetoothGatt, int);
@@ -7563,12 +7586,18 @@
method public android.bluetooth.BluetoothGattService getService(java.util.UUID);
method public java.util.List<android.bluetooth.BluetoothGattService> getServices();
method public boolean notifyCharacteristicChanged(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothGattCharacteristic, boolean);
+ method public void readPhy(android.bluetooth.BluetoothDevice);
method public boolean removeService(android.bluetooth.BluetoothGattService);
method public boolean sendResponse(android.bluetooth.BluetoothDevice, int, int, int, byte[]);
+ method public void setPreferredPhy(android.bluetooth.BluetoothDevice, int, int, int);
}
- public abstract class BluetoothGattServerCallback {
+ public abstract deprecated class BluetoothGattServerCallback extends android.bluetooth.BluetoothGattServerCallbackExt {
ctor public BluetoothGattServerCallback();
+ }
+
+ public abstract class BluetoothGattServerCallbackExt {
+ ctor public BluetoothGattServerCallbackExt();
method public void onCharacteristicReadRequest(android.bluetooth.BluetoothDevice, int, int, android.bluetooth.BluetoothGattCharacteristic);
method public void onCharacteristicWriteRequest(android.bluetooth.BluetoothDevice, int, android.bluetooth.BluetoothGattCharacteristic, boolean, boolean, int, byte[]);
method public void onConnectionStateChange(android.bluetooth.BluetoothDevice, int, int);
@@ -7577,6 +7606,8 @@
method public void onExecuteWrite(android.bluetooth.BluetoothDevice, int, boolean);
method public void onMtuChanged(android.bluetooth.BluetoothDevice, int);
method public void onNotificationSent(android.bluetooth.BluetoothDevice, int);
+ method public void onPhyRead(android.bluetooth.BluetoothDevice, int, int, int);
+ method public void onPhyUpdate(android.bluetooth.BluetoothDevice, int, int, int);
method public void onServiceAdded(int, android.bluetooth.BluetoothGattService);
}
@@ -7777,10 +7808,85 @@
method public android.bluetooth.le.AdvertiseSettings.Builder setTxPowerLevel(int);
}
+ public final class AdvertisingSet {
+ method public void enableAdvertising(boolean);
+ method public void periodicAdvertisingEnable(boolean);
+ method public void setAdvertisingData(android.bluetooth.le.AdvertiseData);
+ method public void setAdvertisingParameters(android.bluetooth.le.AdvertisingSetParameters);
+ method public void setPeriodicAdvertisingData(android.bluetooth.le.AdvertiseData);
+ method public void setPeriodicAdvertisingParameters(android.bluetooth.le.PeriodicAdvertisingParameters);
+ method public void setScanResponseData(android.bluetooth.le.AdvertiseData);
+ }
+
+ public abstract class AdvertisingSetCallback {
+ ctor public AdvertisingSetCallback();
+ method public void onAdvertisingDataSet(android.bluetooth.le.AdvertisingSet, int);
+ method public void onAdvertisingEnabled(android.bluetooth.le.AdvertisingSet, boolean, int);
+ method public void onAdvertisingParametersUpdated(android.bluetooth.le.AdvertisingSet, int);
+ method public void onAdvertisingSetStarted(android.bluetooth.le.AdvertisingSet, int);
+ method public void onAdvertisingSetStopped(android.bluetooth.le.AdvertisingSet);
+ method public void onPeriodicAdvertisingDataSet(android.bluetooth.le.AdvertisingSet, int);
+ method public void onPeriodicAdvertisingEnable(android.bluetooth.le.AdvertisingSet, boolean, int);
+ method public void onPeriodicAdvertisingParametersUpdated(android.bluetooth.le.AdvertisingSet, int);
+ method public void onScanResponseDataSet(android.bluetooth.le.AdvertisingSet, int);
+ field public static final int ADVERTISE_FAILED_ALREADY_STARTED = 3; // 0x3
+ field public static final int ADVERTISE_FAILED_DATA_TOO_LARGE = 1; // 0x1
+ field public static final int ADVERTISE_FAILED_FEATURE_UNSUPPORTED = 5; // 0x5
+ field public static final int ADVERTISE_FAILED_INTERNAL_ERROR = 4; // 0x4
+ field public static final int ADVERTISE_FAILED_TOO_MANY_ADVERTISERS = 2; // 0x2
+ field public static final int ADVERTISE_SUCCESS = 0; // 0x0
+ }
+
+ public final class AdvertisingSetParameters implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getInterval();
+ method public int getPrimaryPhy();
+ method public int getSecondaryPhy();
+ method public int getTimeout();
+ method public int getTxPowerLevel();
+ method public boolean includeTxPower();
+ method public boolean isAnonymous();
+ method public boolean isConnectable();
+ method public boolean isLegacy();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.bluetooth.le.AdvertisingSetParameters> CREATOR;
+ field public static final int INTERVAL_HIGH = 160; // 0xa0
+ field public static final int INTERVAL_LOW = 1600; // 0x640
+ field public static final int INTERVAL_MAX = 16777215; // 0xffffff
+ field public static final int INTERVAL_MEDIUM = 400; // 0x190
+ field public static final int INTERVAL_MIN = 160; // 0xa0
+ field public static final int PHY_LE_1M = 1; // 0x1
+ field public static final int PHY_LE_2M = 2; // 0x2
+ field public static final int PHY_LE_CODED = 3; // 0x3
+ field public static final int TX_POWER_HIGH = 1; // 0x1
+ field public static final int TX_POWER_LOW = -15; // 0xfffffff1
+ field public static final int TX_POWER_MAX = 1; // 0x1
+ field public static final int TX_POWER_MEDIUM = -7; // 0xfffffff9
+ field public static final int TX_POWER_MIN = -127; // 0xffffff81
+ field public static final int TX_POWER_ULTRA_LOW = -21; // 0xffffffeb
+ }
+
+ public static final class AdvertisingSetParameters.Builder {
+ ctor public AdvertisingSetParameters.Builder();
+ method public android.bluetooth.le.AdvertisingSetParameters build();
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setAnonymouus(boolean);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setConnectable(boolean);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setIncludeTxPower(boolean);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setInterval(int);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setLegacyMode(boolean);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setPrimaryPhy(int);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setSecondaryPhy(int);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setTimeout(int);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setTxPowerLevel(int);
+ }
+
public final class BluetoothLeAdvertiser {
method public void startAdvertising(android.bluetooth.le.AdvertiseSettings, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseCallback);
method public void startAdvertising(android.bluetooth.le.AdvertiseSettings, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseCallback);
+ method public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertisingSetCallback);
+ method public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertisingSetCallback, android.os.Handler);
method public void stopAdvertising(android.bluetooth.le.AdvertiseCallback);
+ method public void stopAdvertisingSet(android.bluetooth.le.AdvertisingSetCallback);
}
public final class BluetoothLeScanner {
@@ -7793,6 +7899,53 @@
method public void stopScan(android.bluetooth.le.ScanCallback);
}
+ public abstract class PeriodicAdvertisingCallback {
+ ctor public PeriodicAdvertisingCallback();
+ method public void onPeriodicAdvertisingReport(android.bluetooth.le.PeriodicAdvertisingReport);
+ method public void onSyncEstablished(int, android.bluetooth.BluetoothDevice, int, int, int, int);
+ method public void onSyncLost(int);
+ field public static final int SYNC_NO_RESOURCES = 2; // 0x2
+ field public static final int SYNC_NO_RESPONSE = 1; // 0x1
+ }
+
+ public final class PeriodicAdvertisingManager {
+ method public void registerSync(android.bluetooth.le.ScanResult, int, int, android.bluetooth.le.PeriodicAdvertisingCallback);
+ method public void registerSync(android.bluetooth.le.ScanResult, int, int, android.bluetooth.le.PeriodicAdvertisingCallback, android.os.Handler);
+ method public void unregisterSync(android.bluetooth.le.PeriodicAdvertisingCallback);
+ }
+
+ public final class PeriodicAdvertisingParameters implements android.os.Parcelable {
+ method public int describeContents();
+ method public boolean getEnable();
+ method public boolean getIncludeTxPower();
+ method public int getInterval();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.bluetooth.le.PeriodicAdvertisingParameters> CREATOR;
+ }
+
+ public static final class PeriodicAdvertisingParameters.Builder {
+ ctor public PeriodicAdvertisingParameters.Builder();
+ method public android.bluetooth.le.PeriodicAdvertisingParameters build();
+ method public android.bluetooth.le.PeriodicAdvertisingParameters.Builder setEnable(boolean);
+ method public android.bluetooth.le.PeriodicAdvertisingParameters.Builder setIncludeTxPower(boolean);
+ method public android.bluetooth.le.PeriodicAdvertisingParameters.Builder setInterval(int);
+ }
+
+ public final class PeriodicAdvertisingReport implements android.os.Parcelable {
+ ctor public PeriodicAdvertisingReport(int, int, int, int, android.bluetooth.le.ScanRecord);
+ method public int describeContents();
+ method public android.bluetooth.le.ScanRecord getData();
+ method public int getDataStatus();
+ method public int getRssi();
+ method public int getSyncHandle();
+ method public long getTimestampNanos();
+ method public int getTxPower();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.bluetooth.le.PeriodicAdvertisingReport> CREATOR;
+ field public static final int DATA_COMPLETE = 0; // 0x0
+ field public static final int DATA_INCOMPLETE_TRUNCATED = 2; // 0x2
+ }
+
public final class ResultStorageDescriptor implements android.os.Parcelable {
ctor public ResultStorageDescriptor(int, int, int);
method public int describeContents();
@@ -7857,19 +8010,37 @@
}
public final class ScanResult implements android.os.Parcelable {
- ctor public ScanResult(android.bluetooth.BluetoothDevice, android.bluetooth.le.ScanRecord, int, long);
+ ctor public deprecated ScanResult(android.bluetooth.BluetoothDevice, android.bluetooth.le.ScanRecord, int, long);
+ ctor public ScanResult(android.bluetooth.BluetoothDevice, int, int, int, int, int, int, int, android.bluetooth.le.ScanRecord, long);
method public int describeContents();
+ method public int getAdvertisingSid();
+ method public int getDataStatus();
method public android.bluetooth.BluetoothDevice getDevice();
+ method public int getPeriodicAdvertisingInterval();
+ method public int getPrimaryPhy();
method public int getRssi();
method public android.bluetooth.le.ScanRecord getScanRecord();
+ method public int getSecondaryPhy();
method public long getTimestampNanos();
+ method public int getTxPower();
+ method public boolean isConnectable();
+ method public boolean isLegacy();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.bluetooth.le.ScanResult> CREATOR;
+ field public static final int DATA_COMPLETE = 0; // 0x0
+ field public static final int DATA_TRUNCATED = 2; // 0x2
+ field public static final int PHY_LE_1M = 1; // 0x1
+ field public static final int PHY_LE_2M = 2; // 0x2
+ field public static final int PHY_LE_CODED = 3; // 0x3
+ field public static final int PHY_UNUSED = 0; // 0x0
+ field public static final int SID_NOT_PRESENT = 255; // 0xff
}
public final class ScanSettings implements android.os.Parcelable {
method public int describeContents();
method public int getCallbackType();
+ method public boolean getLegacy();
+ method public int getPhy();
method public long getReportDelayMillis();
method public int getScanMode();
method public int getScanResultType();
@@ -7883,6 +8054,9 @@
field public static final int MATCH_NUM_FEW_ADVERTISEMENT = 2; // 0x2
field public static final int MATCH_NUM_MAX_ADVERTISEMENT = 3; // 0x3
field public static final int MATCH_NUM_ONE_ADVERTISEMENT = 1; // 0x1
+ field public static final int PHY_LE_1M = 1; // 0x1
+ field public static final int PHY_LE_ALL_SUPPORTED = 255; // 0xff
+ field public static final int PHY_LE_CODED = 3; // 0x3
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
@@ -7895,8 +8069,10 @@
ctor public ScanSettings.Builder();
method public android.bluetooth.le.ScanSettings build();
method public android.bluetooth.le.ScanSettings.Builder setCallbackType(int);
+ method public android.bluetooth.le.ScanSettings.Builder setLegacy(boolean);
method public android.bluetooth.le.ScanSettings.Builder setMatchMode(int);
method public android.bluetooth.le.ScanSettings.Builder setNumOfMatches(int);
+ method public android.bluetooth.le.ScanSettings.Builder setPhy(int);
method public android.bluetooth.le.ScanSettings.Builder setReportDelay(long);
method public android.bluetooth.le.ScanSettings.Builder setScanMode(int);
method public android.bluetooth.le.ScanSettings.Builder setScanResultType(int);
@@ -8522,6 +8698,7 @@
field public static final java.lang.String HDMI_CONTROL_SERVICE = "hdmi_control";
field public static final java.lang.String INPUT_METHOD_SERVICE = "input_method";
field public static final java.lang.String INPUT_SERVICE = "input";
+ field public static final java.lang.String IPSEC_SERVICE = "ipsec";
field public static final java.lang.String JOB_SCHEDULER_SERVICE = "jobscheduler";
field public static final java.lang.String KEYGUARD_SERVICE = "keyguard";
field public static final java.lang.String LAUNCHER_APPS_SERVICE = "launcherapps";
@@ -8895,6 +9072,7 @@
field public static final java.lang.String ACTION_CALL = "android.intent.action.CALL";
field public static final java.lang.String ACTION_CALL_BUTTON = "android.intent.action.CALL_BUTTON";
field public static final java.lang.String ACTION_CAMERA_BUTTON = "android.intent.action.CAMERA_BUTTON";
+ field public static final java.lang.String ACTION_CARRIER_SETUP = "android.intent.action.CARRIER_SETUP";
field public static final java.lang.String ACTION_CHOOSER = "android.intent.action.CHOOSER";
field public static final java.lang.String ACTION_CLOSE_SYSTEM_DIALOGS = "android.intent.action.CLOSE_SYSTEM_DIALOGS";
field public static final java.lang.String ACTION_CONFIGURATION_CHANGED = "android.intent.action.CONFIGURATION_CHANGED";
@@ -19937,6 +20115,8 @@
field public static final int SHORT_COMMONLY_USED = 6; // 0x6
field public static final int SHORT_GENERIC = 2; // 0x2
field public static final int SHORT_GMT = 4; // 0x4
+ field public static final int TIMEZONE_ICU = 0; // 0x0
+ field public static final int TIMEZONE_JDK = 1; // 0x1
field public static final android.icu.util.TimeZone UNKNOWN_ZONE;
field public static final java.lang.String UNKNOWN_ZONE_ID = "Etc/Unknown";
}
@@ -25419,7 +25599,9 @@
method public static deprecated boolean isNetworkTypeValid(int);
method public boolean isTetheringSupported();
method public void registerDefaultNetworkCallback(android.net.ConnectivityManager.NetworkCallback);
+ method public void registerDefaultNetworkCallback(android.net.ConnectivityManager.NetworkCallback, android.os.Handler);
method public void registerNetworkCallback(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
+ method public void registerNetworkCallback(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback, android.os.Handler);
method public void registerNetworkCallback(android.net.NetworkRequest, android.app.PendingIntent);
method public void releaseNetworkRequest(android.app.PendingIntent);
method public void removeDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener);
@@ -25427,6 +25609,9 @@
method public void reportNetworkConnectivity(android.net.Network, boolean);
method public boolean requestBandwidthUpdate(android.net.Network);
method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
+ method public void requestNetwork(android.net.NetworkRequest, int, android.net.ConnectivityManager.NetworkCallback);
+ method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback, android.os.Handler);
+ method public void requestNetwork(android.net.NetworkRequest, int, android.net.ConnectivityManager.NetworkCallback, android.os.Handler);
method public void requestNetwork(android.net.NetworkRequest, android.app.PendingIntent);
method public deprecated boolean requestRouteToHost(int, int);
method public deprecated void setNetworkPreference(int);
@@ -25480,6 +25665,7 @@
method public void onLinkPropertiesChanged(android.net.Network, android.net.LinkProperties);
method public void onLosing(android.net.Network, int);
method public void onLost(android.net.Network);
+ method public void onUnavailable();
}
public static abstract interface ConnectivityManager.OnNetworkActiveListener {
@@ -25560,6 +25746,73 @@
field public static final android.os.Parcelable.Creator<android.net.IpPrefix> CREATOR;
}
+ public final class IpSecAlgorithm implements android.os.Parcelable {
+ ctor public IpSecAlgorithm(java.lang.String, byte[]);
+ ctor public IpSecAlgorithm(java.lang.String, byte[], int);
+ method public int describeContents();
+ method public byte[] getKey();
+ method public java.lang.String getName();
+ method public int getTruncationLengthBits();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final java.lang.String ALGO_AUTH_HMAC_MD5 = "hmac(md5)";
+ field public static final java.lang.String ALGO_AUTH_HMAC_SHA1 = "hmac(sha1)";
+ field public static final java.lang.String ALGO_AUTH_HMAC_SHA256 = "hmac(sha256)";
+ field public static final java.lang.String ALGO_AUTH_HMAC_SHA384 = "hmac(sha384)";
+ field public static final java.lang.String ALGO_AUTH_HMAC_SHA512 = "hmac(sha512)";
+ field public static final java.lang.String ALGO_CRYPT_AES_CBC = "cbc(aes)";
+ field public static final android.os.Parcelable.Creator<android.net.IpSecAlgorithm> CREATOR;
+ }
+
+ public final class IpSecManager {
+ method public void applyTransportModeTransform(java.net.Socket, android.net.IpSecTransform) throws java.io.IOException;
+ method public void applyTransportModeTransform(java.net.DatagramSocket, android.net.IpSecTransform) throws java.io.IOException;
+ method public void applyTunnelModeTransform(android.net.Network, android.net.IpSecTransform);
+ method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket(int) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
+ method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket() throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
+ method public void removeTransportModeTransform(java.net.Socket, android.net.IpSecTransform);
+ method public void removeTransportModeTransform(java.net.DatagramSocket, android.net.IpSecTransform);
+ method public void removeTunnelModeTransform(android.net.Network, android.net.IpSecTransform);
+ method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
+ field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0
+ }
+
+ public static final class IpSecManager.ResourceUnavailableException extends android.util.AndroidException {
+ }
+
+ public static final class IpSecManager.SecurityParameterIndex implements java.lang.AutoCloseable {
+ method public void close();
+ method public int getSpi();
+ }
+
+ public static final class IpSecManager.SpiUnavailableException extends android.util.AndroidException {
+ method public int getSpi();
+ }
+
+ public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable {
+ method public void close();
+ method public int getPort();
+ method public java.io.FileDescriptor getSocket();
+ }
+
+ public final class IpSecTransform implements java.lang.AutoCloseable {
+ method public void close();
+ field public static final int DIRECTION_IN = 0; // 0x0
+ field public static final int DIRECTION_OUT = 1; // 0x1
+ }
+
+ public static class IpSecTransform.Builder {
+ ctor public IpSecTransform.Builder(android.content.Context);
+ method public android.net.IpSecTransform buildTransportModeTransform(java.net.InetAddress) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
+ method public android.net.IpSecTransform buildTunnelModeTransform(java.net.InetAddress, java.net.InetAddress);
+ method public android.net.IpSecTransform.Builder setAuthentication(int, android.net.IpSecAlgorithm);
+ method public android.net.IpSecTransform.Builder setEncryption(int, android.net.IpSecAlgorithm);
+ method public android.net.IpSecTransform.Builder setIpv4Encapsulation(android.net.IpSecManager.UdpEncapsulationSocket, int);
+ method public android.net.IpSecTransform.Builder setNattKeepalive(int);
+ method public android.net.IpSecTransform.Builder setSpi(int, int);
+ method public android.net.IpSecTransform.Builder setSpi(int, android.net.IpSecManager.SecurityParameterIndex);
+ method public android.net.IpSecTransform.Builder setUnderlyingNetwork(android.net.Network);
+ }
+
public class LinkAddress implements android.os.Parcelable {
method public int describeContents();
method public java.net.InetAddress getAddress();
@@ -27466,10 +27719,10 @@
}
public class DiscoverySession {
- method public java.lang.String createNetworkSpecifier(android.net.wifi.aware.PeerHandle, byte[]);
+ method public java.lang.String createNetworkSpecifierOpen(android.net.wifi.aware.PeerHandle);
+ method public java.lang.String createNetworkSpecifierPassphrase(android.net.wifi.aware.PeerHandle, java.lang.String);
+ method public java.lang.String createNetworkSpecifierPmk(android.net.wifi.aware.PeerHandle, byte[]);
method public void destroy();
- method public static int getMaxSendRetryCount();
- method public void sendMessage(android.net.wifi.aware.PeerHandle, int, byte[], int);
method public void sendMessage(android.net.wifi.aware.PeerHandle, int, byte[]);
}
@@ -27556,7 +27809,9 @@
}
public class WifiAwareSession {
- method public java.lang.String createNetworkSpecifier(int, byte[], byte[]);
+ method public java.lang.String createNetworkSpecifierOpen(int, byte[]);
+ method public java.lang.String createNetworkSpecifierPassphrase(int, byte[], java.lang.String);
+ method public java.lang.String createNetworkSpecifierPmk(int, byte[], byte[]);
method public void destroy();
method public void publish(android.net.wifi.aware.PublishConfig, android.net.wifi.aware.DiscoverySessionCallback, android.os.Handler);
method public void subscribe(android.net.wifi.aware.SubscribeConfig, android.net.wifi.aware.DiscoverySessionCallback, android.os.Handler);
@@ -39548,9 +39803,11 @@
method public android.telecom.Call.Details getDetails();
method public android.telecom.Call getParent();
method public java.lang.String getRemainingPostDialSequence();
+ method public android.telecom.Call.RttCall getRttCall();
method public int getState();
method public android.telecom.InCallService.VideoCall getVideoCall();
method public void hold();
+ method public boolean isRttActive();
method public void mergeConference();
method public void phoneAccountSelected(android.telecom.PhoneAccountHandle, boolean);
method public void playDtmfTone(char);
@@ -39563,9 +39820,12 @@
method public final void removeExtras(java.util.List<java.lang.String>);
method public final void removeExtras(java.lang.String...);
method public deprecated void removeListener(android.telecom.Call.Listener);
+ method public void respondToRttRequest(int, boolean);
method public void sendCallEvent(java.lang.String, android.os.Bundle);
+ method public void sendRttRequest();
method public void splitFromConference();
method public void stopDtmfTone();
+ method public void stopRtt();
method public void swapConference();
method public void unhold();
method public void unregisterCallback(android.telecom.Call.Callback);
@@ -39593,6 +39853,10 @@
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 onRttInitiationFailure(android.telecom.Call, int);
+ method public void onRttModeChanged(android.telecom.Call, int);
+ method public void onRttRequest(android.telecom.Call, int);
+ method public void onRttStatusChanged(android.telecom.Call, boolean, android.telecom.Call.RttCall);
method public void onStateChanged(android.telecom.Call, int);
method public void onVideoCallChanged(android.telecom.Call, android.telecom.InCallService.VideoCall);
}
@@ -39643,6 +39907,7 @@
field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 128; // 0x80
field public static final int PROPERTY_HIGH_DEF_AUDIO = 16; // 0x10
field public static final int PROPERTY_IS_EXTERNAL_CALL = 64; // 0x40
+ field public static final int PROPERTY_SELF_MANAGED = 256; // 0x100
field public static final int PROPERTY_WIFI = 8; // 0x8
}
@@ -39650,6 +39915,16 @@
ctor public Call.Listener();
}
+ public static final class Call.RttCall {
+ method public int getRttAudioMode();
+ method public java.lang.String read();
+ method public void setRttMode(int);
+ method public void write(java.lang.String) throws java.io.IOException;
+ field public static final int RTT_MODE_FULL = 1; // 0x1
+ field public static final int RTT_MODE_HCO = 2; // 0x2
+ field public static final int RTT_MODE_VCO = 3; // 0x3
+ }
+
public final class CallAudioState implements android.os.Parcelable {
ctor public CallAudioState(boolean, int, int);
method public static java.lang.String audioRouteToString(int);
@@ -39856,6 +40131,15 @@
field public static final int STATE_RINGING = 2; // 0x2
}
+ public static final class Connection.RttModifyStatus {
+ ctor public Connection.RttModifyStatus();
+ 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 static abstract class Connection.VideoProvider {
ctor public Connection.VideoProvider();
method public void changeCameraCapabilities(android.telecom.VideoProfile.CameraCapabilities);
@@ -39913,9 +40197,9 @@
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 void onCreateIncomingConnectionFailed(android.telecom.ConnectionRequest);
+ method public void onCreateIncomingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
method public android.telecom.Connection onCreateOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
- method public void onCreateOutgoingConnectionFailed(android.telecom.ConnectionRequest);
+ method public void onCreateOutgoingConnectionFailed(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";
@@ -40152,6 +40436,7 @@
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_RTT = 4096; // 0x1000
field public static final int CAPABILITY_SELF_MANAGED = 2048; // 0x800
field public static final int CAPABILITY_SIM_SUBSCRIPTION = 4; // 0x4
field public static final int CAPABILITY_SUPPORTS_VIDEO_CALLING = 1024; // 0x400
@@ -40391,6 +40676,7 @@
method public boolean handleMmi(java.lang.String);
method public boolean handleMmi(java.lang.String, android.telecom.PhoneAccountHandle);
method public boolean isInCall();
+ method public boolean isInManagedCall();
method public boolean isIncomingCallPermitted(android.telecom.PhoneAccountHandle);
method public boolean isOutgoingCallPermitted(android.telecom.PhoneAccountHandle);
method public boolean isRinging();
@@ -40428,11 +40714,13 @@
field public static final java.lang.String EXTRA_NOTIFICATION_PHONE_NUMBER = "android.telecom.extra.NOTIFICATION_PHONE_NUMBER";
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_RTT = "android.telecom.extra.START_CALL_WITH_RTT";
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 java.lang.String METADATA_INCLUDE_EXTERNAL_CALLS = "android.telecom.INCLUDE_EXTERNAL_CALLS";
+ field public static final java.lang.String METADATA_INCLUDE_SELF_MANAGED_CALLS = "android.telecom.INCLUDE_SELF_MANAGED_CALLS";
field public static final java.lang.String METADATA_IN_CALL_SERVICE_RINGING = "android.telecom.IN_CALL_SERVICE_RINGING";
field public static final java.lang.String METADATA_IN_CALL_SERVICE_UI = "android.telecom.IN_CALL_SERVICE_UI";
field public static final int PRESENTATION_ALLOWED = 1; // 0x1
@@ -40583,6 +40871,7 @@
field public static final java.lang.String KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL = "show_onscreen_dial_button_bool";
field public static final java.lang.String KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL = "simplified_network_settings_bool";
field public static final java.lang.String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool";
+ field public static final java.lang.String KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL = "support_3gpp_call_forwarding_while_roaming_bool";
field public static final java.lang.String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool";
field public static final java.lang.String KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL = "support_pause_ims_video_calls_bool";
field public static final java.lang.String KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL = "support_swap_after_merge_bool";
@@ -40933,7 +41222,9 @@
method public void sendDataMessage(java.lang.String, java.lang.String, short, byte[], android.app.PendingIntent, android.app.PendingIntent);
method public void sendMultimediaMessage(android.content.Context, android.net.Uri, java.lang.String, android.os.Bundle, android.app.PendingIntent);
method public void sendMultipartTextMessage(java.lang.String, java.lang.String, java.util.ArrayList<java.lang.String>, java.util.ArrayList<android.app.PendingIntent>, java.util.ArrayList<android.app.PendingIntent>);
+ method public void sendMultipartTextMessageWithoutPersisting(java.lang.String, java.lang.String, java.util.List<java.lang.String>, java.util.List<android.app.PendingIntent>, java.util.List<android.app.PendingIntent>);
method public void sendTextMessage(java.lang.String, java.lang.String, java.lang.String, android.app.PendingIntent, android.app.PendingIntent);
+ method public void sendTextMessageWithoutPersisting(java.lang.String, java.lang.String, java.lang.String, android.app.PendingIntent, android.app.PendingIntent);
field public static final java.lang.String EXTRA_MMS_DATA = "android.telephony.extra.MMS_DATA";
field public static final java.lang.String EXTRA_MMS_HTTP_STATUS = "android.telephony.extra.MMS_HTTP_STATUS";
field public static final java.lang.String MMS_CONFIG_ALIAS_ENABLED = "aliasEnabled";
@@ -41141,6 +41432,7 @@
method public java.lang.String getDeviceId();
method public java.lang.String getDeviceId(int);
method public java.lang.String getDeviceSoftwareVersion();
+ method public java.lang.String[] getForbiddenPlmns();
method public java.lang.String getGroupIdLevel1();
method public java.lang.String getIccAuthentication(int, int, java.lang.String);
method public java.lang.String getImei();
@@ -42001,7 +42293,7 @@
package android.test.suitebuilder {
- public class TestMethod {
+ public deprecated class TestMethod {
ctor public TestMethod(java.lang.reflect.Method, java.lang.Class<? extends junit.framework.TestCase>);
ctor public TestMethod(java.lang.String, java.lang.Class<? extends junit.framework.TestCase>);
ctor public TestMethod(junit.framework.TestCase);
@@ -42012,7 +42304,7 @@
method public java.lang.String getName();
}
- public class TestSuiteBuilder {
+ public deprecated class TestSuiteBuilder {
ctor public TestSuiteBuilder(java.lang.Class);
ctor public TestSuiteBuilder(java.lang.String, java.lang.ClassLoader);
method public android.test.suitebuilder.TestSuiteBuilder addRequirements(java.util.List<com.android.internal.util.Predicate<android.test.suitebuilder.TestMethod>>);
@@ -42025,7 +42317,7 @@
method public android.test.suitebuilder.TestSuiteBuilder named(java.lang.String);
}
- public static class TestSuiteBuilder.FailedToCreateTests extends junit.framework.TestCase {
+ public static deprecated class TestSuiteBuilder.FailedToCreateTests extends junit.framework.TestCase {
ctor public TestSuiteBuilder.FailedToCreateTests(java.lang.Exception);
method public void testSuiteConstructionFailed();
}
@@ -52752,7 +53044,7 @@
package com.android.internal.util {
- public abstract interface Predicate<T> {
+ public abstract deprecated interface Predicate<T> {
method public abstract boolean apply(T);
}
@@ -52890,6 +53182,8 @@
field public static final int OP_INT_TO_FLOAT = 130; // 0x82
field public static final int OP_INT_TO_LONG = 129; // 0x81
field public static final int OP_INT_TO_SHORT = 143; // 0x8f
+ field public static final int OP_INVOKE_CUSTOM = 252; // 0xfc
+ field public static final int OP_INVOKE_CUSTOM_RANGE = 253; // 0xfd
field public static final int OP_INVOKE_DIRECT = 112; // 0x70
field public static final deprecated int OP_INVOKE_DIRECT_EMPTY = 240; // 0xf0
field public static final int OP_INVOKE_DIRECT_JUMBO = 9471; // 0x24ff
@@ -53080,7 +53374,7 @@
method public static dalvik.system.DexFile loadDex(java.lang.String, java.lang.String, int) throws java.io.IOException;
}
- public final class InMemoryDexClassLoader extends java.lang.ClassLoader {
+ public final class InMemoryDexClassLoader extends dalvik.system.BaseDexClassLoader {
ctor public InMemoryDexClassLoader(java.nio.ByteBuffer, java.lang.ClassLoader);
}
@@ -56291,7 +56585,6 @@
method public java.lang.Class<?> lookupClass();
method public int lookupModes();
method public java.lang.invoke.MethodHandleInfo revealDirect(java.lang.invoke.MethodHandle);
- method public void throwMakeAccessException(java.lang.String, java.lang.Object) throws java.lang.IllegalAccessException;
method public java.lang.invoke.MethodHandle unreflect(java.lang.reflect.Method) throws java.lang.IllegalAccessException;
method public java.lang.invoke.MethodHandle unreflectConstructor(java.lang.reflect.Constructor<?>) throws java.lang.IllegalAccessException;
method public java.lang.invoke.MethodHandle unreflectGetter(java.lang.reflect.Field) throws java.lang.IllegalAccessException;
diff --git a/api/test-current.txt b/api/test-current.txt
index 1763b46..43072bd 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -6704,6 +6704,7 @@
method public java.util.Set<android.bluetooth.BluetoothDevice> getBondedDevices();
method public static synchronized android.bluetooth.BluetoothAdapter getDefaultAdapter();
method public java.lang.String getName();
+ method public android.bluetooth.le.PeriodicAdvertisingManager getPeriodicAdvertisingManager();
method public int getProfileConnectionState(int);
method public boolean getProfileProxy(android.content.Context, android.bluetooth.BluetoothProfile.ServiceListener, int);
method public android.bluetooth.BluetoothDevice getRemoteDevice(java.lang.String);
@@ -6712,6 +6713,10 @@
method public int getState();
method public boolean isDiscovering();
method public boolean isEnabled();
+ method public boolean isLe2MPhySupported();
+ method public boolean isLeCodedPhySupported();
+ method public boolean isLeExtendedAdvertisingSupported();
+ method public boolean isLePeriodicAdvertisingSupported();
method public boolean isMultipleAdvertisementSupported();
method public boolean isOffloadedFilteringSupported();
method public boolean isOffloadedScanBatchingSupported();
@@ -7080,6 +7085,9 @@
public final class BluetoothDevice implements android.os.Parcelable {
method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback);
method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback, int);
+ method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallbackExt);
+ method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallbackExt, int);
+ method public android.bluetooth.BluetoothGatt connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallbackExt, int, int);
method public boolean createBond();
method public android.bluetooth.BluetoothSocket createInsecureRfcommSocketToServiceRecord(java.util.UUID) throws java.io.IOException;
method public android.bluetooth.BluetoothSocket createRfcommSocketToServiceRecord(java.util.UUID) throws java.io.IOException;
@@ -7123,6 +7131,13 @@
field public static final java.lang.String EXTRA_UUID = "android.bluetooth.device.extra.UUID";
field public static final int PAIRING_VARIANT_PASSKEY_CONFIRMATION = 2; // 0x2
field public static final int PAIRING_VARIANT_PIN = 0; // 0x0
+ field public static final int PHY_LE_1M = 1; // 0x1
+ field public static final int PHY_LE_2M = 2; // 0x2
+ field public static final int PHY_LE_ANY = 7; // 0x7
+ field public static final int PHY_LE_CODED = 4; // 0x4
+ field public static final int PHY_OPTION_NO_PREFERRED = 0; // 0x0
+ field public static final int PHY_OPTION_S2 = 1; // 0x1
+ field public static final int PHY_OPTION_S8 = 2; // 0x2
field public static final int TRANSPORT_AUTO = 0; // 0x0
field public static final int TRANSPORT_BREDR = 1; // 0x1
field public static final int TRANSPORT_LE = 2; // 0x2
@@ -7145,10 +7160,12 @@
method public java.util.List<android.bluetooth.BluetoothGattService> getServices();
method public boolean readCharacteristic(android.bluetooth.BluetoothGattCharacteristic);
method public boolean readDescriptor(android.bluetooth.BluetoothGattDescriptor);
+ method public void readPhy();
method public boolean readRemoteRssi();
method public boolean requestConnectionPriority(int);
method public boolean requestMtu(int);
method public boolean setCharacteristicNotification(android.bluetooth.BluetoothGattCharacteristic, boolean);
+ method public void setPreferredPhy(int, int, int);
method public boolean writeCharacteristic(android.bluetooth.BluetoothGattCharacteristic);
method public boolean writeDescriptor(android.bluetooth.BluetoothGattDescriptor);
field public static final int CONNECTION_PRIORITY_BALANCED = 0; // 0x0
@@ -7166,8 +7183,12 @@
field public static final int GATT_WRITE_NOT_PERMITTED = 3; // 0x3
}
- public abstract class BluetoothGattCallback {
+ public abstract deprecated class BluetoothGattCallback extends android.bluetooth.BluetoothGattCallbackExt {
ctor public BluetoothGattCallback();
+ }
+
+ public abstract class BluetoothGattCallbackExt {
+ ctor public BluetoothGattCallbackExt();
method public void onCharacteristicChanged(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic);
method public void onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int);
method public void onCharacteristicWrite(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int);
@@ -7175,6 +7196,8 @@
method public void onDescriptorRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattDescriptor, int);
method public void onDescriptorWrite(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattDescriptor, int);
method public void onMtuChanged(android.bluetooth.BluetoothGatt, int, int);
+ method public void onPhyRead(android.bluetooth.BluetoothGatt, int, int, int);
+ method public void onPhyUpdate(android.bluetooth.BluetoothGatt, int, int, int);
method public void onReadRemoteRssi(android.bluetooth.BluetoothGatt, int, int);
method public void onReliableWriteCompleted(android.bluetooth.BluetoothGatt, int);
method public void onServicesDiscovered(android.bluetooth.BluetoothGatt, int);
@@ -7268,12 +7291,18 @@
method public android.bluetooth.BluetoothGattService getService(java.util.UUID);
method public java.util.List<android.bluetooth.BluetoothGattService> getServices();
method public boolean notifyCharacteristicChanged(android.bluetooth.BluetoothDevice, android.bluetooth.BluetoothGattCharacteristic, boolean);
+ method public void readPhy(android.bluetooth.BluetoothDevice);
method public boolean removeService(android.bluetooth.BluetoothGattService);
method public boolean sendResponse(android.bluetooth.BluetoothDevice, int, int, int, byte[]);
+ method public void setPreferredPhy(android.bluetooth.BluetoothDevice, int, int, int);
}
- public abstract class BluetoothGattServerCallback {
+ public abstract deprecated class BluetoothGattServerCallback extends android.bluetooth.BluetoothGattServerCallbackExt {
ctor public BluetoothGattServerCallback();
+ }
+
+ public abstract class BluetoothGattServerCallbackExt {
+ ctor public BluetoothGattServerCallbackExt();
method public void onCharacteristicReadRequest(android.bluetooth.BluetoothDevice, int, int, android.bluetooth.BluetoothGattCharacteristic);
method public void onCharacteristicWriteRequest(android.bluetooth.BluetoothDevice, int, android.bluetooth.BluetoothGattCharacteristic, boolean, boolean, int, byte[]);
method public void onConnectionStateChange(android.bluetooth.BluetoothDevice, int, int);
@@ -7282,6 +7311,8 @@
method public void onExecuteWrite(android.bluetooth.BluetoothDevice, int, boolean);
method public void onMtuChanged(android.bluetooth.BluetoothDevice, int);
method public void onNotificationSent(android.bluetooth.BluetoothDevice, int);
+ method public void onPhyRead(android.bluetooth.BluetoothDevice, int, int, int);
+ method public void onPhyUpdate(android.bluetooth.BluetoothDevice, int, int, int);
method public void onServiceAdded(int, android.bluetooth.BluetoothGattService);
}
@@ -7482,10 +7513,85 @@
method public android.bluetooth.le.AdvertiseSettings.Builder setTxPowerLevel(int);
}
+ public final class AdvertisingSet {
+ method public void enableAdvertising(boolean);
+ method public void periodicAdvertisingEnable(boolean);
+ method public void setAdvertisingData(android.bluetooth.le.AdvertiseData);
+ method public void setAdvertisingParameters(android.bluetooth.le.AdvertisingSetParameters);
+ method public void setPeriodicAdvertisingData(android.bluetooth.le.AdvertiseData);
+ method public void setPeriodicAdvertisingParameters(android.bluetooth.le.PeriodicAdvertisingParameters);
+ method public void setScanResponseData(android.bluetooth.le.AdvertiseData);
+ }
+
+ public abstract class AdvertisingSetCallback {
+ ctor public AdvertisingSetCallback();
+ method public void onAdvertisingDataSet(android.bluetooth.le.AdvertisingSet, int);
+ method public void onAdvertisingEnabled(android.bluetooth.le.AdvertisingSet, boolean, int);
+ method public void onAdvertisingParametersUpdated(android.bluetooth.le.AdvertisingSet, int);
+ method public void onAdvertisingSetStarted(android.bluetooth.le.AdvertisingSet, int);
+ method public void onAdvertisingSetStopped(android.bluetooth.le.AdvertisingSet);
+ method public void onPeriodicAdvertisingDataSet(android.bluetooth.le.AdvertisingSet, int);
+ method public void onPeriodicAdvertisingEnable(android.bluetooth.le.AdvertisingSet, boolean, int);
+ method public void onPeriodicAdvertisingParametersUpdated(android.bluetooth.le.AdvertisingSet, int);
+ method public void onScanResponseDataSet(android.bluetooth.le.AdvertisingSet, int);
+ field public static final int ADVERTISE_FAILED_ALREADY_STARTED = 3; // 0x3
+ field public static final int ADVERTISE_FAILED_DATA_TOO_LARGE = 1; // 0x1
+ field public static final int ADVERTISE_FAILED_FEATURE_UNSUPPORTED = 5; // 0x5
+ field public static final int ADVERTISE_FAILED_INTERNAL_ERROR = 4; // 0x4
+ field public static final int ADVERTISE_FAILED_TOO_MANY_ADVERTISERS = 2; // 0x2
+ field public static final int ADVERTISE_SUCCESS = 0; // 0x0
+ }
+
+ public final class AdvertisingSetParameters implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getInterval();
+ method public int getPrimaryPhy();
+ method public int getSecondaryPhy();
+ method public int getTimeout();
+ method public int getTxPowerLevel();
+ method public boolean includeTxPower();
+ method public boolean isAnonymous();
+ method public boolean isConnectable();
+ method public boolean isLegacy();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.bluetooth.le.AdvertisingSetParameters> CREATOR;
+ field public static final int INTERVAL_HIGH = 160; // 0xa0
+ field public static final int INTERVAL_LOW = 1600; // 0x640
+ field public static final int INTERVAL_MAX = 16777215; // 0xffffff
+ field public static final int INTERVAL_MEDIUM = 400; // 0x190
+ field public static final int INTERVAL_MIN = 160; // 0xa0
+ field public static final int PHY_LE_1M = 1; // 0x1
+ field public static final int PHY_LE_2M = 2; // 0x2
+ field public static final int PHY_LE_CODED = 3; // 0x3
+ field public static final int TX_POWER_HIGH = 1; // 0x1
+ field public static final int TX_POWER_LOW = -15; // 0xfffffff1
+ field public static final int TX_POWER_MAX = 1; // 0x1
+ field public static final int TX_POWER_MEDIUM = -7; // 0xfffffff9
+ field public static final int TX_POWER_MIN = -127; // 0xffffff81
+ field public static final int TX_POWER_ULTRA_LOW = -21; // 0xffffffeb
+ }
+
+ public static final class AdvertisingSetParameters.Builder {
+ ctor public AdvertisingSetParameters.Builder();
+ method public android.bluetooth.le.AdvertisingSetParameters build();
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setAnonymouus(boolean);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setConnectable(boolean);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setIncludeTxPower(boolean);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setInterval(int);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setLegacyMode(boolean);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setPrimaryPhy(int);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setSecondaryPhy(int);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setTimeout(int);
+ method public android.bluetooth.le.AdvertisingSetParameters.Builder setTxPowerLevel(int);
+ }
+
public final class BluetoothLeAdvertiser {
method public void startAdvertising(android.bluetooth.le.AdvertiseSettings, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseCallback);
method public void startAdvertising(android.bluetooth.le.AdvertiseSettings, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseCallback);
+ method public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertisingSetCallback);
+ method public void startAdvertisingSet(android.bluetooth.le.AdvertisingSetParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertiseData, android.bluetooth.le.PeriodicAdvertisingParameters, android.bluetooth.le.AdvertiseData, android.bluetooth.le.AdvertisingSetCallback, android.os.Handler);
method public void stopAdvertising(android.bluetooth.le.AdvertiseCallback);
+ method public void stopAdvertisingSet(android.bluetooth.le.AdvertisingSetCallback);
}
public final class BluetoothLeScanner {
@@ -7495,6 +7601,53 @@
method public void stopScan(android.bluetooth.le.ScanCallback);
}
+ public abstract class PeriodicAdvertisingCallback {
+ ctor public PeriodicAdvertisingCallback();
+ method public void onPeriodicAdvertisingReport(android.bluetooth.le.PeriodicAdvertisingReport);
+ method public void onSyncEstablished(int, android.bluetooth.BluetoothDevice, int, int, int, int);
+ method public void onSyncLost(int);
+ field public static final int SYNC_NO_RESOURCES = 2; // 0x2
+ field public static final int SYNC_NO_RESPONSE = 1; // 0x1
+ }
+
+ public final class PeriodicAdvertisingManager {
+ method public void registerSync(android.bluetooth.le.ScanResult, int, int, android.bluetooth.le.PeriodicAdvertisingCallback);
+ method public void registerSync(android.bluetooth.le.ScanResult, int, int, android.bluetooth.le.PeriodicAdvertisingCallback, android.os.Handler);
+ method public void unregisterSync(android.bluetooth.le.PeriodicAdvertisingCallback);
+ }
+
+ public final class PeriodicAdvertisingParameters implements android.os.Parcelable {
+ method public int describeContents();
+ method public boolean getEnable();
+ method public boolean getIncludeTxPower();
+ method public int getInterval();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.bluetooth.le.PeriodicAdvertisingParameters> CREATOR;
+ }
+
+ public static final class PeriodicAdvertisingParameters.Builder {
+ ctor public PeriodicAdvertisingParameters.Builder();
+ method public android.bluetooth.le.PeriodicAdvertisingParameters build();
+ method public android.bluetooth.le.PeriodicAdvertisingParameters.Builder setEnable(boolean);
+ method public android.bluetooth.le.PeriodicAdvertisingParameters.Builder setIncludeTxPower(boolean);
+ method public android.bluetooth.le.PeriodicAdvertisingParameters.Builder setInterval(int);
+ }
+
+ public final class PeriodicAdvertisingReport implements android.os.Parcelable {
+ ctor public PeriodicAdvertisingReport(int, int, int, int, android.bluetooth.le.ScanRecord);
+ method public int describeContents();
+ method public android.bluetooth.le.ScanRecord getData();
+ method public int getDataStatus();
+ method public int getRssi();
+ method public int getSyncHandle();
+ method public long getTimestampNanos();
+ method public int getTxPower();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.bluetooth.le.PeriodicAdvertisingReport> CREATOR;
+ field public static final int DATA_COMPLETE = 0; // 0x0
+ field public static final int DATA_INCOMPLETE_TRUNCATED = 2; // 0x2
+ }
+
public abstract class ScanCallback {
ctor public ScanCallback();
method public void onBatchScanResults(java.util.List<android.bluetooth.le.ScanResult>);
@@ -7549,19 +7702,37 @@
}
public final class ScanResult implements android.os.Parcelable {
- ctor public ScanResult(android.bluetooth.BluetoothDevice, android.bluetooth.le.ScanRecord, int, long);
+ ctor public deprecated ScanResult(android.bluetooth.BluetoothDevice, android.bluetooth.le.ScanRecord, int, long);
+ ctor public ScanResult(android.bluetooth.BluetoothDevice, int, int, int, int, int, int, int, android.bluetooth.le.ScanRecord, long);
method public int describeContents();
+ method public int getAdvertisingSid();
+ method public int getDataStatus();
method public android.bluetooth.BluetoothDevice getDevice();
+ method public int getPeriodicAdvertisingInterval();
+ method public int getPrimaryPhy();
method public int getRssi();
method public android.bluetooth.le.ScanRecord getScanRecord();
+ method public int getSecondaryPhy();
method public long getTimestampNanos();
+ method public int getTxPower();
+ method public boolean isConnectable();
+ method public boolean isLegacy();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.bluetooth.le.ScanResult> CREATOR;
+ field public static final int DATA_COMPLETE = 0; // 0x0
+ field public static final int DATA_TRUNCATED = 2; // 0x2
+ field public static final int PHY_LE_1M = 1; // 0x1
+ field public static final int PHY_LE_2M = 2; // 0x2
+ field public static final int PHY_LE_CODED = 3; // 0x3
+ field public static final int PHY_UNUSED = 0; // 0x0
+ field public static final int SID_NOT_PRESENT = 255; // 0xff
}
public final class ScanSettings implements android.os.Parcelable {
method public int describeContents();
method public int getCallbackType();
+ method public boolean getLegacy();
+ method public int getPhy();
method public long getReportDelayMillis();
method public int getScanMode();
method public int getScanResultType();
@@ -7575,6 +7746,9 @@
field public static final int MATCH_NUM_FEW_ADVERTISEMENT = 2; // 0x2
field public static final int MATCH_NUM_MAX_ADVERTISEMENT = 3; // 0x3
field public static final int MATCH_NUM_ONE_ADVERTISEMENT = 1; // 0x1
+ field public static final int PHY_LE_1M = 1; // 0x1
+ field public static final int PHY_LE_ALL_SUPPORTED = 255; // 0xff
+ field public static final int PHY_LE_CODED = 3; // 0x3
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
@@ -7585,8 +7759,10 @@
ctor public ScanSettings.Builder();
method public android.bluetooth.le.ScanSettings build();
method public android.bluetooth.le.ScanSettings.Builder setCallbackType(int);
+ method public android.bluetooth.le.ScanSettings.Builder setLegacy(boolean);
method public android.bluetooth.le.ScanSettings.Builder setMatchMode(int);
method public android.bluetooth.le.ScanSettings.Builder setNumOfMatches(int);
+ method public android.bluetooth.le.ScanSettings.Builder setPhy(int);
method public android.bluetooth.le.ScanSettings.Builder setReportDelay(long);
method public android.bluetooth.le.ScanSettings.Builder setScanMode(int);
}
@@ -8199,6 +8375,7 @@
field public static final java.lang.String HARDWARE_PROPERTIES_SERVICE = "hardware_properties";
field public static final java.lang.String INPUT_METHOD_SERVICE = "input_method";
field public static final java.lang.String INPUT_SERVICE = "input";
+ field public static final java.lang.String IPSEC_SERVICE = "ipsec";
field public static final java.lang.String JOB_SCHEDULER_SERVICE = "jobscheduler";
field public static final java.lang.String KEYGUARD_SERVICE = "keyguard";
field public static final java.lang.String LAUNCHER_APPS_SERVICE = "launcherapps";
@@ -8565,6 +8742,7 @@
field public static final java.lang.String ACTION_CALL = "android.intent.action.CALL";
field public static final java.lang.String ACTION_CALL_BUTTON = "android.intent.action.CALL_BUTTON";
field public static final java.lang.String ACTION_CAMERA_BUTTON = "android.intent.action.CAMERA_BUTTON";
+ field public static final java.lang.String ACTION_CARRIER_SETUP = "android.intent.action.CARRIER_SETUP";
field public static final java.lang.String ACTION_CHOOSER = "android.intent.action.CHOOSER";
field public static final java.lang.String ACTION_CLOSE_SYSTEM_DIALOGS = "android.intent.action.CLOSE_SYSTEM_DIALOGS";
field public static final java.lang.String ACTION_CONFIGURATION_CHANGED = "android.intent.action.CONFIGURATION_CHANGED";
@@ -18740,6 +18918,8 @@
field public static final int SHORT_COMMONLY_USED = 6; // 0x6
field public static final int SHORT_GENERIC = 2; // 0x2
field public static final int SHORT_GMT = 4; // 0x4
+ field public static final int TIMEZONE_ICU = 0; // 0x0
+ field public static final int TIMEZONE_JDK = 1; // 0x1
field public static final android.icu.util.TimeZone UNKNOWN_ZONE;
field public static final java.lang.String UNKNOWN_ZONE_ID = "Etc/Unknown";
}
@@ -23705,7 +23885,9 @@
method public boolean isDefaultNetworkActive();
method public static deprecated boolean isNetworkTypeValid(int);
method public void registerDefaultNetworkCallback(android.net.ConnectivityManager.NetworkCallback);
+ method public void registerDefaultNetworkCallback(android.net.ConnectivityManager.NetworkCallback, android.os.Handler);
method public void registerNetworkCallback(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
+ method public void registerNetworkCallback(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback, android.os.Handler);
method public void registerNetworkCallback(android.net.NetworkRequest, android.app.PendingIntent);
method public void releaseNetworkRequest(android.app.PendingIntent);
method public void removeDefaultNetworkActiveListener(android.net.ConnectivityManager.OnNetworkActiveListener);
@@ -23713,6 +23895,9 @@
method public void reportNetworkConnectivity(android.net.Network, boolean);
method public boolean requestBandwidthUpdate(android.net.Network);
method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback);
+ method public void requestNetwork(android.net.NetworkRequest, int, android.net.ConnectivityManager.NetworkCallback);
+ method public void requestNetwork(android.net.NetworkRequest, android.net.ConnectivityManager.NetworkCallback, android.os.Handler);
+ method public void requestNetwork(android.net.NetworkRequest, int, android.net.ConnectivityManager.NetworkCallback, android.os.Handler);
method public void requestNetwork(android.net.NetworkRequest, android.app.PendingIntent);
method public deprecated boolean requestRouteToHost(int, int);
method public deprecated void setNetworkPreference(int);
@@ -23760,6 +23945,7 @@
method public void onLinkPropertiesChanged(android.net.Network, android.net.LinkProperties);
method public void onLosing(android.net.Network, int);
method public void onLost(android.net.Network);
+ method public void onUnavailable();
}
public static abstract interface ConnectivityManager.OnNetworkActiveListener {
@@ -23796,6 +23982,68 @@
field public static final android.os.Parcelable.Creator<android.net.IpPrefix> CREATOR;
}
+ public final class IpSecAlgorithm implements android.os.Parcelable {
+ ctor public IpSecAlgorithm(java.lang.String, byte[]);
+ ctor public IpSecAlgorithm(java.lang.String, byte[], int);
+ method public int describeContents();
+ method public byte[] getKey();
+ method public java.lang.String getName();
+ method public int getTruncationLengthBits();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final java.lang.String ALGO_AUTH_HMAC_MD5 = "hmac(md5)";
+ field public static final java.lang.String ALGO_AUTH_HMAC_SHA1 = "hmac(sha1)";
+ field public static final java.lang.String ALGO_AUTH_HMAC_SHA256 = "hmac(sha256)";
+ field public static final java.lang.String ALGO_AUTH_HMAC_SHA384 = "hmac(sha384)";
+ field public static final java.lang.String ALGO_AUTH_HMAC_SHA512 = "hmac(sha512)";
+ field public static final java.lang.String ALGO_CRYPT_AES_CBC = "cbc(aes)";
+ field public static final android.os.Parcelable.Creator<android.net.IpSecAlgorithm> CREATOR;
+ }
+
+ public final class IpSecManager {
+ method public void applyTransportModeTransform(java.net.Socket, android.net.IpSecTransform) throws java.io.IOException;
+ method public void applyTransportModeTransform(java.net.DatagramSocket, android.net.IpSecTransform) throws java.io.IOException;
+ method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket(int) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
+ method public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket() throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
+ method public void removeTransportModeTransform(java.net.Socket, android.net.IpSecTransform);
+ method public void removeTransportModeTransform(java.net.DatagramSocket, android.net.IpSecTransform);
+ method public android.net.IpSecManager.SecurityParameterIndex reserveSecurityParameterIndex(java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
+ field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0
+ }
+
+ public static final class IpSecManager.ResourceUnavailableException extends android.util.AndroidException {
+ }
+
+ public static final class IpSecManager.SecurityParameterIndex implements java.lang.AutoCloseable {
+ method public void close();
+ method public int getSpi();
+ }
+
+ public static final class IpSecManager.SpiUnavailableException extends android.util.AndroidException {
+ method public int getSpi();
+ }
+
+ public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable {
+ method public void close();
+ method public int getPort();
+ method public java.io.FileDescriptor getSocket();
+ }
+
+ public final class IpSecTransform implements java.lang.AutoCloseable {
+ method public void close();
+ field public static final int DIRECTION_IN = 0; // 0x0
+ field public static final int DIRECTION_OUT = 1; // 0x1
+ }
+
+ public static class IpSecTransform.Builder {
+ ctor public IpSecTransform.Builder(android.content.Context);
+ method public android.net.IpSecTransform buildTransportModeTransform(java.net.InetAddress) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
+ method public android.net.IpSecTransform.Builder setAuthentication(int, android.net.IpSecAlgorithm);
+ method public android.net.IpSecTransform.Builder setEncryption(int, android.net.IpSecAlgorithm);
+ method public android.net.IpSecTransform.Builder setIpv4Encapsulation(android.net.IpSecManager.UdpEncapsulationSocket, int);
+ method public android.net.IpSecTransform.Builder setSpi(int, int);
+ method public android.net.IpSecTransform.Builder setSpi(int, android.net.IpSecManager.SecurityParameterIndex);
+ }
+
public class LinkAddress implements android.os.Parcelable {
method public int describeContents();
method public java.net.InetAddress getAddress();
@@ -24986,10 +25234,9 @@
}
public class DiscoverySession {
- method public java.lang.String createNetworkSpecifier(android.net.wifi.aware.PeerHandle, byte[]);
+ method public java.lang.String createNetworkSpecifierOpen(android.net.wifi.aware.PeerHandle);
+ method public java.lang.String createNetworkSpecifierPassphrase(android.net.wifi.aware.PeerHandle, java.lang.String);
method public void destroy();
- method public static int getMaxSendRetryCount();
- method public void sendMessage(android.net.wifi.aware.PeerHandle, int, byte[], int);
method public void sendMessage(android.net.wifi.aware.PeerHandle, int, byte[]);
}
@@ -25076,7 +25323,8 @@
}
public class WifiAwareSession {
- method public java.lang.String createNetworkSpecifier(int, byte[], byte[]);
+ method public java.lang.String createNetworkSpecifierOpen(int, byte[]);
+ method public java.lang.String createNetworkSpecifierPassphrase(int, byte[], java.lang.String);
method public void destroy();
method public void publish(android.net.wifi.aware.PublishConfig, android.net.wifi.aware.DiscoverySessionCallback, android.os.Handler);
method public void subscribe(android.net.wifi.aware.SubscribeConfig, android.net.wifi.aware.DiscoverySessionCallback, android.os.Handler);
@@ -36663,9 +36911,11 @@
method public android.telecom.Call.Details getDetails();
method public android.telecom.Call getParent();
method public java.lang.String getRemainingPostDialSequence();
+ method public android.telecom.Call.RttCall getRttCall();
method public int getState();
method public android.telecom.InCallService.VideoCall getVideoCall();
method public void hold();
+ method public boolean isRttActive();
method public void mergeConference();
method public void phoneAccountSelected(android.telecom.PhoneAccountHandle, boolean);
method public void playDtmfTone(char);
@@ -36677,9 +36927,12 @@
method public void reject(boolean, java.lang.String);
method public final void removeExtras(java.util.List<java.lang.String>);
method public final void removeExtras(java.lang.String...);
+ method public void respondToRttRequest(int, boolean);
method public void sendCallEvent(java.lang.String, android.os.Bundle);
+ method public void sendRttRequest();
method public void splitFromConference();
method public void stopDtmfTone();
+ method public void stopRtt();
method public void swapConference();
method public void unhold();
method public void unregisterCallback(android.telecom.Call.Callback);
@@ -36706,6 +36959,10 @@
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 onRttInitiationFailure(android.telecom.Call, int);
+ method public void onRttModeChanged(android.telecom.Call, int);
+ method public void onRttRequest(android.telecom.Call, int);
+ method public void onRttStatusChanged(android.telecom.Call, boolean, android.telecom.Call.RttCall);
method public void onStateChanged(android.telecom.Call, int);
method public void onVideoCallChanged(android.telecom.Call, android.telecom.InCallService.VideoCall);
}
@@ -36756,9 +37013,20 @@
field public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 128; // 0x80
field public static final int PROPERTY_HIGH_DEF_AUDIO = 16; // 0x10
field public static final int PROPERTY_IS_EXTERNAL_CALL = 64; // 0x40
+ field public static final int PROPERTY_SELF_MANAGED = 256; // 0x100
field public static final int PROPERTY_WIFI = 8; // 0x8
}
+ public static final class Call.RttCall {
+ method public int getRttAudioMode();
+ method public java.lang.String read();
+ method public void setRttMode(int);
+ method public void write(java.lang.String) throws java.io.IOException;
+ field public static final int RTT_MODE_FULL = 1; // 0x1
+ field public static final int RTT_MODE_HCO = 2; // 0x2
+ field public static final int RTT_MODE_VCO = 3; // 0x3
+ }
+
public final class CallAudioState implements android.os.Parcelable {
ctor public CallAudioState(boolean, int, int);
method public static java.lang.String audioRouteToString(int);
@@ -36958,6 +37226,15 @@
field public static final int STATE_RINGING = 2; // 0x2
}
+ public static final class Connection.RttModifyStatus {
+ ctor public Connection.RttModifyStatus();
+ 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 static abstract class Connection.VideoProvider {
ctor public Connection.VideoProvider();
method public void changeCameraCapabilities(android.telecom.VideoProfile.CameraCapabilities);
@@ -37015,9 +37292,9 @@
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 void onCreateIncomingConnectionFailed(android.telecom.ConnectionRequest);
+ method public void onCreateIncomingConnectionFailed(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
method public android.telecom.Connection onCreateOutgoingConnection(android.telecom.PhoneAccountHandle, android.telecom.ConnectionRequest);
- method public void onCreateOutgoingConnectionFailed(android.telecom.ConnectionRequest);
+ method public void onCreateOutgoingConnectionFailed(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";
@@ -37130,6 +37407,7 @@
field public static final int CAPABILITY_CALL_SUBJECT = 64; // 0x40
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_RTT = 4096; // 0x1000
field public static final int CAPABILITY_SELF_MANAGED = 2048; // 0x800
field public static final int CAPABILITY_SIM_SUBSCRIPTION = 4; // 0x4
field public static final int CAPABILITY_SUPPORTS_VIDEO_CALLING = 1024; // 0x400
@@ -37312,6 +37590,7 @@
method public boolean handleMmi(java.lang.String);
method public boolean handleMmi(java.lang.String, android.telecom.PhoneAccountHandle);
method public boolean isInCall();
+ method public boolean isInManagedCall();
method public boolean isIncomingCallPermitted(android.telecom.PhoneAccountHandle);
method public boolean isOutgoingCallPermitted(android.telecom.PhoneAccountHandle);
method public boolean isVoiceMailNumber(android.telecom.PhoneAccountHandle, java.lang.String);
@@ -37342,11 +37621,13 @@
field public static final java.lang.String EXTRA_NOTIFICATION_PHONE_NUMBER = "android.telecom.extra.NOTIFICATION_PHONE_NUMBER";
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_RTT = "android.telecom.extra.START_CALL_WITH_RTT";
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 java.lang.String METADATA_INCLUDE_EXTERNAL_CALLS = "android.telecom.INCLUDE_EXTERNAL_CALLS";
+ field public static final java.lang.String METADATA_INCLUDE_SELF_MANAGED_CALLS = "android.telecom.INCLUDE_SELF_MANAGED_CALLS";
field public static final java.lang.String METADATA_IN_CALL_SERVICE_RINGING = "android.telecom.IN_CALL_SERVICE_RINGING";
field public static final java.lang.String METADATA_IN_CALL_SERVICE_UI = "android.telecom.IN_CALL_SERVICE_UI";
field public static final int PRESENTATION_ALLOWED = 1; // 0x1
@@ -37495,6 +37776,7 @@
field public static final java.lang.String KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL = "show_onscreen_dial_button_bool";
field public static final java.lang.String KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL = "simplified_network_settings_bool";
field public static final java.lang.String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool";
+ field public static final java.lang.String KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL = "support_3gpp_call_forwarding_while_roaming_bool";
field public static final java.lang.String KEY_SUPPORT_CONFERENCE_CALL_BOOL = "support_conference_call_bool";
field public static final java.lang.String KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL = "support_pause_ims_video_calls_bool";
field public static final java.lang.String KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL = "support_swap_after_merge_bool";
@@ -38014,6 +38296,7 @@
method public java.lang.String getDeviceId();
method public java.lang.String getDeviceId(int);
method public java.lang.String getDeviceSoftwareVersion();
+ method public java.lang.String[] getForbiddenPlmns();
method public java.lang.String getGroupIdLevel1();
method public java.lang.String getIccAuthentication(int, int, java.lang.String);
method public java.lang.String getLine1Number();
@@ -38821,7 +39104,7 @@
package android.test.suitebuilder {
- public class TestMethod {
+ public deprecated class TestMethod {
ctor public TestMethod(java.lang.reflect.Method, java.lang.Class<? extends junit.framework.TestCase>);
ctor public TestMethod(java.lang.String, java.lang.Class<? extends junit.framework.TestCase>);
ctor public TestMethod(junit.framework.TestCase);
@@ -38832,7 +39115,7 @@
method public java.lang.String getName();
}
- public class TestSuiteBuilder {
+ public deprecated class TestSuiteBuilder {
ctor public TestSuiteBuilder(java.lang.Class);
ctor public TestSuiteBuilder(java.lang.String, java.lang.ClassLoader);
method public android.test.suitebuilder.TestSuiteBuilder addRequirements(java.util.List<com.android.internal.util.Predicate<android.test.suitebuilder.TestMethod>>);
@@ -38845,7 +39128,7 @@
method public android.test.suitebuilder.TestSuiteBuilder named(java.lang.String);
}
- public static class TestSuiteBuilder.FailedToCreateTests extends junit.framework.TestCase {
+ public static deprecated class TestSuiteBuilder.FailedToCreateTests extends junit.framework.TestCase {
ctor public TestSuiteBuilder.FailedToCreateTests(java.lang.Exception);
method public void testSuiteConstructionFailed();
}
@@ -49222,7 +49505,7 @@
package com.android.internal.util {
- public abstract interface Predicate<T> {
+ public abstract deprecated interface Predicate<T> {
method public abstract boolean apply(T);
}
@@ -49360,6 +49643,8 @@
field public static final int OP_INT_TO_FLOAT = 130; // 0x82
field public static final int OP_INT_TO_LONG = 129; // 0x81
field public static final int OP_INT_TO_SHORT = 143; // 0x8f
+ field public static final int OP_INVOKE_CUSTOM = 252; // 0xfc
+ field public static final int OP_INVOKE_CUSTOM_RANGE = 253; // 0xfd
field public static final int OP_INVOKE_DIRECT = 112; // 0x70
field public static final deprecated int OP_INVOKE_DIRECT_EMPTY = 240; // 0xf0
field public static final int OP_INVOKE_DIRECT_JUMBO = 9471; // 0x24ff
@@ -49550,7 +49835,7 @@
method public static dalvik.system.DexFile loadDex(java.lang.String, java.lang.String, int) throws java.io.IOException;
}
- public final class InMemoryDexClassLoader extends java.lang.ClassLoader {
+ public final class InMemoryDexClassLoader extends dalvik.system.BaseDexClassLoader {
ctor public InMemoryDexClassLoader(java.nio.ByteBuffer, java.lang.ClassLoader);
}
@@ -52761,7 +53046,6 @@
method public java.lang.Class<?> lookupClass();
method public int lookupModes();
method public java.lang.invoke.MethodHandleInfo revealDirect(java.lang.invoke.MethodHandle);
- method public void throwMakeAccessException(java.lang.String, java.lang.Object) throws java.lang.IllegalAccessException;
method public java.lang.invoke.MethodHandle unreflect(java.lang.reflect.Method) throws java.lang.IllegalAccessException;
method public java.lang.invoke.MethodHandle unreflectConstructor(java.lang.reflect.Constructor<?>) throws java.lang.IllegalAccessException;
method public java.lang.invoke.MethodHandle unreflectGetter(java.lang.reflect.Field) throws java.lang.IllegalAccessException;
diff --git a/cmds/svc/src/com/android/commands/svc/UsbCommand.java b/cmds/svc/src/com/android/commands/svc/UsbCommand.java
index 4dcb05e..adbe9d0 100644
--- a/cmds/svc/src/com/android/commands/svc/UsbCommand.java
+++ b/cmds/svc/src/com/android/commands/svc/UsbCommand.java
@@ -36,8 +36,8 @@
public String longHelp() {
return shortHelp() + "\n"
+ "\n"
- + "usage: svc usb setFunction [function]\n"
- + " Set the current usb function.\n\n"
+ + "usage: svc usb setFunction [function] [usbDataUnlocked=false]\n"
+ + " Set the current usb function and optionally the data lock state.\n\n"
+ " svc usb getFunction\n"
+ " Gets the list of currently enabled functions\n";
}
@@ -49,8 +49,12 @@
if ("setFunction".equals(args[1])) {
IUsbManager usbMgr = IUsbManager.Stub.asInterface(ServiceManager.getService(
Context.USB_SERVICE));
+ boolean unlockData = false;
+ if (args.length >= 4) {
+ unlockData = Boolean.valueOf(args[3]);
+ }
try {
- usbMgr.setCurrentFunction((args.length >=3 ? args[2] : null), false);
+ usbMgr.setCurrentFunction((args.length >=3 ? args[2] : null), unlockData);
} catch (RemoteException e) {
System.err.println("Error communicating with UsbManager: " + e);
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index cae4be6..c7fc860 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -974,6 +974,10 @@
sendMessage(H.DUMP_HEAP, dhd, managed ? 1 : 0, 0, true /*async*/);
}
+ public void attachAgent(String agent) {
+ sendMessage(H.ATTACH_AGENT, agent);
+ }
+
public void setSchedulingGroup(int group) {
// Note: do this immediately, since going into the foreground
// should happen regardless of what pending work we have to do
@@ -1408,6 +1412,7 @@
public static final int MULTI_WINDOW_MODE_CHANGED = 152;
public static final int PICTURE_IN_PICTURE_MODE_CHANGED = 153;
public static final int LOCAL_VOICE_INTERACTION_STARTED = 154;
+ public static final int ATTACH_AGENT = 155;
String codeToString(int code) {
if (DEBUG_MESSAGES) {
@@ -1464,6 +1469,7 @@
case MULTI_WINDOW_MODE_CHANGED: return "MULTI_WINDOW_MODE_CHANGED";
case PICTURE_IN_PICTURE_MODE_CHANGED: return "PICTURE_IN_PICTURE_MODE_CHANGED";
case LOCAL_VOICE_INTERACTION_STARTED: return "LOCAL_VOICE_INTERACTION_STARTED";
+ case ATTACH_AGENT: return "ATTACH_AGENT";
}
}
return Integer.toString(code);
@@ -1719,6 +1725,9 @@
handleLocalVoiceInteractionStarted((IBinder) ((SomeArgs) msg.obj).arg1,
(IVoiceInteractor) ((SomeArgs) msg.obj).arg2);
break;
+ case ATTACH_AGENT:
+ handleAttachAgent((String) msg.obj);
+ break;
}
Object obj = msg.obj;
if (obj instanceof SomeArgs) {
@@ -2987,6 +2996,14 @@
}
}
+ static final void handleAttachAgent(String agent) {
+ try {
+ VMDebug.attachAgent(agent);
+ } catch (IOException e) {
+ Slog.e(TAG, "Attaching agent failed: " + agent);
+ }
+ }
+
private static final ThreadLocal<Intent> sCurrentBroadcastIntent = new ThreadLocal<Intent>();
/**
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index 05d9d7e..ad7f577 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -502,6 +502,14 @@
return true;
}
+ case ATTACH_AGENT_TRANSACTION:
+ {
+ data.enforceInterface(IApplicationThread.descriptor);
+ String agent = data.readString();
+ attachAgent(agent);
+ return true;
+ }
+
case DUMP_ACTIVITY_TRANSACTION: {
data.enforceInterface(IApplicationThread.descriptor);
ParcelFileDescriptor fd = data.readFileDescriptor();
@@ -1305,6 +1313,14 @@
data.recycle();
}
+ public void attachAgent(String agent) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(IApplicationThread.descriptor);
+ data.writeString(agent);
+ mRemote.transact(ATTACH_AGENT_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
+ data.recycle();
+ }
+
public void setCoreSettings(Bundle coreSettings) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index 3fa88ae..bfd97f8 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -123,6 +123,7 @@
throws RemoteException;
void dumpHeap(boolean managed, String path, ParcelFileDescriptor fd)
throws RemoteException;
+ void attachAgent(String path) throws RemoteException;
void setSchedulingGroup(int group) throws RemoteException;
// the package has been removed, clean up internal references
static final int PACKAGE_REMOVED = 0;
@@ -225,4 +226,5 @@
int SCHEDULE_MULTI_WINDOW_CHANGED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+58;
int SCHEDULE_PICTURE_IN_PICTURE_CHANGED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+59;
int SCHEDULE_LOCAL_VOICE_INTERACTION_STARTED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+60;
+ int ATTACH_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+61;
}
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index afe9651..c625756 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -266,7 +266,7 @@
setApplicationInfo(aInfo);
final List<String> newPaths = new ArrayList<>();
- makePaths(mActivityThread, aInfo, newPaths, null /*libPaths*/);
+ makePaths(mActivityThread, aInfo, newPaths);
final List<String> addedPaths = new ArrayList<>(newPaths.size());
if (oldPaths != null) {
@@ -314,8 +314,17 @@
mCredentialProtectedDataDirFile = FileUtils.newFileOrNull(aInfo.credentialProtectedDataDir);
}
- public static void makePaths(ActivityThread activityThread, ApplicationInfo aInfo,
- List<String> outZipPaths, List<String> outLibPaths) {
+ public static void makePaths(ActivityThread activityThread,
+ ApplicationInfo aInfo,
+ List<String> outZipPaths) {
+ makePaths(activityThread, false, aInfo, outZipPaths, null);
+ }
+
+ public static void makePaths(ActivityThread activityThread,
+ boolean isBundledApp,
+ ApplicationInfo aInfo,
+ List<String> outZipPaths,
+ List<String> outLibPaths) {
final String appDir = aInfo.sourceDir;
final String[] splitAppDirs = aInfo.splitSourceDirs;
final String libDir = aInfo.nativeLibraryDir;
@@ -398,7 +407,7 @@
}
}
- if (aInfo.isSystemApp() && !aInfo.isUpdatedSystemApp()) {
+ if (isBundledApp) {
// Add path to system libraries to libPaths;
// Access to system libs should be limited
// to bundled applications; this is why updated
@@ -471,11 +480,12 @@
// space and initialize to a small value (instead of incurring growth code).
final List<String> zipPaths = new ArrayList<>(10);
final List<String> libPaths = new ArrayList<>(10);
- makePaths(mActivityThread, mApplicationInfo, zipPaths, libPaths);
final boolean isBundledApp = mApplicationInfo.isSystemApp()
&& !mApplicationInfo.isUpdatedSystemApp();
+ makePaths(mActivityThread, isBundledApp, mApplicationInfo, zipPaths, libPaths);
+
String libraryPermittedPath = mDataDir;
if (isBundledApp) {
// This is necessary to grant bundled apps access to
@@ -596,19 +606,17 @@
}
final File profileFile = getPrimaryProfileFile(mPackageName);
- final File foreignDexProfilesFile =
- Environment.getDataProfilesDeForeignDexDirectory(UserHandle.myUserId());
- VMRuntime.registerAppInfo(profileFile.getPath(), mApplicationInfo.dataDir,
- codePaths.toArray(new String[codePaths.size()]), foreignDexProfilesFile.getPath());
+ VMRuntime.registerAppInfo(profileFile.getPath(),
+ codePaths.toArray(new String[codePaths.size()]));
// Setup the reporter to notify package manager of any relevant dex loads.
// At this point the primary apk is loaded and will not be reported.
// Anything loaded from now on will be tracked as a potential secondary
// or foreign dex file. The goal is to enable:
// 1) monitoring and compilation of secondary dex file
- // 2) track foreign dex file usage (used to determined the
- // compilation filter of apks).
+ // 2) track whether or not a dex file is used by other apps (used to
+ // determined the compilation filter of apks).
if (BaseDexClassLoader.getReporter() != DexLoadReporter.INSTANCE) {
// Set the dex load reporter if not already set.
// Note that during the app's life cycle different LoadedApks may be
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 9302cbc..8186937 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -26,6 +26,7 @@
import android.app.ActivityThread;
import android.bluetooth.le.BluetoothLeAdvertiser;
import android.bluetooth.le.BluetoothLeScanner;
+import android.bluetooth.le.PeriodicAdvertisingManager;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanRecord;
@@ -525,6 +526,7 @@
private static BluetoothLeScanner sBluetoothLeScanner;
private static BluetoothLeAdvertiser sBluetoothLeAdvertiser;
+ private static PeriodicAdvertisingManager sPeriodicAdvertisingManager;
private final IBluetoothManager mManagerService;
private IBluetooth mService;
@@ -630,6 +632,30 @@
}
/**
+ * Returns a {@link PeriodicAdvertisingManager} object for Bluetooth LE Periodic Advertising
+ * operations. Will return null if Bluetooth is turned off or if Bluetooth LE Periodic
+ * Advertising is not supported on this device.
+ * <p>
+ * Use {@link #isLePeriodicAdvertisingSupported()} to check whether LE Periodic Advertising is
+ * supported on this device before calling this method.
+ */
+ public PeriodicAdvertisingManager getPeriodicAdvertisingManager() {
+ if (!getLeAccess())
+ return null;
+
+ if (!isLePeriodicAdvertisingSupported())
+ return null;
+
+ synchronized (mLock) {
+ if (sPeriodicAdvertisingManager == null) {
+ sPeriodicAdvertisingManager =
+ new PeriodicAdvertisingManager(mManagerService);
+ }
+ }
+ return sPeriodicAdvertisingManager;
+ }
+
+ /**
* Returns a {@link BluetoothLeScanner} object for Bluetooth LE scan operations.
*/
public BluetoothLeScanner getBluetoothLeScanner() {
@@ -1385,6 +1411,78 @@
}
/**
+ * Return true if LE 2M PHY feature is supported.
+ *
+ * @return true if chipset supports LE 2M PHY feature
+ */
+ public boolean isLe2MPhySupported() {
+ if (!getLeAccess()) return false;
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null) return mService.isLe2MPhySupported();
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to get isExtendedAdvertisingSupported, error: ", e);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ return false;
+ }
+
+ /**
+ * Return true if LE Coded PHY feature is supported.
+ *
+ * @return true if chipset supports LE Coded PHY feature
+ */
+ public boolean isLeCodedPhySupported() {
+ if (!getLeAccess()) return false;
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null) return mService.isLeCodedPhySupported();
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to get isLeCodedPhySupported, error: ", e);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ return false;
+ }
+
+ /**
+ * Return true if LE Periodic Advertising feature is supported.
+ *
+ * @return true if chipset supports LE Periodic Advertising feature
+ */
+ public boolean isLeExtendedAdvertisingSupported() {
+ if (!getLeAccess()) return false;
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null) return mService.isLeExtendedAdvertisingSupported();
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to get isLeExtendedAdvertisingSupported, error: ", e);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ return false;
+ }
+
+ /**
+ * Return true if LE Periodic Advertising feature is supported.
+ *
+ * @return true if chipset supports LE Periodic Advertising feature
+ */
+ public boolean isLePeriodicAdvertisingSupported() {
+ if (!getLeAccess()) return false;
+ try {
+ mServiceLock.readLock().lock();
+ if (mService != null) return mService.isLePeriodicAdvertisingSupported();
+ } catch (RemoteException e) {
+ Log.e(TAG, "failed to get isLePeriodicAdvertisingSupported, error: ", e);
+ } finally {
+ mServiceLock.readLock().unlock();
+ }
+ return false;
+ }
+
+ /**
* Return true if hardware has entries available for matching beacons
*
* @return true if there are hw entries available for matching beacons
@@ -1858,6 +1956,35 @@
return listenUsingL2capOn(port, false, false);
}
+
+ /**
+ * Construct an insecure L2CAP server socket.
+ * Call #accept to retrieve connections to this socket.
+ * <p>To auto assign a port without creating a SDP record use
+ * {@link SOCKET_CHANNEL_AUTO_STATIC_NO_SDP} as port number.
+ * @param port the PSM to listen on
+ * @return An L2CAP BluetoothServerSocket
+ * @throws IOException On error, for example Bluetooth not available, or
+ * insufficient permissions.
+ * @hide
+ */
+ public BluetoothServerSocket listenUsingInsecureL2capOn(int port) throws IOException {
+ BluetoothServerSocket socket = new BluetoothServerSocket(
+ BluetoothSocket.TYPE_L2CAP, false, false, port, false, false);
+ 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}
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 5c9e2ee..31fc294 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -592,6 +592,42 @@
*/
public static final int TRANSPORT_LE = 2;
+ /**
+ * 1M initiating PHY.
+ */
+ public static final int PHY_LE_1M = 1;
+
+ /**
+ * 2M initiating PHY.
+ */
+ public static final int PHY_LE_2M = 2;
+
+ /**
+ * LE Coded initiating PHY.
+ */
+ public static final int PHY_LE_CODED = 4;
+
+ /**
+ * Any LE PHY.
+ */
+ public static final int PHY_LE_ANY = PHY_LE_1M | PHY_LE_2M | PHY_LE_CODED;
+
+ /**
+ * No preferred coding when transmitting on the LE Coded PHY.
+ */
+ public static final int PHY_OPTION_NO_PREFERRED = 0;
+
+ /**
+ * Prefer the S=2 coding to be used when transmitting on the LE Coded PHY.
+ */
+ public static final int PHY_OPTION_S2 = 1;
+
+ /**
+ * Prefer the S=8 coding to be used when transmitting on the LE Coded PHY.
+ */
+ public static final int PHY_OPTION_S8 = 2;
+
+
/** @hide */
public static final String EXTRA_MAS_INSTANCE =
"android.bluetooth.device.extra.MAS_INSTANCE";
@@ -1412,6 +1448,27 @@
}
/**
+ * Create an L2cap {@link BluetoothSocket} ready to start an insecure
+ * outgoing connection to this remote device on given channel.
+ * <p>The remote device will be not authenticated and communication on this
+ * socket will not be encrypted.
+ * <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 createInsecureL2capSocket(int channel) throws IOException {
+ return new BluetoothSocket(BluetoothSocket.TYPE_L2CAP, -1, false, false, 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
@@ -1594,6 +1651,67 @@
*/
public BluetoothGatt connectGatt(Context context, boolean autoConnect,
BluetoothGattCallback callback, int transport) {
+ return (connectGatt(context, autoConnect,callback, TRANSPORT_AUTO, PHY_LE_1M));
+ }
+
+ /**
+ * Connect to GATT Server hosted by this device. Caller acts as GATT client.
+ * The callback is used to deliver results to Caller, such as connection status as well
+ * as any further GATT client operations.
+ * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct
+ * GATT client operations.
+ * @param callback GATT callback handler that will receive asynchronous callbacks.
+ * @param autoConnect Whether to directly connect to the remote device (false)
+ * or to automatically connect as soon as the remote
+ * device becomes available (true).
+ * @throws IllegalArgumentException if callback is null
+ */
+ public BluetoothGatt connectGatt(Context context, boolean autoConnect,
+ BluetoothGattCallbackExt callback) {
+ return (connectGatt(context, autoConnect,callback, TRANSPORT_AUTO));
+ }
+
+ /**
+ * Connect to GATT Server hosted by this device. Caller acts as GATT client.
+ * The callback is used to deliver results to Caller, such as connection status as well
+ * as any further GATT client operations.
+ * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct
+ * GATT client operations.
+ * @param callback GATT callback handler that will receive asynchronous callbacks.
+ * @param autoConnect Whether to directly connect to the remote device (false)
+ * or to automatically connect as soon as the remote
+ * device becomes available (true).
+ * @param transport preferred transport for GATT connections to remote dual-mode devices
+ * {@link BluetoothDevice#TRANSPORT_AUTO} or
+ * {@link BluetoothDevice#TRANSPORT_BREDR} or {@link BluetoothDevice#TRANSPORT_LE}
+ * @throws IllegalArgumentException if callback is null
+ */
+ public BluetoothGatt connectGatt(Context context, boolean autoConnect,
+ BluetoothGattCallbackExt callback, int transport) {
+ return (connectGatt(context, autoConnect,callback, TRANSPORT_AUTO, PHY_LE_1M));
+ }
+
+ /**
+ * Connect to GATT Server hosted by this device. Caller acts as GATT client.
+ * The callback is used to deliver results to Caller, such as connection status as well
+ * as any further GATT client operations.
+ * The method returns a BluetoothGatt instance. You can use BluetoothGatt to conduct
+ * GATT client operations.
+ * @param callback GATT callback handler that will receive asynchronous callbacks.
+ * @param autoConnect Whether to directly connect to the remote device (false)
+ * or to automatically connect as soon as the remote
+ * device becomes available (true).
+ * @param transport preferred transport for GATT connections to remote dual-mode devices
+ * {@link BluetoothDevice#TRANSPORT_AUTO} or
+ * {@link BluetoothDevice#TRANSPORT_BREDR} or {@link BluetoothDevice#TRANSPORT_LE}
+ * @param phy preferred PHY for connections to remote LE device. Bitwise OR of any of
+ * {@link BluetoothDevice#PHY_LE_1M}, {@link BluetoothDevice#PHY_LE_2M},
+ * and {@link BluetoothDevice#PHY_LE_CODED}. This option does not take effect if
+ * {@code autoConnect} is set to true.
+ * @throws IllegalArgumentException if callback is null
+ */
+ public BluetoothGatt connectGatt(Context context, boolean autoConnect,
+ BluetoothGattCallbackExt callback, int transport, int phy) {
// TODO(Bluetooth) check whether platform support BLE
// Do the check here or in GattServer?
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
@@ -1604,7 +1722,7 @@
// BLE is not supported
return null;
}
- BluetoothGatt gatt = new BluetoothGatt(iGatt, this, transport);
+ BluetoothGatt gatt = new BluetoothGatt(iGatt, this, transport, phy);
gatt.connect(autoConnect, callback);
return gatt;
} catch (RemoteException e) {Log.e(TAG, "", e);}
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index 97a3297..0cb69ae 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -31,7 +31,7 @@
* <p>This class provides Bluetooth GATT functionality to enable communication
* with Bluetooth Smart or Smart Ready devices.
*
- * <p>To connect to a remote peripheral device, create a {@link BluetoothGattCallback}
+ * <p>To connect to a remote peripheral device, create a {@link BluetoothGattCallbackExt}
* and call {@link BluetoothDevice#connectGatt} to get a instance of this class.
* GATT capable devices can be discovered using the Bluetooth device discovery or BLE
* scan process.
@@ -42,7 +42,7 @@
private static final boolean VDBG = false;
private IBluetoothGatt mService;
- private BluetoothGattCallback mCallback;
+ private BluetoothGattCallbackExt mCallback;
private int mClientIf;
private BluetoothDevice mDevice;
private boolean mAutoConnect;
@@ -51,6 +51,7 @@
private final Object mStateLock = new Object();
private Boolean mDeviceBusy = false;
private int mTransport;
+ private int mPhy;
private static final int AUTH_RETRY_STATE_IDLE = 0;
private static final int AUTH_RETRY_STATE_NO_MITM = 1;
@@ -132,10 +133,10 @@
/*package*/ static final int AUTHENTICATION_MITM = 2;
/**
- * Bluetooth GATT callbacks. Overrides the default BluetoothGattCallback implementation.
+ * Bluetooth GATT callbacks. Overrides the default BluetoothGattCallbackExt implementation.
*/
- private final IBluetoothGattCallback mBluetoothGattCallback =
- new IBluetoothGattCallback.Stub() {
+ private final IBluetoothGattCallbackExt mBluetoothGattCallbackExt =
+ new IBluetoothGattCallbackExt.Stub() {
/**
* Application interface registered - app is ready to go
* @hide
@@ -161,13 +162,51 @@
}
try {
mService.clientConnect(mClientIf, mDevice.getAddress(),
- !mAutoConnect, mTransport); // autoConnect is inverse of "isDirect"
+ !mAutoConnect, mTransport, mPhy); // autoConnect is inverse of "isDirect"
} catch (RemoteException e) {
Log.e(TAG,"",e);
}
}
/**
+ * Phy update callback
+ * @hide
+ */
+ @Override
+ public void onPhyUpdate(String address, int txPhy, int rxPhy, int status) {
+ if (DBG) Log.d(TAG, "onPhyUpdate() - status=" + status
+ + " address=" + address + " txPhy=" + txPhy + " rxPhy=" + rxPhy);
+ if (!address.equals(mDevice.getAddress())) {
+ return;
+ }
+
+ try {
+ mCallback.onPhyUpdate(BluetoothGatt.this, txPhy, rxPhy, status);
+ } catch (Exception ex) {
+ Log.w(TAG, "Unhandled exception in callback", ex);
+ }
+ }
+
+ /**
+ * Phy read callback
+ * @hide
+ */
+ @Override
+ public void onPhyRead(String address, int txPhy, int rxPhy, int status) {
+ if (DBG) Log.d(TAG, "onPhyRead() - status=" + status
+ + " address=" + address + " txPhy=" + txPhy + " rxPhy=" + rxPhy);
+ if (!address.equals(mDevice.getAddress())) {
+ return;
+ }
+
+ try {
+ mCallback.onPhyRead(BluetoothGatt.this, txPhy, rxPhy, status);
+ } catch (Exception ex) {
+ Log.w(TAG, "Unhandled exception in callback", ex);
+ }
+ }
+
+ /**
* Client connection state changed
* @hide
*/
@@ -503,10 +542,11 @@
};
/*package*/ BluetoothGatt(IBluetoothGatt iGatt, BluetoothDevice device,
- int transport) {
+ int transport, int phy) {
mService = iGatt;
mDevice = device;
mTransport = transport;
+ mPhy = phy;
mServices = new ArrayList<BluetoothGattService>();
mConnState = CONN_STATE_IDLE;
@@ -578,7 +618,7 @@
/**
* Register an application callback to start using GATT.
*
- * <p>This is an asynchronous call. The callback {@link BluetoothGattCallback#onAppRegistered}
+ * <p>This is an asynchronous call. The callback {@link BluetoothGattCallbackExt#onAppRegistered}
* is used to notify success or failure if the function returns true.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
@@ -587,7 +627,7 @@
* @return If true, the callback will be called to notify success or failure,
* false on immediate error
*/
- private boolean registerApp(BluetoothGattCallback callback) {
+ private boolean registerApp(BluetoothGattCallbackExt callback) {
if (DBG) Log.d(TAG, "registerApp()");
if (mService == null) return false;
@@ -596,7 +636,7 @@
if (DBG) Log.d(TAG, "registerApp() - UUID=" + uuid);
try {
- mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallback);
+ mService.registerClient(new ParcelUuid(uuid), mBluetoothGattCallbackExt);
} catch (RemoteException e) {
Log.e(TAG,"",e);
return false;
@@ -626,7 +666,7 @@
*
* <p>The connection may not be established right away, but will be
* completed when the remote device is available. A
- * {@link BluetoothGattCallback#onConnectionStateChange} callback will be
+ * {@link BluetoothGattCallbackExt#onConnectionStateChange} callback will be
* invoked when the connection state changes as a result of this function.
*
* <p>The autoConnect parameter determines whether to actively connect to
@@ -644,7 +684,7 @@
* device becomes available (true).
* @return true, if the connection attempt was initiated successfully
*/
- /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallback callback) {
+ /*package*/ boolean connect(Boolean autoConnect, BluetoothGattCallbackExt callback) {
if (DBG) Log.d(TAG, "connect() - device: " + mDevice.getAddress() + ", auto: " + autoConnect);
synchronized(mStateLock) {
if (mConnState != CONN_STATE_IDLE) {
@@ -696,7 +736,7 @@
public boolean connect() {
try {
mService.clientConnect(mClientIf, mDevice.getAddress(),
- false, mTransport); // autoConnect is inverse of "isDirect"
+ false, mTransport, mPhy); // autoConnect is inverse of "isDirect"
return true;
} catch (RemoteException e) {
Log.e(TAG,"",e);
@@ -705,6 +745,45 @@
}
/**
+ * Set the preferred connection PHY for this app. Please note that this is just a
+ * recommendation, wether the PHY change will happen depends on other applications peferences,
+ * local and remote controller capabilities. Controller can override these settings.
+ * <p>
+ * {@link BluetoothGattCallbackExt#onPhyUpdate} will be triggered as a result of this call, even
+ * if no PHY change happens. It is also triggered when remote device updates the PHY.
+ *
+ * @param txPhy preferred transmitter PHY. Bitwise OR of any of
+ * {@link BluetoothDevice#PHY_LE_1M}, {@link BluetoothDevice#PHY_LE_2M}, and
+ * {@link BluetoothDevice#PHY_LE_CODED}.
+ * @param rxPhy preferred receiver PHY. Bitwise OR of any of
+ * {@link BluetoothDevice#PHY_LE_1M}, {@link BluetoothDevice#PHY_LE_2M}, and
+ * {@link BluetoothDevice#PHY_LE_CODED}.
+ * @param phyOptions preferred coding to use when transmitting on the LE Coded PHY. Can be one
+ * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED},
+ * {@link BluetoothDevice#PHY_OPTION_S2} or {@link BluetoothDevice#PHY_OPTION_S8}
+ */
+ public void setPreferredPhy(int txPhy, int rxPhy, int phyOptions) {
+ try {
+ mService.clientSetPreferredPhy(mClientIf, mDevice.getAddress(), txPhy, rxPhy,
+ phyOptions);
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ }
+ }
+
+ /**
+ * Read the current transmitter PHY and receiver PHY of the connection. The values are returned
+ * in {@link BluetoothGattCallbackExt#onPhyRead}
+ */
+ public void readPhy() {
+ try {
+ mService.clientReadPhy(mClientIf, mDevice.getAddress());
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ }
+ }
+
+ /**
* Return the remote bluetooth device this GATT client targets to
*
* @return remote bluetooth device
@@ -718,7 +797,7 @@
* characteristics and descriptors.
*
* <p>This is an asynchronous operation. Once service discovery is completed,
- * the {@link BluetoothGattCallback#onServicesDiscovered} callback is
+ * the {@link BluetoothGattCallbackExt#onServicesDiscovered} callback is
* triggered. If the discovery was successful, the remote services can be
* retrieved using the {@link #getServices} function.
*
@@ -797,7 +876,7 @@
* Reads the requested characteristic from the associated remote device.
*
* <p>This is an asynchronous operation. The result of the read operation
- * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
+ * is reported by the {@link BluetoothGattCallbackExt#onCharacteristicRead}
* callback.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
@@ -839,7 +918,7 @@
* Writes a given characteristic and its values to the associated remote device.
*
* <p>Once the write operation has been completed, the
- * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
+ * {@link BluetoothGattCallbackExt#onCharacteristicWrite} callback is invoked,
* reporting the result of the operation.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
@@ -883,7 +962,7 @@
* Reads the value for a given descriptor from the associated remote device.
*
* <p>Once the read operation has been completed, the
- * {@link BluetoothGattCallback#onDescriptorRead} callback is
+ * {@link BluetoothGattCallbackExt#onDescriptorRead} callback is
* triggered, signaling the result of the operation.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
@@ -924,7 +1003,7 @@
/**
* Write the value of a given descriptor to the associated remote device.
*
- * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is
+ * <p>A {@link BluetoothGattCallbackExt#onDescriptorWrite} callback is
* triggered to report the result of the write operation.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
@@ -968,7 +1047,7 @@
* <p>Once a reliable write transaction has been initiated, all calls
* to {@link #writeCharacteristic} are sent to the remote device for
* verification and queued up for atomic execution. The application will
- * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback
+ * receive an {@link BluetoothGattCallbackExt#onCharacteristicWrite} callback
* in response to every {@link #writeCharacteristic} call and is responsible
* for verifying if the value has been transmitted accurately.
*
@@ -1002,7 +1081,7 @@
* <p>This function will commit all queued up characteristic write
* operations for a given remote device.
*
- * <p>A {@link BluetoothGattCallback#onReliableWriteCompleted} callback is
+ * <p>A {@link BluetoothGattCallbackExt#onReliableWriteCompleted} callback is
* invoked to indicate whether the transaction has been executed correctly.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
@@ -1059,7 +1138,7 @@
* Enable or disable notifications/indications for a given characteristic.
*
* <p>Once notifications are enabled for a characteristic, a
- * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be
+ * {@link BluetoothGattCallbackExt#onCharacteristicChanged} callback will be
* triggered if the remote device indicates that the given characteristic
* has changed.
*
@@ -1114,7 +1193,7 @@
/**
* Read the RSSI for a connected remote device.
*
- * <p>The {@link BluetoothGattCallback#onReadRemoteRssi} callback will be
+ * <p>The {@link BluetoothGattCallbackExt#onReadRemoteRssi} callback will be
* invoked when the RSSI value has been read.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
@@ -1142,7 +1221,7 @@
* the data sent is truncated to the MTU size. This function may be used
* to request a larger MTU size to be able to send more data at once.
*
- * <p>A {@link BluetoothGattCallback#onMtuChanged} callback will indicate
+ * <p>A {@link BluetoothGattCallbackExt#onMtuChanged} callback will indicate
* whether this operation was successful.
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
diff --git a/core/java/android/bluetooth/BluetoothGattCallback.java b/core/java/android/bluetooth/BluetoothGattCallback.java
index a915620..4da106d 100644
--- a/core/java/android/bluetooth/BluetoothGattCallback.java
+++ b/core/java/android/bluetooth/BluetoothGattCallback.java
@@ -18,138 +18,22 @@
/**
* This abstract class is used to implement {@link BluetoothGatt} callbacks.
+ * @deprecated use {@link BluetoothGattCallbackExt}
*/
-public abstract class BluetoothGattCallback {
+public abstract class BluetoothGattCallback extends BluetoothGattCallbackExt {
/**
- * Callback indicating when GATT client has connected/disconnected to/from a remote
- * GATT server.
- *
- * @param gatt GATT client
- * @param status Status of the connect or disconnect operation.
- * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds.
- * @param newState Returns the new connection state. Can be one of
- * {@link BluetoothProfile#STATE_DISCONNECTED} or
- * {@link BluetoothProfile#STATE_CONNECTED}
+ * @hide
*/
- public void onConnectionStateChange(BluetoothGatt gatt, int status,
- int newState) {
+ @Override
+ public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
}
/**
- * Callback invoked when the list of remote services, characteristics and descriptors
- * for the remote device have been updated, ie new services have been discovered.
- *
- * @param gatt GATT client invoked {@link BluetoothGatt#discoverServices}
- * @param status {@link BluetoothGatt#GATT_SUCCESS} if the remote device
- * has been explored successfully.
+ * @hide
*/
- public void onServicesDiscovered(BluetoothGatt gatt, int status) {
+ @Override
+ public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
}
- /**
- * Callback reporting the result of a characteristic read operation.
- *
- * @param gatt GATT client invoked {@link BluetoothGatt#readCharacteristic}
- * @param characteristic Characteristic that was read from the associated
- * remote device.
- * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation
- * was completed successfully.
- */
- public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic,
- int status) {
- }
-
- /**
- * Callback indicating the result of a characteristic write operation.
- *
- * <p>If this callback is invoked while a reliable write transaction is
- * in progress, the value of the characteristic represents the value
- * reported by the remote device. An application should compare this
- * value to the desired value to be written. If the values don't match,
- * the application must abort the reliable write transaction.
- *
- * @param gatt GATT client invoked {@link BluetoothGatt#writeCharacteristic}
- * @param characteristic Characteristic that was written to the associated
- * remote device.
- * @param status The result of the write operation
- * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds.
- */
- public void onCharacteristicWrite(BluetoothGatt gatt,
- BluetoothGattCharacteristic characteristic, int status) {
- }
-
- /**
- * Callback triggered as a result of a remote characteristic notification.
- *
- * @param gatt GATT client the characteristic is associated with
- * @param characteristic Characteristic that has been updated as a result
- * of a remote notification event.
- */
- public void onCharacteristicChanged(BluetoothGatt gatt,
- BluetoothGattCharacteristic characteristic) {
- }
-
- /**
- * Callback reporting the result of a descriptor read operation.
- *
- * @param gatt GATT client invoked {@link BluetoothGatt#readDescriptor}
- * @param descriptor Descriptor that was read from the associated
- * remote device.
- * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation
- * was completed successfully
- */
- public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
- int status) {
- }
-
- /**
- * Callback indicating the result of a descriptor write operation.
- *
- * @param gatt GATT client invoked {@link BluetoothGatt#writeDescriptor}
- * @param descriptor Descriptor that was writte to the associated
- * remote device.
- * @param status The result of the write operation
- * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds.
- */
- public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
- int status) {
- }
-
- /**
- * Callback invoked when a reliable write transaction has been completed.
- *
- * @param gatt GATT client invoked {@link BluetoothGatt#executeReliableWrite}
- * @param status {@link BluetoothGatt#GATT_SUCCESS} if the reliable write
- * transaction was executed successfully
- */
- public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
- }
-
- /**
- * Callback reporting the RSSI for a remote device connection.
- *
- * This callback is triggered in response to the
- * {@link BluetoothGatt#readRemoteRssi} function.
- *
- * @param gatt GATT client invoked {@link BluetoothGatt#readRemoteRssi}
- * @param rssi The RSSI value for the remote device
- * @param status {@link BluetoothGatt#GATT_SUCCESS} if the RSSI was read successfully
- */
- public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
- }
-
- /**
- * Callback indicating the MTU for a given device connection has changed.
- *
- * This callback is triggered in response to the
- * {@link BluetoothGatt#requestMtu} function, or in response to a connection
- * event.
- *
- * @param gatt GATT client invoked {@link BluetoothGatt#requestMtu}
- * @param mtu The new MTU size
- * @param status {@link BluetoothGatt#GATT_SUCCESS} if the MTU has been changed successfully
- */
- public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
- }
}
diff --git a/core/java/android/bluetooth/BluetoothGattCallbackExt.java b/core/java/android/bluetooth/BluetoothGattCallbackExt.java
new file mode 100644
index 0000000..63774c8
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothGattCallbackExt.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2017 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;
+
+/**
+ * This abstract class is used to implement {@link BluetoothGatt} callbacks.
+ */
+public abstract class BluetoothGattCallbackExt {
+
+ /**
+ * Callback triggered as result of {@link BluetoothGatt#setPreferredPhy}, or as a result of
+ * remote device changing the PHY.
+ *
+ * @param gatt GATT client
+ * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M},
+ * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}.
+ * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M},
+ * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}.
+ * @param status status of the operation
+ */
+ public void onPhyUpdate(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
+ }
+
+ /**
+ * Callback triggered as result of {@link BluetoothGatt#readPhy}
+ *
+ * @param gatt GATT client
+ * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M},
+ * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}.
+ * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M},
+ * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}.
+ * @param status status of the operation
+ */
+ public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) {
+ }
+
+ /**
+ * Callback indicating when GATT client has connected/disconnected to/from a remote
+ * GATT server.
+ *
+ * @param gatt GATT client
+ * @param status Status of the connect or disconnect operation.
+ * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds.
+ * @param newState Returns the new connection state. Can be one of
+ * {@link BluetoothProfile#STATE_DISCONNECTED} or
+ * {@link BluetoothProfile#STATE_CONNECTED}
+ */
+ public void onConnectionStateChange(BluetoothGatt gatt, int status,
+ int newState) {
+ }
+
+ /**
+ * Callback invoked when the list of remote services, characteristics and descriptors
+ * for the remote device have been updated, ie new services have been discovered.
+ *
+ * @param gatt GATT client invoked {@link BluetoothGatt#discoverServices}
+ * @param status {@link BluetoothGatt#GATT_SUCCESS} if the remote device
+ * has been explored successfully.
+ */
+ public void onServicesDiscovered(BluetoothGatt gatt, int status) {
+ }
+
+ /**
+ * Callback reporting the result of a characteristic read operation.
+ *
+ * @param gatt GATT client invoked {@link BluetoothGatt#readCharacteristic}
+ * @param characteristic Characteristic that was read from the associated
+ * remote device.
+ * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation
+ * was completed successfully.
+ */
+ public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic,
+ int status) {
+ }
+
+ /**
+ * Callback indicating the result of a characteristic write operation.
+ *
+ * <p>If this callback is invoked while a reliable write transaction is
+ * in progress, the value of the characteristic represents the value
+ * reported by the remote device. An application should compare this
+ * value to the desired value to be written. If the values don't match,
+ * the application must abort the reliable write transaction.
+ *
+ * @param gatt GATT client invoked {@link BluetoothGatt#writeCharacteristic}
+ * @param characteristic Characteristic that was written to the associated
+ * remote device.
+ * @param status The result of the write operation
+ * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds.
+ */
+ public void onCharacteristicWrite(BluetoothGatt gatt,
+ BluetoothGattCharacteristic characteristic, int status) {
+ }
+
+ /**
+ * Callback triggered as a result of a remote characteristic notification.
+ *
+ * @param gatt GATT client the characteristic is associated with
+ * @param characteristic Characteristic that has been updated as a result
+ * of a remote notification event.
+ */
+ public void onCharacteristicChanged(BluetoothGatt gatt,
+ BluetoothGattCharacteristic characteristic) {
+ }
+
+ /**
+ * Callback reporting the result of a descriptor read operation.
+ *
+ * @param gatt GATT client invoked {@link BluetoothGatt#readDescriptor}
+ * @param descriptor Descriptor that was read from the associated
+ * remote device.
+ * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation
+ * was completed successfully
+ */
+ public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
+ int status) {
+ }
+
+ /**
+ * Callback indicating the result of a descriptor write operation.
+ *
+ * @param gatt GATT client invoked {@link BluetoothGatt#writeDescriptor}
+ * @param descriptor Descriptor that was writte to the associated
+ * remote device.
+ * @param status The result of the write operation
+ * {@link BluetoothGatt#GATT_SUCCESS} if the operation succeeds.
+ */
+ public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
+ int status) {
+ }
+
+ /**
+ * Callback invoked when a reliable write transaction has been completed.
+ *
+ * @param gatt GATT client invoked {@link BluetoothGatt#executeReliableWrite}
+ * @param status {@link BluetoothGatt#GATT_SUCCESS} if the reliable write
+ * transaction was executed successfully
+ */
+ public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
+ }
+
+ /**
+ * Callback reporting the RSSI for a remote device connection.
+ *
+ * This callback is triggered in response to the
+ * {@link BluetoothGatt#readRemoteRssi} function.
+ *
+ * @param gatt GATT client invoked {@link BluetoothGatt#readRemoteRssi}
+ * @param rssi The RSSI value for the remote device
+ * @param status {@link BluetoothGatt#GATT_SUCCESS} if the RSSI was read successfully
+ */
+ public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
+ }
+
+ /**
+ * Callback indicating the MTU for a given device connection has changed.
+ *
+ * This callback is triggered in response to the
+ * {@link BluetoothGatt#requestMtu} function, or in response to a connection
+ * event.
+ *
+ * @param gatt GATT client invoked {@link BluetoothGatt#requestMtu}
+ * @param mtu The new MTU size
+ * @param status {@link BluetoothGatt#GATT_SUCCESS} if the MTU has been changed successfully
+ */
+ public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothGattServer.java b/core/java/android/bluetooth/BluetoothGattServer.java
index 5ffceba..9ee739f 100644
--- a/core/java/android/bluetooth/BluetoothGattServer.java
+++ b/core/java/android/bluetooth/BluetoothGattServer.java
@@ -46,7 +46,7 @@
private BluetoothAdapter mAdapter;
private IBluetoothGatt mService;
- private BluetoothGattServerCallback mCallback;
+ private BluetoothGattServerCallbackExt mCallback;
private Object mServerIfLock = new Object();
private int mServerIf;
@@ -59,8 +59,8 @@
/**
* Bluetooth GATT interface callbacks
*/
- private final IBluetoothGattServerCallback mBluetoothGattServerCallback =
- new IBluetoothGattServerCallback.Stub() {
+ private final IBluetoothGattServerCallbackExt mBluetoothGattServerCallback =
+ new IBluetoothGattServerCallbackExt.Stub() {
/**
* Application interface registered - app is ready to go
* @hide
@@ -292,6 +292,42 @@
Log.w(TAG, "Unhandled exception: " + ex);
}
}
+
+ /**
+ * The PHY for a connection was updated
+ * @hide
+ */
+ public void onPhyUpdate(String address, int txPhy, int rxPhy, int status) {
+ if (DBG) Log.d(TAG, "onPhyUpdate() - " + "device=" + address + ", txPHy=" + txPhy
+ + ", rxPHy=" + rxPhy);
+
+ BluetoothDevice device = mAdapter.getRemoteDevice(address);
+ if (device == null) return;
+
+ try {
+ mCallback.onPhyUpdate(device, txPhy, rxPhy, status);
+ } catch (Exception ex) {
+ Log.w(TAG, "Unhandled exception: " + ex);
+ }
+ }
+
+ /**
+ * The PHY for a connection was read
+ * @hide
+ */
+ public void onPhyRead(String address, int txPhy, int rxPhy, int status) {
+ if (DBG) Log.d(TAG, "onPhyUpdate() - " + "device=" + address + ", txPHy=" + txPhy
+ + ", rxPHy=" + rxPhy);
+
+ BluetoothDevice device = mAdapter.getRemoteDevice(address);
+ if (device == null) return;
+
+ try {
+ mCallback.onPhyRead(device, txPhy, rxPhy, status);
+ } catch (Exception ex) {
+ Log.w(TAG, "Unhandled exception: " + ex);
+ }
+ }
};
/**
@@ -360,7 +396,7 @@
* @return true, the callback will be called to notify success or failure,
* false on immediate error
*/
- /*package*/ boolean registerCallback(BluetoothGattServerCallback callback) {
+ /*package*/ boolean registerCallback(BluetoothGattServerCallbackExt callback) {
if (DBG) Log.d(TAG, "registerCallback()");
if (mService == null) {
Log.e(TAG, "GATT service not available");
@@ -436,7 +472,7 @@
*
* <p>The connection may not be established right away, but will be
* completed when the remote device is available. A
- * {@link BluetoothGattServerCallback#onConnectionStateChange} callback will be
+ * {@link BluetoothGattServerCallbackExt#onConnectionStateChange} callback will be
* invoked when the connection state changes as a result of this function.
*
* <p>The autoConnect paramter determines whether to actively connect to
@@ -488,16 +524,58 @@
}
/**
+ * Set the preferred connection PHY for this app. Please note that this is just a
+ * recommendation, wether the PHY change will happen depends on other applications peferences,
+ * local and remote controller capabilities. Controller can override these settings.
+ * <p>
+ * {@link BluetoothGattServerCallbackExt#onPhyUpdate} will be triggered as a result of this call, even
+ * if no PHY change happens. It is also triggered when remote device updates the PHY.
+ *
+ * @param device The remote device to send this response to
+ * @param txPhy preferred transmitter PHY. Bitwise OR of any of
+ * {@link BluetoothDevice#PHY_LE_1M}, {@link BluetoothDevice#PHY_LE_2M}, and
+ * {@link BluetoothDevice#PHY_LE_CODED}.
+ * @param rxPhy preferred receiver PHY. Bitwise OR of any of
+ * {@link BluetoothDevice#PHY_LE_1M}, {@link BluetoothDevice#PHY_LE_2M}, and
+ * {@link BluetoothDevice#PHY_LE_CODED}.
+ * @param phyOptions preferred coding to use when transmitting on the LE Coded PHY. Can be one
+ * of {@link BluetoothDevice#PHY_OPTION_NO_PREFERRED},
+ * {@link BluetoothDevice#PHY_OPTION_S2} or {@link BluetoothDevice#PHY_OPTION_S8}
+ */
+ public void setPreferredPhy(BluetoothDevice device, int txPhy, int rxPhy, int phyOptions) {
+ try {
+ mService.serverSetPreferredPhy(mServerIf, device.getAddress(), txPhy, rxPhy,
+ phyOptions);
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ }
+ }
+
+ /**
+ * Read the current transmitter PHY and receiver PHY of the connection. The values are returned
+ * in {@link BluetoothGattServerCallbackExt#onPhyRead}
+ *
+ * @param device The remote device to send this response to
+ */
+ public void readPhy(BluetoothDevice device) {
+ try {
+ mService.serverReadPhy(mServerIf, device.getAddress());
+ } catch (RemoteException e) {
+ Log.e(TAG,"",e);
+ }
+ }
+
+ /**
* Send a response to a read or write request to a remote device.
*
* <p>This function must be invoked in when a remote read/write request
* is received by one of these callback methods:
*
* <ul>
- * <li>{@link BluetoothGattServerCallback#onCharacteristicReadRequest}
- * <li>{@link BluetoothGattServerCallback#onCharacteristicWriteRequest}
- * <li>{@link BluetoothGattServerCallback#onDescriptorReadRequest}
- * <li>{@link BluetoothGattServerCallback#onDescriptorWriteRequest}
+ * <li>{@link BluetoothGattServerCallbackExt#onCharacteristicReadRequest}
+ * <li>{@link BluetoothGattServerCallbackExt#onCharacteristicWriteRequest}
+ * <li>{@link BluetoothGattServerCallbackExt#onDescriptorReadRequest}
+ * <li>{@link BluetoothGattServerCallbackExt#onDescriptorWriteRequest}
* </ul>
*
* <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission.
diff --git a/core/java/android/bluetooth/BluetoothGattServerCallback.java b/core/java/android/bluetooth/BluetoothGattServerCallback.java
index 2afcf9a..75ceb52 100644
--- a/core/java/android/bluetooth/BluetoothGattServerCallback.java
+++ b/core/java/android/bluetooth/BluetoothGattServerCallback.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -20,141 +20,21 @@
/**
* This abstract class is used to implement {@link BluetoothGattServer} callbacks.
+ * @deprecated please use {@link BluetoothGattServerCallbackExt}
*/
-public abstract class BluetoothGattServerCallback {
+public abstract class BluetoothGattServerCallback extends BluetoothGattServerCallbackExt {
/**
- * Callback indicating when a remote device has been connected or disconnected.
- *
- * @param device Remote device that has been connected or disconnected.
- * @param status Status of the connect or disconnect operation.
- * @param newState Returns the new connection state. Can be one of
- * {@link BluetoothProfile#STATE_DISCONNECTED} or
- * {@link BluetoothProfile#STATE_CONNECTED}
+ * @hide
*/
- public void onConnectionStateChange(BluetoothDevice device, int status,
- int newState) {
+ @Override
+ public void onPhyUpdate(BluetoothDevice device, int txPhy, int rxPhy, int status) {
}
/**
- * Indicates whether a local service has been added successfully.
- *
- * @param status Returns {@link BluetoothGatt#GATT_SUCCESS} if the service
- * was added successfully.
- * @param service The service that has been added
+ * @hide
*/
- public void onServiceAdded(int status, BluetoothGattService service) {
- }
-
- /**
- * A remote client has requested to read a local characteristic.
- *
- * <p>An application must call {@link BluetoothGattServer#sendResponse}
- * to complete the request.
- *
- * @param device The remote device that has requested the read operation
- * @param requestId The Id of the request
- * @param offset Offset into the value of the characteristic
- * @param characteristic Characteristic to be read
- */
- public void onCharacteristicReadRequest(BluetoothDevice device, int requestId,
- int offset, BluetoothGattCharacteristic characteristic) {
- }
-
- /**
- * A remote client has requested to write to a local characteristic.
- *
- * <p>An application must call {@link BluetoothGattServer#sendResponse}
- * to complete the request.
- *
- * @param device The remote device that has requested the write operation
- * @param requestId The Id of the request
- * @param characteristic Characteristic to be written to.
- * @param preparedWrite true, if this write operation should be queued for
- * later execution.
- * @param responseNeeded true, if the remote device requires a response
- * @param offset The offset given for the value
- * @param value The value the client wants to assign to the characteristic
- */
- public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId,
- BluetoothGattCharacteristic characteristic,
- boolean preparedWrite, boolean responseNeeded,
- int offset, byte[] value) {
- }
-
- /**
- * A remote client has requested to read a local descriptor.
- *
- * <p>An application must call {@link BluetoothGattServer#sendResponse}
- * to complete the request.
- *
- * @param device The remote device that has requested the read operation
- * @param requestId The Id of the request
- * @param offset Offset into the value of the characteristic
- * @param descriptor Descriptor to be read
- */
- public void onDescriptorReadRequest(BluetoothDevice device, int requestId,
- int offset, BluetoothGattDescriptor descriptor) {
- }
-
- /**
- * A remote client has requested to write to a local descriptor.
- *
- * <p>An application must call {@link BluetoothGattServer#sendResponse}
- * to complete the request.
- *
- * @param device The remote device that has requested the write operation
- * @param requestId The Id of the request
- * @param descriptor Descriptor to be written to.
- * @param preparedWrite true, if this write operation should be queued for
- * later execution.
- * @param responseNeeded true, if the remote device requires a response
- * @param offset The offset given for the value
- * @param value The value the client wants to assign to the descriptor
- */
- public void onDescriptorWriteRequest(BluetoothDevice device, int requestId,
- BluetoothGattDescriptor descriptor,
- boolean preparedWrite, boolean responseNeeded,
- int offset, byte[] value) {
- }
-
- /**
- * Execute all pending write operations for this device.
- *
- * <p>An application must call {@link BluetoothGattServer#sendResponse}
- * to complete the request.
- *
- * @param device The remote device that has requested the write operations
- * @param requestId The Id of the request
- * @param execute Whether the pending writes should be executed (true) or
- * cancelled (false)
- */
- public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) {
- }
-
- /**
- * Callback invoked when a notification or indication has been sent to
- * a remote device.
- *
- * <p>When multiple notifications are to be sent, an application must
- * wait for this callback to be received before sending additional
- * notifications.
- *
- * @param device The remote device the notification has been sent to
- * @param status {@link BluetoothGatt#GATT_SUCCESS} if the operation was successful
- */
- public void onNotificationSent(BluetoothDevice device, int status) {
- }
-
- /**
- * Callback indicating the MTU for a given device connection has changed.
- *
- * <p>This callback will be invoked if a remote client has requested to change
- * the MTU for a given connection.
- *
- * @param device The remote device that requested the MTU change
- * @param mtu The new MTU size
- */
- public void onMtuChanged(BluetoothDevice device, int mtu) {
+ @Override
+ public void onPhyRead(BluetoothDevice device, int txPhy, int rxPhy, int status) {
}
}
diff --git a/core/java/android/bluetooth/BluetoothGattServerCallbackExt.java b/core/java/android/bluetooth/BluetoothGattServerCallbackExt.java
new file mode 100644
index 0000000..455cce0
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothGattServerCallbackExt.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.bluetooth.BluetoothDevice;
+
+/**
+ * This abstract class is used to implement {@link BluetoothGattServer} callbacks.
+ */
+public abstract class BluetoothGattServerCallbackExt {
+
+ /**
+ * Callback indicating when a remote device has been connected or disconnected.
+ *
+ * @param device Remote device that has been connected or disconnected.
+ * @param status Status of the connect or disconnect operation.
+ * @param newState Returns the new connection state. Can be one of
+ * {@link BluetoothProfile#STATE_DISCONNECTED} or
+ * {@link BluetoothProfile#STATE_CONNECTED}
+ */
+ public void onConnectionStateChange(BluetoothDevice device, int status,
+ int newState) {
+ }
+
+ /**
+ * Indicates whether a local service has been added successfully.
+ *
+ * @param status Returns {@link BluetoothGatt#GATT_SUCCESS} if the service
+ * was added successfully.
+ * @param service The service that has been added
+ */
+ public void onServiceAdded(int status, BluetoothGattService service) {
+ }
+
+ /**
+ * A remote client has requested to read a local characteristic.
+ *
+ * <p>An application must call {@link BluetoothGattServer#sendResponse}
+ * to complete the request.
+ *
+ * @param device The remote device that has requested the read operation
+ * @param requestId The Id of the request
+ * @param offset Offset into the value of the characteristic
+ * @param characteristic Characteristic to be read
+ */
+ public void onCharacteristicReadRequest(BluetoothDevice device, int requestId,
+ int offset, BluetoothGattCharacteristic characteristic) {
+ }
+
+ /**
+ * A remote client has requested to write to a local characteristic.
+ *
+ * <p>An application must call {@link BluetoothGattServer#sendResponse}
+ * to complete the request.
+ *
+ * @param device The remote device that has requested the write operation
+ * @param requestId The Id of the request
+ * @param characteristic Characteristic to be written to.
+ * @param preparedWrite true, if this write operation should be queued for
+ * later execution.
+ * @param responseNeeded true, if the remote device requires a response
+ * @param offset The offset given for the value
+ * @param value The value the client wants to assign to the characteristic
+ */
+ public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId,
+ BluetoothGattCharacteristic characteristic,
+ boolean preparedWrite, boolean responseNeeded,
+ int offset, byte[] value) {
+ }
+
+ /**
+ * A remote client has requested to read a local descriptor.
+ *
+ * <p>An application must call {@link BluetoothGattServer#sendResponse}
+ * to complete the request.
+ *
+ * @param device The remote device that has requested the read operation
+ * @param requestId The Id of the request
+ * @param offset Offset into the value of the characteristic
+ * @param descriptor Descriptor to be read
+ */
+ public void onDescriptorReadRequest(BluetoothDevice device, int requestId,
+ int offset, BluetoothGattDescriptor descriptor) {
+ }
+
+ /**
+ * A remote client has requested to write to a local descriptor.
+ *
+ * <p>An application must call {@link BluetoothGattServer#sendResponse}
+ * to complete the request.
+ *
+ * @param device The remote device that has requested the write operation
+ * @param requestId The Id of the request
+ * @param descriptor Descriptor to be written to.
+ * @param preparedWrite true, if this write operation should be queued for
+ * later execution.
+ * @param responseNeeded true, if the remote device requires a response
+ * @param offset The offset given for the value
+ * @param value The value the client wants to assign to the descriptor
+ */
+ public void onDescriptorWriteRequest(BluetoothDevice device, int requestId,
+ BluetoothGattDescriptor descriptor,
+ boolean preparedWrite, boolean responseNeeded,
+ int offset, byte[] value) {
+ }
+
+ /**
+ * Execute all pending write operations for this device.
+ *
+ * <p>An application must call {@link BluetoothGattServer#sendResponse}
+ * to complete the request.
+ *
+ * @param device The remote device that has requested the write operations
+ * @param requestId The Id of the request
+ * @param execute Whether the pending writes should be executed (true) or
+ * cancelled (false)
+ */
+ public void onExecuteWrite(BluetoothDevice device, int requestId, boolean execute) {
+ }
+
+ /**
+ * Callback invoked when a notification or indication has been sent to
+ * a remote device.
+ *
+ * <p>When multiple notifications are to be sent, an application must
+ * wait for this callback to be received before sending additional
+ * notifications.
+ *
+ * @param device The remote device the notification has been sent to
+ * @param status {@link BluetoothGatt#GATT_SUCCESS} if the operation was successful
+ */
+ public void onNotificationSent(BluetoothDevice device, int status) {
+ }
+
+ /**
+ * Callback indicating the MTU for a given device connection has changed.
+ *
+ * <p>This callback will be invoked if a remote client has requested to change
+ * the MTU for a given connection.
+ *
+ * @param device The remote device that requested the MTU change
+ * @param mtu The new MTU size
+ */
+ public void onMtuChanged(BluetoothDevice device, int mtu) {
+ }
+
+ /**
+ * Callback triggered as result of {@link BluetoothGattServer#setPreferredPhy}, or as a result
+ * of remote device changing the PHY.
+ *
+ * @param device The remote device
+ * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M},
+ * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}
+ * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M},
+ * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}
+ * @param status status of the operation
+ */
+ public void onPhyUpdate(BluetoothDevice device, int txPhy, int rxPhy, int status) {
+ }
+
+ /**
+ * Callback triggered as result of {@link BluetoothGattServer#readPhy}
+ *
+ * @param device The remote device that requested the PHY read
+ * @param txPhy the transmitter PHY in use. One of {@link BluetoothDevice#PHY_LE_1M},
+ * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}
+ * @param rxPhy the receiver PHY in use. One of {@link BluetoothDevice#PHY_LE_1M},
+ * {@link BluetoothDevice#PHY_LE_2M}, and {@link BluetoothDevice#PHY_LE_CODED}
+ * @param status status of the operation
+ */
+ public void onPhyRead(BluetoothDevice device, int txPhy, int rxPhy, int status) {
+ }
+}
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index 53fef2a..76ca554 100644
--- a/core/java/android/bluetooth/IBluetooth.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -104,6 +104,10 @@
boolean isOffloadedFilteringSupported();
boolean isOffloadedScanBatchingSupported();
boolean isActivityAndEnergyReportingSupported();
+ boolean isLe2MPhySupported();
+ boolean isLeCodedPhySupported();
+ boolean isLeExtendedAdvertisingSupported();
+ boolean isLePeriodicAdvertisingSupported();
BluetoothActivityEnergyInfo reportActivityInfo();
/**
diff --git a/core/java/android/bluetooth/IBluetoothGatt.aidl b/core/java/android/bluetooth/IBluetoothGatt.aidl
index aa2291e..33fedc7 100644
--- a/core/java/android/bluetooth/IBluetoothGatt.aidl
+++ b/core/java/android/bluetooth/IBluetoothGatt.aidl
@@ -20,15 +20,20 @@
import android.bluetooth.BluetoothGattService;
import android.bluetooth.le.AdvertiseSettings;
import android.bluetooth.le.AdvertiseData;
+import android.bluetooth.le.AdvertisingSetParameters;
+import android.bluetooth.le.PeriodicAdvertisingParameters;
import android.bluetooth.le.ScanFilter;
+import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.bluetooth.le.ResultStorageDescriptor;
import android.os.ParcelUuid;
import android.os.WorkSource;
-import android.bluetooth.IBluetoothGattCallback;
-import android.bluetooth.IBluetoothGattServerCallback;
+import android.bluetooth.IBluetoothGattCallbackExt;
+import android.bluetooth.IBluetoothGattServerCallbackExt;
import android.bluetooth.le.IAdvertiserCallback;
+import android.bluetooth.le.IAdvertisingSetCallback;
+import android.bluetooth.le.IPeriodicAdvertisingCallback;
import android.bluetooth.le.IScannerCallback;
/**
@@ -53,10 +58,29 @@
in AdvertiseSettings settings);
void stopMultiAdvertising(in int advertiserId);
- void registerClient(in ParcelUuid appId, in IBluetoothGattCallback callback);
+ void startAdvertisingSet(in AdvertisingSetParameters parameters, in AdvertiseData advertiseData,
+ in AdvertiseData scanResponse, in PeriodicAdvertisingParameters periodicParameters,
+ in AdvertiseData periodicData, in IAdvertisingSetCallback callback);
+ void stopAdvertisingSet(in IAdvertisingSetCallback callback);
+
+ void enableAdverisingSet(in int advertiserId, in boolean enable);
+ void setAdvertisingData(in int advertiserId, in AdvertiseData data);
+ void setScanResponseData(in int advertiserId, in AdvertiseData data);
+ void setAdvertisingParameters(in int advertiserId, in AdvertisingSetParameters parameters);
+ void setPeriodicAdvertisingParameters(in int advertiserId, in PeriodicAdvertisingParameters parameters);
+ void setPeriodicAdvertisingData(in int advertiserId, in AdvertiseData data);
+ void periodicAdvertisingEnable(in int advertiserId, in boolean enable);
+
+ void registerSync(in ScanResult scanResult, in int skip, in int timeout, in IPeriodicAdvertisingCallback callback);
+ void unregisterSync(in IPeriodicAdvertisingCallback callback);
+
+ void registerClient(in ParcelUuid appId, in IBluetoothGattCallbackExt callback);
+
void unregisterClient(in int clientIf);
- void clientConnect(in int clientIf, in String address, in boolean isDirect, in int transport);
+ void clientConnect(in int clientIf, in String address, in boolean isDirect, in int transport, in int phy);
void clientDisconnect(in int clientIf, in String address);
+ void clientSetPreferredPhy(in int clientIf, in String address, in int txPhy, in int rxPhy, in int phyOptions);
+ void clientReadPhy(in int clientIf, in String address);
void refreshDevice(in int clientIf, in String address);
void discoverServices(in int clientIf, in String address);
void readCharacteristic(in int clientIf, in String address, in int handle, in int authReq);
@@ -72,10 +96,12 @@
void configureMTU(in int clientIf, in String address, in int mtu);
void connectionParameterUpdate(in int clientIf, in String address, in int connectionPriority);
- void registerServer(in ParcelUuid appId, in IBluetoothGattServerCallback callback);
+ void registerServer(in ParcelUuid appId, in IBluetoothGattServerCallbackExt callback);
void unregisterServer(in int serverIf);
void serverConnect(in int serverIf, in String address, in boolean isDirect, in int transport);
void serverDisconnect(in int serverIf, in String address);
+ void serverSetPreferredPhy(in int clientIf, in String address, in int txPhy, in int rxPhy, in int phyOptions);
+ void serverReadPhy(in int clientIf, in String address);
void addService(in int serverIf, in BluetoothGattService service);
void removeService(in int serverIf, in int handle);
void clearServices(in int serverIf);
diff --git a/core/java/android/bluetooth/IBluetoothGattCallback.aidl b/core/java/android/bluetooth/IBluetoothGattCallbackExt.aidl
similarity index 88%
rename from core/java/android/bluetooth/IBluetoothGattCallback.aidl
rename to core/java/android/bluetooth/IBluetoothGattCallbackExt.aidl
index 72cb618..736f4b2 100644
--- a/core/java/android/bluetooth/IBluetoothGattCallback.aidl
+++ b/core/java/android/bluetooth/IBluetoothGattCallbackExt.aidl
@@ -22,10 +22,12 @@
* Callback definitions for interacting with BLE / GATT
* @hide
*/
-oneway interface IBluetoothGattCallback {
+oneway interface IBluetoothGattCallbackExt {
void onClientRegistered(in int status, in int clientIf);
void onClientConnectionState(in int status, in int clientIf,
in boolean connected, in String address);
+ void onPhyUpdate(in String address, in int txPhy, in int rxPhy, in int status);
+ void onPhyRead(in String address, in int txPhy, in int rxPhy, in int status);
void onSearchComplete(in String address, in List<BluetoothGattService> services, in int status);
void onCharacteristicRead(in String address, in int status, in int handle, in byte[] value);
void onCharacteristicWrite(in String address, in int status, in int handle);
diff --git a/core/java/android/bluetooth/IBluetoothGattServerCallback.aidl b/core/java/android/bluetooth/IBluetoothGattServerCallbackExt.aidl
similarity index 87%
rename from core/java/android/bluetooth/IBluetoothGattServerCallback.aidl
rename to core/java/android/bluetooth/IBluetoothGattServerCallbackExt.aidl
index 1a924fb..091ffb3 100644
--- a/core/java/android/bluetooth/IBluetoothGattServerCallback.aidl
+++ b/core/java/android/bluetooth/IBluetoothGattServerCallbackExt.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2013 The Android Open Source Project
+ * Copyright (C) 2017 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.
@@ -21,7 +21,7 @@
* Callback definitions for interacting with BLE / GATT
* @hide
*/
-oneway interface IBluetoothGattServerCallback {
+oneway interface IBluetoothGattServerCallbackExt {
void onServerRegistered(in int status, in int serverIf);
void onServerConnectionState(in int status, in int serverIf,
in boolean connected, in String address);
@@ -40,4 +40,6 @@
void onExecuteWrite(in String address, in int transId, in boolean execWrite);
void onNotificationSent(in String address, in int status);
void onMtuChanged(in String address, in int mtu);
+ void onPhyUpdate(in String address, in int txPhy, in int rxPhy, in int status);
+ void onPhyRead(in String address, in int txPhy, in int rxPhy, in int status);
}
diff --git a/core/java/android/bluetooth/le/AdvertisingSet.java b/core/java/android/bluetooth/le/AdvertisingSet.java
new file mode 100644
index 0000000..1524022
--- /dev/null
+++ b/core/java/android/bluetooth/le/AdvertisingSet.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2017 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.le;
+
+import android.bluetooth.IBluetoothGatt;
+import android.bluetooth.IBluetoothManager;
+import android.bluetooth.le.IAdvertisingSetCallback;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * This class provides a way to control single Bluetooth LE advertising instance.
+ * <p>
+ * To get an instance of {@link AdvertisingSet}, call the
+ * {@link BluetoothLeAdvertiser#startAdvertisingSet} method.
+ * <p>
+ * <b>Note:</b> Most of the methods here require {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+ * permission.
+ *
+ * @see AdvertiseData
+ */
+public final class AdvertisingSet {
+ private static final String TAG = "AdvertisingSet";
+
+ private final IBluetoothGatt gatt;
+ private int advertiserId;
+
+ /* package */ AdvertisingSet(int advertiserId,
+ IBluetoothManager bluetoothManager) {
+ this.advertiserId = advertiserId;
+
+ try {
+ this.gatt = bluetoothManager.getBluetoothGatt();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to get Bluetooth gatt - ", e);
+ throw new IllegalStateException("Failed to get Bluetooth");
+ }
+ }
+
+ /* package */ void setAdvertiserId(int advertiserId) {
+ this.advertiserId = advertiserId;
+ }
+
+ /**
+ * Enables Advertising. This method returns immediately, the operation status is
+ * delivered
+ * through {@code callback.onAdvertisingEnabled()}.
+ * <p>
+ * Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+ *
+ */
+ public void enableAdvertising(boolean enable) {
+ try {
+ gatt.enableAdverisingSet(this.advertiserId, enable);
+ } catch (RemoteException e) {
+ Log.e(TAG, "remote exception - ", e);
+ }
+ }
+
+ /**
+ * Set/update data being Advertised. Make sure that data doesn't exceed the size limit for
+ * specified AdvertisingSetParameters. This method returns immediately, the operation status is
+ * delivered through {@code callback.onAdvertisingDataSet()}.
+ * <p>
+ * Advertising data must be empty if non-legacy scannable advertising is used.
+ */
+ public void setAdvertisingData(AdvertiseData data) {
+ try {
+ gatt.setAdvertisingData(this.advertiserId, data);
+ } catch (RemoteException e) {
+ Log.e(TAG, "remote exception - ", e);
+ }
+ }
+
+ /**
+ * Set/update scan response data. Make sure that data doesn't exceed the size limit for
+ * specified AdvertisingSetParameters. This method returns immediately, the operation status
+ * is delivered through {@code callback.onScanResponseDataSet()}.
+ */
+ public void setScanResponseData(AdvertiseData data) {
+ try {
+ gatt.setScanResponseData(this.advertiserId, data);
+ } catch (RemoteException e) {
+ Log.e(TAG, "remote exception - ", e);
+ }
+ }
+
+ /**
+ * Update advertising parameters associated with this AdvertisingSet. Must be called when
+ * advertising is not active. This method returns immediately, the operation status is delivered
+ * through {@code callback.onAdvertisingParametersUpdated}.
+ */
+ public void setAdvertisingParameters(AdvertisingSetParameters parameters) {
+ try {
+ gatt.setAdvertisingParameters(this.advertiserId, parameters);
+ } catch (RemoteException e) {
+ Log.e(TAG, "remote exception - ", e);
+ }
+ }
+
+ /**
+ * Update periodic advertising parameters associated with this set. Must be called when
+ * periodic advertising is not enabled. This method returns immediately, the operation
+ * status is delivered through {@code callback.onPeriodicAdvertisingParametersUpdated()}.
+ */
+ public void setPeriodicAdvertisingParameters(PeriodicAdvertisingParameters parameters) {
+ try {
+ gatt.setPeriodicAdvertisingParameters(this.advertiserId, parameters);
+ } catch (RemoteException e) {
+ Log.e(TAG, "remote exception - ", e);
+ }
+ }
+
+ /**
+ * Used to set periodic advertising data, must be called after setPeriodicAdvertisingParameters,
+ * or after advertising was started with periodic advertising data set. This method returns
+ * immediately, the operation status is delivered through
+ * {@code callback.onPeriodicAdvertisingDataSet()}.
+ */
+ public void setPeriodicAdvertisingData(AdvertiseData data) {
+ try {
+ gatt.setPeriodicAdvertisingData(this.advertiserId, data);
+ } catch (RemoteException e) {
+ Log.e(TAG, "remote exception - ", e);
+ }
+ }
+
+ /**
+ * Used to enable/disable periodic advertising. This method returns immediately, the operation
+ * status is delivered through {@code callback.onPeriodicAdvertisingEnable()}.
+ */
+ public void periodicAdvertisingEnable(boolean enable) {
+ try {
+ gatt.periodicAdvertisingEnable(this.advertiserId, enable);
+ } catch (RemoteException e) {
+ Log.e(TAG, "remote exception - ", e);
+ }
+ }
+
+ /**
+ * Returns advertiserId associated with thsi advertising set.
+ *
+ * @hide
+ */
+ public int getAdvertiserId(){
+ return advertiserId;
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/bluetooth/le/AdvertisingSetCallback.java b/core/java/android/bluetooth/le/AdvertisingSetCallback.java
new file mode 100644
index 0000000..ceed8d9
--- /dev/null
+++ b/core/java/android/bluetooth/le/AdvertisingSetCallback.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2017 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.le;
+
+import android.bluetooth.BluetoothDevice;
+
+/**
+ * Bluetooth LE advertising set callbacks, used to deliver advertising operation
+ * status.
+ */
+public abstract class AdvertisingSetCallback {
+
+ /**
+ * The requested operation was successful.
+ */
+ public static final int ADVERTISE_SUCCESS = 0;
+
+ /**
+ * Failed to start advertising as the advertise data to be broadcasted is too
+ * large.
+ */
+ public static final int ADVERTISE_FAILED_DATA_TOO_LARGE = 1;
+
+ /**
+ * Failed to start advertising because no advertising instance is available.
+ */
+ public static final int ADVERTISE_FAILED_TOO_MANY_ADVERTISERS = 2;
+
+ /**
+ * Failed to start advertising as the advertising is already started.
+ */
+ public static final int ADVERTISE_FAILED_ALREADY_STARTED = 3;
+
+ /**
+ * Operation failed due to an internal error.
+ */
+ public static final int ADVERTISE_FAILED_INTERNAL_ERROR = 4;
+
+ /**
+ * This feature is not supported on this platform.
+ */
+ public static final int ADVERTISE_FAILED_FEATURE_UNSUPPORTED = 5;
+
+ /**
+ * Callback triggered in response to {@link BluetoothLeAdvertiser#startAdvertisingSet}
+ * indicating result of the operation. If status is ADVERTISE_SUCCESS, then advertisingSet
+ * contains the started set and it is advertising. If error occured, advertisingSet is
+ * null, and status will be set to proper error code.
+ *
+ * @param advertisingSet The advertising set that was started or null if error.
+ * @param status Status of the operation.
+ */
+ public void onAdvertisingSetStarted(AdvertisingSet advertisingSet, int status) {}
+
+ /**
+ * Callback triggered in response to {@link BluetoothLeAdvertiser#stopAdvertisingSet}
+ * indicating advertising set is stopped.
+ *
+ * @param advertisingSet The advertising set.
+ */
+ public void onAdvertisingSetStopped(AdvertisingSet advertisingSet) {}
+
+ /**
+ * Callback triggered in response to {@link BluetoothLeAdvertiser#startAdvertisingSet} indicating
+ * result of the operation. If status is ADVERTISE_SUCCESS, then advertising set is advertising.
+ *
+ * @param advertisingSet The advertising set.
+ * @param status Status of the operation.
+ */
+ public void onAdvertisingEnabled(AdvertisingSet advertisingSet, boolean enable, int status) {}
+
+ /**
+ * Callback triggered in response to {@link AdvertisingSet#setAdvertisingData} indicating
+ * result of the operation. If status is ADVERTISE_SUCCESS, then data was changed.
+ *
+ * @param advertisingSet The advertising set.
+ * @param status Status of the operation.
+ */
+ public void onAdvertisingDataSet(AdvertisingSet advertisingSet, int status) {}
+
+ /**
+ * Callback triggered in response to {@link AdvertisingSet#setAdvertisingData} indicating
+ * result of the operation.
+ *
+ * @param advertisingSet The advertising set.
+ * @param status Status of the operation.
+ */
+ public void onScanResponseDataSet(AdvertisingSet advertisingSet, int status) {}
+
+ /**
+ * Callback triggered in response to {@link AdvertisingSet#setAdvertisingParameters}
+ * indicating result of the operation.
+ *
+ * @param advertisingSet The advertising set.
+ * @param status Status of the operation.
+ */
+ public void onAdvertisingParametersUpdated(AdvertisingSet advertisingSet,
+ int status) {}
+
+ /**
+ * Callback triggered in response to {@link AdvertisingSet#setPeriodicAdvertisingParameters}
+ * indicating result of the operation.
+ *
+ * @param advertisingSet The advertising set.
+ * @param status Status of the operation.
+ */
+ public void
+ onPeriodicAdvertisingParametersUpdated(AdvertisingSet advertisingSet,
+ int status) {}
+
+ /**
+ * Callback triggered in response to {@link AdvertisingSet#setPeriodicAdvertisingData}
+ * indicating result of the operation.
+ *
+ * @param advertisingSet The advertising set.
+ * @param status Status of the operation.
+ */
+ public void onPeriodicAdvertisingDataSet(AdvertisingSet advertisingSet,
+ int status) {}
+
+ /**
+ * Callback triggered in response to {@link AdvertisingSet#periodicAdvertisingEnable}
+ * indicating result of the operation.
+ *
+ * @param advertisingSet The advertising set.
+ * @param status Status of the operation.
+ */
+ public void onPeriodicAdvertisingEnable(AdvertisingSet advertisingSet, boolean enable,
+ int status) {}
+}
\ No newline at end of file
diff --git a/core/java/android/bluetooth/le/AdvertisingSetParameters.aidl b/core/java/android/bluetooth/le/AdvertisingSetParameters.aidl
new file mode 100644
index 0000000..39034a0
--- /dev/null
+++ b/core/java/android/bluetooth/le/AdvertisingSetParameters.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2017 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.le;
+
+parcelable AdvertisingSetParameters;
diff --git a/core/java/android/bluetooth/le/AdvertisingSetParameters.java b/core/java/android/bluetooth/le/AdvertisingSetParameters.java
new file mode 100644
index 0000000..03a01e1
--- /dev/null
+++ b/core/java/android/bluetooth/le/AdvertisingSetParameters.java
@@ -0,0 +1,409 @@
+/*
+ * Copyright (C) 2017 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.le;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * The {@link AdvertisingSetParameters} provide a way to adjust advertising
+ * preferences for each
+ * Bluetooth LE advertising set. Use {@link AdvertisingSetParameters.Builder} to
+ * create an
+ * instance of this class.
+ */
+public final class AdvertisingSetParameters implements Parcelable {
+
+ /**
+ * 1M advertiser PHY.
+ */
+ public static final int PHY_LE_1M = 1;
+
+ /**
+ * 2M advertiser PHY.
+ */
+ public static final int PHY_LE_2M = 2;
+
+ /**
+ * LE Coded advertiser PHY.
+ */
+ public static final int PHY_LE_CODED = 3;
+
+ /**
+ * Advertise on low frequency, around every 1000ms. This is the default and
+ * preferred advertising mode as it consumes the least power.
+ */
+ public static final int INTERVAL_LOW = 1600;
+
+ /**
+ * Advertise on medium frequency, around every 250ms. This is balanced
+ * between advertising frequency and power consumption.
+ */
+ public static final int INTERVAL_MEDIUM = 400;
+
+ /**
+ * Perform high frequency, low latency advertising, around every 100ms. This
+ * has the highest power consumption and should not be used for continuous
+ * background advertising.
+ */
+ public static final int INTERVAL_HIGH = 160;
+
+ /**
+ * Minimum value for advertising interval.
+ */
+ public static final int INTERVAL_MIN = 160;
+
+ /**
+ * Maximum value for advertising interval.
+ */
+ public static final int INTERVAL_MAX = 16777215;
+
+ /**
+ * Advertise using the lowest transmission (TX) power level. Low transmission
+ * power can be used to restrict the visibility range of advertising packets.
+ */
+ public static final int TX_POWER_ULTRA_LOW = -21;
+
+ /**
+ * Advertise using low TX power level.
+ */
+ public static final int TX_POWER_LOW = -15;
+
+ /**
+ * Advertise using medium TX power level.
+ */
+ public static final int TX_POWER_MEDIUM = -7;
+
+ /**
+ * Advertise using high TX power level. This corresponds to largest visibility
+ * range of the advertising packet.
+ */
+ public static final int TX_POWER_HIGH = 1;
+
+ /**
+ * Minimum value for TX power.
+ */
+ public static final int TX_POWER_MIN = -127;
+
+ /**
+ * Maximum value for TX power.
+ */
+ public static final int TX_POWER_MAX = 1;
+
+ /**
+ * The maximum limited advertisement duration as specified by the Bluetooth
+ * SIG
+ */
+ private static final int LIMITED_ADVERTISING_MAX_MILLIS = 180 * 1000;
+
+ private final boolean isLegacy;
+ private final boolean isAnonymous;
+ private final boolean includeTxPower;
+ private final int primaryPhy;
+ private final int secondaryPhy;
+ private final boolean connectable;
+ private final int interval;
+ private final int txPowerLevel;
+ private final int timeoutMillis;
+
+ private AdvertisingSetParameters(boolean connectable, boolean isLegacy,
+ boolean isAnonymous, boolean includeTxPower,
+ int primaryPhy, int secondaryPhy,
+ int interval, int txPowerLevel,
+ int timeoutMillis) {
+ this.connectable = connectable;
+ this.isLegacy = isLegacy;
+ this.isAnonymous = isAnonymous;
+ this.includeTxPower = includeTxPower;
+ this.primaryPhy = primaryPhy;
+ this.secondaryPhy = secondaryPhy;
+ this.interval = interval;
+ this.txPowerLevel = txPowerLevel;
+ this.timeoutMillis = timeoutMillis;
+ }
+
+ private AdvertisingSetParameters(Parcel in) {
+ connectable = in.readInt() != 0 ? true : false;
+ isLegacy = in.readInt() != 0 ? true : false;
+ isAnonymous = in.readInt() != 0 ? true : false;
+ includeTxPower = in.readInt() != 0 ? true : false;
+ primaryPhy = in.readInt();
+ secondaryPhy = in.readInt();
+ interval = in.readInt();
+ txPowerLevel = in.readInt();
+ timeoutMillis = in.readInt();
+ }
+
+ /**
+ * Returns whether the advertisement will be connectable.
+ */
+ public boolean isConnectable() { return connectable; }
+
+ /**
+ * Returns whether the legacy advertisement will be used.
+ */
+ public boolean isLegacy() { return isLegacy; }
+
+ /**
+ * Returns whether the advertisement will be anonymous.
+ */
+ public boolean isAnonymous() { return isAnonymous; }
+
+ /**
+ * Returns whether the TX Power will be included.
+ */
+ public boolean includeTxPower() { return includeTxPower; }
+
+ /**
+ * Returns the primary advertising phy.
+ */
+ public int getPrimaryPhy() { return primaryPhy; }
+
+ /**
+ * Returns the secondary advertising phy.
+ */
+ public int getSecondaryPhy() { return secondaryPhy; }
+
+ /**
+ * Returns the advertising interval.
+ */
+ public int getInterval() { return interval; }
+
+ /**
+ * Returns the TX power level for advertising.
+ */
+ public int getTxPowerLevel() { return txPowerLevel; }
+
+ /**
+ * Returns the advertising time limit in milliseconds.
+ */
+ public int getTimeout() { return timeoutMillis; }
+
+ @Override
+ public String toString() {
+ return "AdvertisingSetParameters [connectable=" + connectable
+ + ", isLegacy=" + isLegacy
+ + ", isAnonymous=" + isAnonymous
+ + ", includeTxPower=" + includeTxPower
+ + ", primaryPhy=" + primaryPhy
+ + ", secondaryPhy=" + secondaryPhy
+ + ", interval=" + interval
+ + ", txPowerLevel=" + txPowerLevel
+ + ", timeoutMillis=" + timeoutMillis + "]";
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(connectable ? 1 : 0);
+ dest.writeInt(isLegacy ? 1 : 0);
+ dest.writeInt(isAnonymous ? 1 : 0);
+ dest.writeInt(includeTxPower ? 1 : 0);
+ dest.writeInt(primaryPhy);
+ dest.writeInt(secondaryPhy);
+ dest.writeInt(interval);
+ dest.writeInt(txPowerLevel);
+ dest.writeInt(timeoutMillis);
+ }
+
+ public static final Parcelable.Creator<AdvertisingSetParameters> CREATOR =
+ new Creator<AdvertisingSetParameters>() {
+ @Override
+ public AdvertisingSetParameters[] newArray(int size) {
+ return new AdvertisingSetParameters[size];
+ }
+
+ @Override
+ public AdvertisingSetParameters createFromParcel(Parcel in) {
+ return new AdvertisingSetParameters(in);
+ }
+ };
+
+ /**
+ * Builder class for {@link AdvertisingSetParameters}.
+ */
+ public static final class Builder {
+
+ private boolean connectable = true;
+ private boolean isLegacy = false;
+ private boolean isAnonymous = false;
+ private boolean includeTxPower = false;
+ private int primaryPhy = PHY_LE_1M;
+ private int secondaryPhy = PHY_LE_1M;
+ private int interval = INTERVAL_LOW;
+ private int txPowerLevel = TX_POWER_MEDIUM;
+ private int timeoutMillis = 0;
+
+ /**
+ * Set whether the advertisement type should be connectable or
+ * non-connectable.
+ * Legacy advertisements can be both connectable and scannable. Other
+ * advertisements can be connectable only if not scannable.
+ * @param connectable Controls whether the advertisment type will be
+ * connectable (true) or non-connectable (false).
+ */
+ public Builder setConnectable(boolean connectable) {
+ this.connectable = connectable;
+ return this;
+ }
+
+ /**
+ * When set to true, advertising set will advertise 4.x Spec compliant
+ * advertisements.
+ *
+ * @param isLegacy wether legacy advertising mode should be used.
+ */
+ public Builder setLegacyMode(boolean isLegacy) {
+ this.isLegacy = isLegacy;
+ return this;
+ }
+
+ /**
+ * Set wether advertiser address should be ommited from all packets. If this
+ * mode is used, periodic advertising can't be enabled for this set.
+ *
+ * This is used only if legacy mode is not used.
+ *
+ * @param isAnonymous wether anonymous advertising should be used.
+ */
+ public Builder setAnonymouus(boolean isAnonymous) {
+ this.isAnonymous = isAnonymous;
+ return this;
+ }
+
+ /**
+ * Set wether TX power should be included in the extended header.
+ *
+ * This is used only if legacy mode is not used.
+ *
+ * @param includeTxPower wether TX power should be included in extended
+ * header
+ */
+ public Builder setIncludeTxPower(boolean includeTxPower) {
+ this.includeTxPower = includeTxPower;
+ return this;
+ }
+
+ /**
+ * Set the primary physical channel used for this advertising set.
+ *
+ * This is used only if legacy mode is not used.
+ *
+ * @param primaryPhy Primary advertising physical channel, can only be
+ * {@link AdvertisingSetParameters#PHY_LE_1M} or
+ * {@link AdvertisingSetParameters#PHY_LE_CODED}.
+ * @throws IllegalArgumentException If the primaryPhy is invalid.
+ */
+ public Builder setPrimaryPhy(int primaryPhy) {
+ if (primaryPhy != PHY_LE_1M && primaryPhy != PHY_LE_CODED) {
+ throw new IllegalArgumentException("bad primaryPhy " + primaryPhy);
+ }
+ this.primaryPhy = primaryPhy;
+ return this;
+ }
+
+ /**
+ * Set the secondary physical channel used for this advertising set.
+ *
+ * This is used only if legacy mode is not used.
+ *
+ * @param secondaryPhy Secondary advertising physical channel, can only be
+ * one of {@link AdvertisingSetParameters#PHY_LE_1M},
+ * {@link AdvertisingSetParameters#PHY_LE_2M} or
+ * {@link AdvertisingSetParameters#PHY_LE_CODED}.
+ * @throws IllegalArgumentException If the secondaryPhy is invalid.
+ */
+ public Builder setSecondaryPhy(int secondaryPhy) {
+ if (secondaryPhy != PHY_LE_1M && secondaryPhy !=PHY_LE_2M &&
+ secondaryPhy != PHY_LE_CODED) {
+ throw new IllegalArgumentException("bad secondaryPhy " + secondaryPhy);
+ }
+ this.secondaryPhy = secondaryPhy;
+ return this;
+ }
+
+ /**
+ * Set advertising interval.
+ *
+ * @param interval Bluetooth LE Advertising interval, in 0.625ms unit. Valid
+ * range is from 160 (100ms) to 16777215 (10,485.759375 s).
+ * Recommended values are:
+ * {@link AdvertisingSetParameters#INTERVAL_LOW},
+ * {@link AdvertisingSetParameters#INTERVAL_MEDIUM}, or
+ * {@link AdvertisingSetParameters#INTERVAL_HIGH}.
+ * @throws IllegalArgumentException If the interval is invalid.
+ */
+ public Builder setInterval(int interval) {
+ if (interval < INTERVAL_MIN || interval > INTERVAL_MAX) {
+ throw new IllegalArgumentException("unknown interval " + interval);
+ }
+ this.interval = interval;
+ return this;
+ }
+
+ /**
+ * Set the transmission power level for the advertising.
+ * @param txPowerLevel Transmission power of Bluetooth LE Advertising, in
+ * dBm. The valid range is [-127, 1] Recommended values are:
+ * {@link AdvertisingSetParameters#TX_POWER_ULTRA_LOW},
+ * {@link AdvertisingSetParameters#TX_POWER_LOW},
+ * {@link AdvertisingSetParameters#TX_POWER_MEDIUM}, or
+ * {@link AdvertisingSetParameters#TX_POWER_HIGH}.
+ *
+ * @throws IllegalArgumentException If the {@code txPowerLevel} is invalid.
+ */
+ public Builder setTxPowerLevel(int txPowerLevel) {
+ if (txPowerLevel < TX_POWER_MIN || txPowerLevel > TX_POWER_MAX) {
+ throw new IllegalArgumentException("unknown txPowerLevel " +
+ txPowerLevel);
+ }
+ this.txPowerLevel = txPowerLevel;
+ return this;
+ }
+
+ /**
+ * Limit advertising to a given amount of time.
+ * @param timeoutMillis Advertising time limit. May not exceed 180000
+ * milliseconds. A value of 0 will disable the time limit.
+ * @throws IllegalArgumentException If the provided timeout is over 180000
+ * ms.
+ */
+ public Builder setTimeout(int timeoutMillis) {
+ if (timeoutMillis < 0 || timeoutMillis > LIMITED_ADVERTISING_MAX_MILLIS) {
+ throw new IllegalArgumentException("timeoutMillis invalid (must be 0-" +
+ LIMITED_ADVERTISING_MAX_MILLIS +
+ " milliseconds)");
+ }
+ this.timeoutMillis = timeoutMillis;
+ return this;
+ }
+
+ /**
+ * Build the {@link AdvertisingSetParameters} object.
+ */
+ public AdvertisingSetParameters build() {
+ return new AdvertisingSetParameters(connectable, isLegacy, isAnonymous,
+ includeTxPower, primaryPhy,
+ secondaryPhy, interval, txPowerLevel,
+ timeoutMillis);
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
index 94d03e5..e03c947 100644
--- a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
+++ b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
@@ -62,6 +62,9 @@
private BluetoothAdapter mBluetoothAdapter;
private final Map<AdvertiseCallback, AdvertiseCallbackWrapper>
mLeAdvertisers = new HashMap<AdvertiseCallback, AdvertiseCallbackWrapper>();
+ private final Map<AdvertisingSetCallback, IAdvertisingSetCallback>
+ advertisingSetCallbackWrappers = new HashMap<>();
+ private final Map<Integer, AdvertisingSet> advertisingSets = new HashMap<>();
/**
* Use BluetoothAdapter.getLeAdvertiser() instead.
@@ -156,6 +159,93 @@
}
/**
+ * Creates a new advertising set. If operation succeed, device will start advertising. This
+ * method returns immediately, the operation status is delivered through
+ * {@code callback.onNewAdvertisingSet()}.
+ * <p>
+ * @param parameters advertising set parameters.
+ * @param advertiseData Advertisement data to be broadcasted.
+ * @param scanResponse Scan response associated with the advertisement data.
+ * @param periodicData Periodic advertising data.
+ * @param callback Callback for advertising set.
+ */
+ public void startAdvertisingSet(AdvertisingSetParameters parameters,
+ AdvertiseData advertiseData, AdvertiseData scanResponse,
+ PeriodicAdvertisingParameters periodicParameters,
+ AdvertiseData periodicData, AdvertisingSetCallback callback) {
+ startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters,
+ periodicData, callback, new Handler(Looper.getMainLooper()));
+ }
+
+ /**
+ * Creates a new advertising set. If operation succeed, device will start advertising. This
+ * method returns immediately, the operation status is delivered through
+ * {@code callback.onNewAdvertisingSet()}.
+ * <p>
+ * @param parameters advertising set parameters.
+ * @param advertiseData Advertisement data to be broadcasted.
+ * @param scanResponse Scan response associated with the advertisement data.
+ * @param periodicData Periodic advertising data.
+ * @param callback Callback for advertising set.
+ * @param handler thread upon which the callbacks will be invoked.
+ */
+ public void startAdvertisingSet(AdvertisingSetParameters parameters,
+ AdvertiseData advertiseData, AdvertiseData scanResponse,
+ PeriodicAdvertisingParameters periodicParameters,
+ AdvertiseData periodicData, AdvertisingSetCallback callback,
+ Handler handler) {
+ BluetoothLeUtils.checkAdapterStateOn(mBluetoothAdapter);
+
+ if (callback == null) {
+ throw new IllegalArgumentException("callback cannot be null");
+ }
+
+ IBluetoothGatt gatt;
+ try {
+ gatt = mBluetoothManager.getBluetoothGatt();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to get Bluetooth gatt - ", e);
+ throw new IllegalStateException("Failed to get Bluetooth");
+ }
+
+ IAdvertisingSetCallback wrapped = wrap(callback, handler);
+ advertisingSetCallbackWrappers.put(callback, wrapped);
+
+ try {
+ gatt.startAdvertisingSet(parameters, advertiseData, scanResponse, periodicParameters,
+ periodicData, wrapped);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to start advertising set - ", e);
+ throw new IllegalStateException("Failed to start advertising set");
+ }
+ }
+
+ /**
+ * Used to dispose of a {@link AdvertisingSet} object, obtained with {@link
+ * BluetoothLeAdvertiser#startAdvertisingSet}.
+ */
+ public void stopAdvertisingSet(AdvertisingSetCallback callback) {
+ if (callback == null) {
+ throw new IllegalArgumentException("callback cannot be null");
+ }
+
+ IAdvertisingSetCallback wrapped = advertisingSetCallbackWrappers.remove(callback);
+ if (wrapped == null) {
+ throw new IllegalArgumentException(
+ "callback does not represent valid registered callback.");
+ }
+
+ IBluetoothGatt gatt;
+ try {
+ gatt = mBluetoothManager.getBluetoothGatt();
+ gatt.stopAdvertisingSet(wrapped);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to stop advertising - ", e);
+ throw new IllegalStateException("Failed to stop advertising");
+ }
+ }
+
+ /**
* Cleans up advertisers. Should be called when bluetooth is down.
*
* @hide
@@ -219,6 +309,110 @@
return array == null ? 0 : array.length;
}
+ IAdvertisingSetCallback wrap(AdvertisingSetCallback callback, Handler handler) {
+ return new IAdvertisingSetCallback.Stub() {
+ public void onAdvertisingSetStarted(int advertiserId, int status) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (status != AdvertisingSetCallback.ADVERTISE_SUCCESS) {
+ callback.onAdvertisingSetStarted(null, status);
+ advertisingSetCallbackWrappers.remove(callback);
+ return;
+ }
+
+ AdvertisingSet advertisingSet =
+ new AdvertisingSet(advertiserId, mBluetoothManager);
+ advertisingSets.put(advertiserId, advertisingSet);
+ callback.onAdvertisingSetStarted(advertisingSet, status);
+ }
+ });
+ }
+
+ public void onAdvertisingSetStopped(int advertiserId) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ AdvertisingSet advertisingSet = advertisingSets.get(advertiserId);
+ callback.onAdvertisingSetStopped(advertisingSet);
+ advertisingSets.remove(advertiserId);
+ advertisingSetCallbackWrappers.remove(callback);
+ }
+ });
+ }
+
+ public void onAdvertisingEnabled(int advertiserId, boolean enabled, int status) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ AdvertisingSet advertisingSet = advertisingSets.get(advertiserId);
+ callback.onAdvertisingEnabled(advertisingSet, enabled, status);
+ }
+ });
+ }
+
+ public void onAdvertisingDataSet(int advertiserId, int status) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ AdvertisingSet advertisingSet = advertisingSets.get(advertiserId);
+ callback.onAdvertisingDataSet(advertisingSet, status);
+ }
+ });
+ }
+
+ public void onScanResponseDataSet(int advertiserId, int status) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ AdvertisingSet advertisingSet = advertisingSets.get(advertiserId);
+ callback.onScanResponseDataSet(advertisingSet, status);
+ }
+ });
+ }
+
+ public void onAdvertisingParametersUpdated(int advertiserId, int status) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ AdvertisingSet advertisingSet = advertisingSets.get(advertiserId);
+ callback.onAdvertisingParametersUpdated(advertisingSet, status);
+ }
+ });
+ }
+
+ public void onPeriodicAdvertisingParametersUpdated(int advertiserId, int status) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ AdvertisingSet advertisingSet = advertisingSets.get(advertiserId);
+ callback.onPeriodicAdvertisingParametersUpdated(advertisingSet, status);
+ }
+ });
+ }
+
+ public void onPeriodicAdvertisingDataSet(int advertiserId, int status) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ AdvertisingSet advertisingSet = advertisingSets.get(advertiserId);
+ callback.onPeriodicAdvertisingDataSet(advertisingSet, status);
+ }
+ });
+ }
+
+ public void onPeriodicAdvertisingEnable(int advertiserId, boolean enable, int status) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ AdvertisingSet advertisingSet = advertisingSets.get(advertiserId);
+ callback.onPeriodicAdvertisingEnable(advertisingSet, enable, status);
+ }
+ });
+ }
+ };
+ }
+
/**
* Bluetooth GATT interface callbacks for advertising.
*/
diff --git a/core/java/android/bluetooth/le/IAdvertisingSetCallback.aidl b/core/java/android/bluetooth/le/IAdvertisingSetCallback.aidl
new file mode 100644
index 0000000..4b0a111
--- /dev/null
+++ b/core/java/android/bluetooth/le/IAdvertisingSetCallback.aidl
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 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.le;
+
+/**
+ * Callback definitions for interacting with Advertiser
+ * @hide
+ */
+oneway interface IAdvertisingSetCallback {
+ void onAdvertisingSetStarted(in int advertiserId, in int status);
+ void onAdvertisingSetStopped(in int advertiserId);
+ void onAdvertisingEnabled(in int advertiserId, in boolean enable, in int status);
+ void onAdvertisingDataSet(in int advertiserId, in int status);
+ void onScanResponseDataSet(in int advertiserId, in int status);
+ void onAdvertisingParametersUpdated(in int advertiserId, in int status);
+ void onPeriodicAdvertisingParametersUpdated(in int advertiserId, in int status);
+ void onPeriodicAdvertisingDataSet(in int advertiserId, in int status);
+ void onPeriodicAdvertisingEnable(in int advertiserId, in boolean enable, in int status);
+}
diff --git a/core/java/android/bluetooth/le/IPeriodicAdvertisingCallback.aidl b/core/java/android/bluetooth/le/IPeriodicAdvertisingCallback.aidl
new file mode 100644
index 0000000..a76c54d
--- /dev/null
+++ b/core/java/android/bluetooth/le/IPeriodicAdvertisingCallback.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2017 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.le;
+
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.le.PeriodicAdvertisingReport;
+
+/**
+ * Callback definitions for interacting with Periodic Advertising
+ * @hide
+ */
+oneway interface IPeriodicAdvertisingCallback {
+
+ void onSyncEstablished(in int syncHandle, in BluetoothDevice device, in int advertisingSid,
+ in int skip, in int timeout, in int status);
+ void onPeriodicAdvertisingReport(in PeriodicAdvertisingReport report);
+ void onSyncLost(in int syncHandle);
+}
diff --git a/core/java/android/bluetooth/le/PeriodicAdvertisingCallback.java b/core/java/android/bluetooth/le/PeriodicAdvertisingCallback.java
new file mode 100644
index 0000000..6616231
--- /dev/null
+++ b/core/java/android/bluetooth/le/PeriodicAdvertisingCallback.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2017 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.le;
+
+import android.bluetooth.BluetoothDevice;
+
+/**
+ * Bluetooth LE periodic advertising callbacks, used to deliver periodic
+ * advertising operation status.
+ *
+ * @see PeriodicAdvertisingManager#createSync
+ */
+public abstract class PeriodicAdvertisingCallback {
+
+ /**
+ * The requested operation was successful.
+ *
+ * @hide
+ */
+ public static final int SYNC_SUCCESS = 0;
+
+ /**
+ * Sync failed to be established because remote device did not respond.
+ */
+ public static final int SYNC_NO_RESPONSE = 1;
+
+ /**
+ * Sync failed to be established because controller can't support more syncs.
+ */
+ public static final int SYNC_NO_RESOURCES = 2;
+
+
+ /**
+ * Callback when synchronization was established.
+ *
+ * @param syncHandle handle used to identify this synchronization.
+ * @param device remote device.
+ * @param advertisingSid synchronized advertising set id.
+ * @param skip The number of periodic advertising packets that can be skipped
+ * after a successful receive in force. @see PeriodicAdvertisingManager#createSync
+ * @param timeout Synchronization timeout for the periodic advertising in force. One
+ * unit is 10ms. @see PeriodicAdvertisingManager#createSync
+ * @param timeout
+ * @param status operation status.
+ */
+ public void onSyncEstablished(int syncHandle, BluetoothDevice device,
+ int advertisingSid, int skip, int timeout,
+ int status) {}
+
+ /**
+ * Callback when periodic advertising report is received.
+ *
+ * @param report periodic advertising report.
+ */
+ public void onPeriodicAdvertisingReport(PeriodicAdvertisingReport report) {}
+
+ /**
+ * Callback when periodic advertising synchronization was lost.
+ *
+ * @param syncHandle handle used to identify this synchronization.
+ */
+ public void onSyncLost(int syncHandle) {}
+}
diff --git a/core/java/android/bluetooth/le/PeriodicAdvertisingManager.java b/core/java/android/bluetooth/le/PeriodicAdvertisingManager.java
new file mode 100644
index 0000000..12c8a8c
--- /dev/null
+++ b/core/java/android/bluetooth/le/PeriodicAdvertisingManager.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2017 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.le;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.IBluetoothGatt;
+import android.bluetooth.IBluetoothManager;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+/**
+ * This class provides methods to perform periodic advertising related
+ * operations. An application can register for periodic advertisements using
+ * {@link PeriodicAdvertisingManager#registerSync}.
+ * <p>
+ * Use {@link BluetoothAdapter#getPeriodicAdvertisingManager()} to get an
+ * instance of {@link PeriodicAdvertisingManager}.
+ * <p>
+ * <b>Note:</b> Most of the methods here require
+ * {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
+ */
+public final class PeriodicAdvertisingManager {
+
+ private static final String TAG = "PeriodicAdvertisingManager";
+
+ private static final int SKIP_MIN = 0;
+ private static final int SKIP_MAX = 499;
+ private static final int TIMEOUT_MIN = 10;
+ private static final int TIMEOUT_MAX = 16384;
+
+ private static final int SYNC_STARTING = -1;
+
+ private final IBluetoothManager mBluetoothManager;
+ private BluetoothAdapter mBluetoothAdapter;
+
+ /* maps callback, to callback wrapper and sync handle */
+ Map<PeriodicAdvertisingCallback,
+ IPeriodicAdvertisingCallback /* callbackWrapper */> callbackWrappers;
+
+ /**
+ * Use {@link BluetoothAdapter#getBluetoothLeScanner()} instead.
+ *
+ * @param bluetoothManager BluetoothManager that conducts overall Bluetooth Management.
+ * @hide
+ */
+ public PeriodicAdvertisingManager(IBluetoothManager bluetoothManager) {
+ mBluetoothManager = bluetoothManager;
+ mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+ callbackWrappers = new IdentityHashMap<>();
+ }
+
+ /**
+ * Synchronize with periodic advertising pointed to by the {@code scanResult}.
+ * The {@code scanResult} used must contain a valid advertisingSid. First
+ * call to registerSync will use the {@code skip} and {@code timeout} provided.
+ * Subsequent calls from other apps, trying to sync with same set will reuse
+ * existing sync, thus {@code skip} and {@code timeout} values will not take
+ * effect. The values in effect will be returned in
+ * {@link PeriodicAdvertisingCallback#onSyncEstablished}.
+ *
+ * @param scanResult Scan result containing advertisingSid.
+ * @param skip The number of periodic advertising packets that can be skipped
+ * after a successful receive. Must be between 0 and 499.
+ * @param timeout Synchronization timeout for the periodic advertising. One
+ * unit is 10ms. Must be between 10 (100ms) and 16384 (163.84s).
+ * @param callback Callback used to deliver all operations status.
+ * @throws IllegalArgumentException if {@code scanResult} is null or {@code
+ * skip} is invalid or {@code timeout} is invalid or {@code callback} is null.
+ */
+ public void registerSync(ScanResult scanResult, int skip, int timeout,
+ PeriodicAdvertisingCallback callback) {
+ registerSync(scanResult, skip, timeout, callback, null);
+ }
+
+ /**
+ * Synchronize with periodic advertising pointed to by the {@code scanResult}.
+ * The {@code scanResult} used must contain a valid advertisingSid. First
+ * call to registerSync will use the {@code skip} and {@code timeout} provided.
+ * Subsequent calls from other apps, trying to sync with same set will reuse
+ * existing sync, thus {@code skip} and {@code timeout} values will not take
+ * effect. The values in effect will be returned in
+ * {@link PeriodicAdvertisingCallback#onSyncEstablished}.
+ *
+ * @param scanResult Scan result containing advertisingSid.
+ * @param skip The number of periodic advertising packets that can be skipped
+ * after a successful receive. Must be between 0 and 499.
+ * @param timeout Synchronization timeout for the periodic advertising. One
+ * unit is 10ms. Must be between 10 (100ms) and 16384 (163.84s).
+ * @param callback Callback used to deliver all operations status.
+ * @param handler thread upon which the callbacks will be invoked.
+ * @throws IllegalArgumentException if {@code scanResult} is null or {@code
+ * skip} is invalid or {@code timeout} is invalid or {@code callback} is null.
+ */
+ public void registerSync(ScanResult scanResult, int skip, int timeout,
+ PeriodicAdvertisingCallback callback, Handler handler) {
+ if (callback == null) {
+ throw new IllegalArgumentException("callback can't be null");
+ }
+
+ if (scanResult == null) {
+ throw new IllegalArgumentException("scanResult can't be null");
+ }
+
+ if (scanResult.getAdvertisingSid() == ScanResult.SID_NOT_PRESENT) {
+ throw new IllegalArgumentException("scanResult must contain a valid sid");
+ }
+
+ if (skip < SKIP_MIN || skip > SKIP_MAX) {
+ throw new IllegalArgumentException(
+ "timeout must be between " + TIMEOUT_MIN + " and " + TIMEOUT_MAX);
+ }
+
+ if (timeout < TIMEOUT_MIN || timeout > TIMEOUT_MAX) {
+ throw new IllegalArgumentException(
+ "timeout must be between " + TIMEOUT_MIN + " and " + TIMEOUT_MAX);
+ }
+
+ IBluetoothGatt gatt;
+ try {
+ gatt = mBluetoothManager.getBluetoothGatt();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to get Bluetooth gatt - ", e);
+ callback.onSyncEstablished(0, scanResult.getDevice(), scanResult.getAdvertisingSid(),
+ skip, timeout,
+ PeriodicAdvertisingCallback.SYNC_NO_RESOURCES);
+ return;
+ }
+
+ if (handler == null)
+ handler = new Handler(Looper.getMainLooper());
+
+ IPeriodicAdvertisingCallback wrapped = wrap(callback, handler);
+ callbackWrappers.put(callback, wrapped);
+
+ try {
+ gatt.registerSync(scanResult, skip, timeout, wrapped);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to register sync - ", e);
+ return;
+ }
+ }
+
+ /**
+ * Cancel pending attempt to create sync, or terminate existing sync.
+ *
+ * @param callback Callback used to deliver all operations status.
+ * @throws IllegalArgumentException if {@code callback} is null, or not a properly
+ * registered callback.
+ */
+ public void unregisterSync(PeriodicAdvertisingCallback callback) {
+ if (callback == null) {
+ throw new IllegalArgumentException("callback can't be null");
+ }
+
+ IBluetoothGatt gatt;
+ try {
+ gatt = mBluetoothManager.getBluetoothGatt();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to get Bluetooth gatt - ", e);
+ return;
+ }
+
+ IPeriodicAdvertisingCallback wrapper = callbackWrappers.remove(callback);
+ if (wrapper == null) {
+ throw new IllegalArgumentException("callback was not properly registered");
+ }
+
+ try {
+ gatt.unregisterSync(wrapper);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to cancel sync creation - ", e);
+ return;
+ }
+ }
+
+ private IPeriodicAdvertisingCallback wrap(PeriodicAdvertisingCallback callback, Handler handler) {
+ return new IPeriodicAdvertisingCallback.Stub() {
+ public void onSyncEstablished(int syncHandle, BluetoothDevice device,
+ int advertisingSid, int skip, int timeout, int status) {
+
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onSyncEstablished(syncHandle, device, advertisingSid, skip, timeout,
+ status);
+
+ if (status != PeriodicAdvertisingCallback.SYNC_SUCCESS) {
+ // App can still unregister the sync until notified it failed. Remove callback
+ // after app was notifed.
+ callbackWrappers.remove(callback);
+ }
+ }
+ });
+ }
+
+ public void onPeriodicAdvertisingReport(PeriodicAdvertisingReport report) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onPeriodicAdvertisingReport(report);
+ }
+ });
+ }
+
+ public void onSyncLost(int syncHandle) {
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onSyncLost(syncHandle);
+ // App can still unregister the sync until notified it's lost. Remove callback after
+ // app was notifed.
+ callbackWrappers.remove(callback);
+ }
+ });
+ }
+ };
+ }
+}
diff --git a/core/java/android/bluetooth/le/PeriodicAdvertisingParameters.aidl b/core/java/android/bluetooth/le/PeriodicAdvertisingParameters.aidl
new file mode 100644
index 0000000..f4bea22
--- /dev/null
+++ b/core/java/android/bluetooth/le/PeriodicAdvertisingParameters.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2017 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.le;
+
+parcelable PeriodicAdvertisingParameters;
diff --git a/core/java/android/bluetooth/le/PeriodicAdvertisingParameters.java b/core/java/android/bluetooth/le/PeriodicAdvertisingParameters.java
new file mode 100644
index 0000000..ebc92bd
--- /dev/null
+++ b/core/java/android/bluetooth/le/PeriodicAdvertisingParameters.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2017 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.le;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * The {@link PeriodicAdvertisingParameters} provide a way to adjust periodic
+ * advertising preferences for each Bluetooth LE advertising set. Use {@link
+ * AdvertisingSetParameters.Builder} to create an instance of this class.
+ */
+public final class PeriodicAdvertisingParameters implements Parcelable {
+
+ private static final int INTERVAL_MAX = 80;
+ private static final int INTERVAL_MIN = 65519;
+
+ private final boolean enable;
+ private final boolean includeTxPower;
+ private final int interval;
+
+ private PeriodicAdvertisingParameters(boolean enable, boolean includeTxPower, int interval) {
+ this.enable = enable;
+ this.includeTxPower = includeTxPower;
+ this.interval = interval;
+ }
+
+ private PeriodicAdvertisingParameters(Parcel in) {
+ enable = in.readInt() != 0 ? true : false;
+ includeTxPower = in.readInt() != 0 ? true : false;
+ interval = in.readInt();
+ }
+
+ /**
+ * Returns whether the periodic advertising shall be enabled.
+ */
+ public boolean getEnable() { return enable; }
+
+ /**
+ * Returns whether the TX Power will be included.
+ */
+ public boolean getIncludeTxPower() { return includeTxPower; }
+
+ /**
+ * Returns the periodic advertising interval, in 1.25ms unit.
+ * Valid values are from 80 (100ms) to 65519 (81.89875s).
+ */
+ public int getInterval() { return interval; }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(enable ? 1 : 0);
+ dest.writeInt(includeTxPower ? 1 : 0);
+ dest.writeInt(interval);
+ }
+
+ public static final Parcelable
+ .Creator<PeriodicAdvertisingParameters> CREATOR =
+ new Creator<PeriodicAdvertisingParameters>() {
+ @Override
+ public PeriodicAdvertisingParameters[] newArray(int size) {
+ return new PeriodicAdvertisingParameters[size];
+ }
+
+ @Override
+ public PeriodicAdvertisingParameters createFromParcel(Parcel in) {
+ return new PeriodicAdvertisingParameters(in);
+ }
+ };
+
+ public static final class Builder {
+ private boolean includeTxPower = false;
+ private boolean enable = false;
+ private int interval = INTERVAL_MAX;
+
+ /**
+ * Set wether the Periodic Advertising should be enabled for this set.
+ */
+ public Builder setEnable(boolean enable) {
+ this.enable = enable;
+ return this;
+ }
+
+ /**
+ * Whether the transmission power level should be included in the periodic
+ * packet.
+ */
+ public Builder setIncludeTxPower(boolean includeTxPower) {
+ this.includeTxPower = includeTxPower;
+ return this;
+ }
+
+ /**
+ * Set advertising interval for periodic advertising, in 1.25ms unit.
+ * Valid values are from 80 (100ms) to 65519 (81.89875s).
+ * Value from range [interval, interval+20ms] will be picked as the actual value.
+ * @throws IllegalArgumentException If the interval is invalid.
+ */
+ public Builder setInterval(int interval) {
+ if (interval < INTERVAL_MIN || interval > INTERVAL_MAX) {
+ throw new IllegalArgumentException("Invalid interval (must be " + INTERVAL_MIN +
+ "-" + INTERVAL_MAX + ")");
+ }
+ this.interval = interval;
+ return this;
+ }
+
+ /**
+ * Build the {@link AdvertisingSetParameters} object.
+ */
+ public PeriodicAdvertisingParameters build() {
+ return new PeriodicAdvertisingParameters(enable, includeTxPower, interval);
+ }
+ }
+}
diff --git a/core/java/android/bluetooth/le/PeriodicAdvertisingReport.aidl b/core/java/android/bluetooth/le/PeriodicAdvertisingReport.aidl
new file mode 100644
index 0000000..547d096
--- /dev/null
+++ b/core/java/android/bluetooth/le/PeriodicAdvertisingReport.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2017 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.le;
+
+parcelable PeriodicAdvertisingReport;
diff --git a/core/java/android/bluetooth/le/PeriodicAdvertisingReport.java b/core/java/android/bluetooth/le/PeriodicAdvertisingReport.java
new file mode 100644
index 0000000..3ff4ca5
--- /dev/null
+++ b/core/java/android/bluetooth/le/PeriodicAdvertisingReport.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2017 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.le;
+
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * PeriodicAdvertisingReport for Bluetooth LE synchronized advertising.
+ */
+public final class PeriodicAdvertisingReport implements Parcelable {
+
+ /**
+ * The data returned is complete
+ */
+ public static final int DATA_COMPLETE = 0;
+
+ /**
+ * The data returned is incomplete. The controller was unsuccessfull to
+ * receive all chained packets, returning only partial data.
+ */
+ public static final int DATA_INCOMPLETE_TRUNCATED = 2;
+
+ private int syncHandle;
+ private int txPower;
+ private int rssi;
+ private int dataStatus;
+
+ // periodic advertising data.
+ @Nullable
+ private ScanRecord data;
+
+ // Device timestamp when the result was last seen.
+ private long timestampNanos;
+
+ /**
+ * Constructor of periodic advertising result.
+ *
+ */
+ public PeriodicAdvertisingReport(int syncHandle, int txPower, int rssi,
+ int dataStatus, ScanRecord data) {
+ this.syncHandle = syncHandle;
+ this.txPower = txPower;
+ this.rssi = rssi;
+ this.dataStatus = dataStatus;
+ this.data = data;
+ }
+
+ private PeriodicAdvertisingReport(Parcel in) {
+ readFromParcel(in);
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(syncHandle);
+ dest.writeLong(txPower);
+ dest.writeInt(rssi);
+ dest.writeInt(dataStatus);
+ if (data != null) {
+ dest.writeInt(1);
+ dest.writeByteArray(data.getBytes());
+ } else {
+ dest.writeInt(0);
+ }
+ }
+
+ private void readFromParcel(Parcel in) {
+ syncHandle = in.readInt();
+ txPower = in.readInt();
+ rssi = in.readInt();
+ dataStatus = in.readInt();
+ if (in.readInt() == 1) {
+ data = ScanRecord.parseFromBytes(in.createByteArray());
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Returns the synchronization handle.
+ */
+ public int getSyncHandle() {
+ return syncHandle;
+ }
+
+ /**
+ * Returns the transmit power in dBm. The valid range is [-127, 126]. Value
+ * of 127 means information was not available.
+ */
+ public int getTxPower() {
+ return txPower;
+ }
+
+ /**
+ * Returns the received signal strength in dBm. The valid range is [-127, 20].
+ */
+ public int getRssi() {
+ return rssi;
+ }
+
+ /**
+ * Returns the data status. Can be one of {@link PeriodicAdvertisingReport#DATA_COMPLETE}
+ * or {@link PeriodicAdvertisingReport#DATA_INCOMPLETE_TRUNCATED}.
+ */
+ public int getDataStatus() {
+ return dataStatus;
+ }
+
+ /**
+ * Returns the data contained in this periodic advertising report.
+ */
+ @Nullable
+ public ScanRecord getData() {
+ return data;
+ }
+
+ /**
+ * Returns timestamp since boot when the scan record was observed.
+ */
+ public long getTimestampNanos() {
+ return timestampNanos;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(syncHandle, txPower, rssi, dataStatus, data, timestampNanos);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ PeriodicAdvertisingReport other = (PeriodicAdvertisingReport) obj;
+ return (syncHandle == other.syncHandle) &&
+ (txPower == other.txPower) &&
+ (rssi == other.rssi) &&
+ (dataStatus == other.dataStatus) &&
+ Objects.equals(data, other.data) &&
+ (timestampNanos == other.timestampNanos);
+ }
+
+ @Override
+ public String toString() {
+ return "PeriodicAdvertisingReport{syncHandle=" + syncHandle +
+ ", txPower=" + txPower + ", rssi=" + rssi + ", dataStatus=" + dataStatus +
+ ", data=" + Objects.toString(data) + ", timestampNanos=" + timestampNanos + '}';
+ }
+
+ public static final Parcelable.Creator<PeriodicAdvertisingReport> CREATOR = new Creator<PeriodicAdvertisingReport>() {
+ @Override
+ public PeriodicAdvertisingReport createFromParcel(Parcel source) {
+ return new PeriodicAdvertisingReport(source);
+ }
+
+ @Override
+ public PeriodicAdvertisingReport[] newArray(int size) {
+ return new PeriodicAdvertisingReport[size];
+ }
+ };
+}
diff --git a/core/java/android/bluetooth/le/ScanResult.java b/core/java/android/bluetooth/le/ScanResult.java
index 2fdfe7f..583ddd2 100644
--- a/core/java/android/bluetooth/le/ScanResult.java
+++ b/core/java/android/bluetooth/le/ScanResult.java
@@ -27,7 +27,56 @@
* ScanResult for Bluetooth LE scan.
*/
public final class ScanResult implements Parcelable {
- // Remote bluetooth device.
+
+ /**
+ * For chained advertisements, inidcates tha the data contained in this
+ * scan result is complete.
+ */
+ public static final int DATA_COMPLETE = 0x00;
+
+ /**
+ * For chained advertisements, indicates that the controller was
+ * unable to receive all chained packets and the scan result contains
+ * incomplete truncated data.
+ */
+ public static final int DATA_TRUNCATED = 0x02;
+
+ /**
+ * Indicates that the secondary physical layer was not used.
+ */
+ public static final int PHY_UNUSED = 0x00;
+
+ /**
+ * Bluetooth LE 1Mbit advertiser PHY.
+ */
+ public static final int PHY_LE_1M = 0x01;
+
+ /**
+ * Bluetooth LE 2Mbit advertiser PHY.
+ */
+ public static final int PHY_LE_2M = 0x02;
+
+ /**
+ * Bluetooth LE Coded advertiser PHY.
+ */
+ public static final int PHY_LE_CODED = 0x03;
+
+ /**
+ * Advertising Set ID is not present in the packet.
+ */
+ public static final int SID_NOT_PRESENT = 0xFF;
+
+ /**
+ * Mask for checking wether event type represents legacy advertisement.
+ */
+ private static final int ET_LEGACY_MASK = 0x10;
+
+ /**
+ * Mask for checking wether event type represents connectable advertisement.
+ */
+ private static final int ET_CONNECTABLE_MASK = 0x01;
+
+ // Remote Bluetooth device.
private BluetoothDevice mDevice;
// Scan record, including advertising data and scan response data.
@@ -40,13 +89,21 @@
// Device timestamp when the result was last seen.
private long mTimestampNanos;
+ private int mEventType;
+ private int mPrimaryPhy;
+ private int mSecondaryPhy;
+ private int mAdvertisingSid;
+ private int mTxPower;
+ private int mPeriodicAdvertisingInterval;
+
/**
- * Constructor of scan result.
+ * Constructs a new ScanResult.
*
- * @param device Remote bluetooth device that is found.
+ * @param device Remote Bluetooth device found.
* @param scanRecord Scan record including both advertising data and scan response data.
* @param rssi Received signal strength.
- * @param timestampNanos Device timestamp when the scan result was observed.
+ * @param timestampNanos Timestamp at which the scan result was observed.
+ * @deprecated use {@link #ScanResult(BluetoothDevice, int, int, int, int, int, int, int, ScanRecord, long)}
*/
public ScanResult(BluetoothDevice device, ScanRecord scanRecord, int rssi,
long timestampNanos) {
@@ -54,6 +111,41 @@
mScanRecord = scanRecord;
mRssi = rssi;
mTimestampNanos = timestampNanos;
+ mEventType = (DATA_COMPLETE << 5) | ET_LEGACY_MASK | ET_CONNECTABLE_MASK;
+ mPrimaryPhy = PHY_LE_1M;
+ mSecondaryPhy = PHY_UNUSED;
+ mAdvertisingSid = SID_NOT_PRESENT;
+ mTxPower = 127;
+ mPeriodicAdvertisingInterval = 0;
+ }
+
+ /**
+ * Constructs a new ScanResult.
+ *
+ * @param device Remote Bluetooth device found.
+ * @param eventType Event type.
+ * @param primaryPhy Primary advertising phy.
+ * @param secondaryPhy Secondary advertising phy.
+ * @param advertisingSid Advertising set ID.
+ * @param txPower Transmit power.
+ * @param rssi Received signal strength.
+ * @param periodicAdvertisingInterval Periodic advertising interval.
+ * @param scanRecord Scan record including both advertising data and scan response data.
+ * @param timestampNanos Timestamp at which the scan result was observed.
+ */
+ public ScanResult(BluetoothDevice device, int eventType, int primaryPhy, int secondaryPhy,
+ int advertisingSid, int txPower, int rssi, int periodicAdvertisingInterval,
+ ScanRecord scanRecord, long timestampNanos) {
+ mDevice = device;
+ mEventType = eventType;
+ mPrimaryPhy = primaryPhy;
+ mSecondaryPhy = secondaryPhy;
+ mAdvertisingSid = advertisingSid;
+ mTxPower = txPower;
+ mRssi = rssi;
+ mPeriodicAdvertisingInterval = periodicAdvertisingInterval;
+ mScanRecord = scanRecord;
+ mTimestampNanos = timestampNanos;
}
private ScanResult(Parcel in) {
@@ -76,6 +168,12 @@
}
dest.writeInt(mRssi);
dest.writeLong(mTimestampNanos);
+ dest.writeInt(mEventType);
+ dest.writeInt(mPrimaryPhy);
+ dest.writeInt(mSecondaryPhy);
+ dest.writeInt(mAdvertisingSid);
+ dest.writeInt(mTxPower);
+ dest.writeInt(mPeriodicAdvertisingInterval);
}
private void readFromParcel(Parcel in) {
@@ -87,6 +185,12 @@
}
mRssi = in.readInt();
mTimestampNanos = in.readLong();
+ mEventType = in.readInt();
+ mPrimaryPhy = in.readInt();
+ mSecondaryPhy = in.readInt();
+ mAdvertisingSid = in.readInt();
+ mTxPower = in.readInt();
+ mPeriodicAdvertisingInterval = in.readInt();
}
@Override
@@ -95,7 +199,7 @@
}
/**
- * Returns the remote bluetooth device identified by the bluetooth device address.
+ * Returns the remote Bluetooth device identified by the Bluetooth device address.
*/
public BluetoothDevice getDevice() {
return mDevice;
@@ -110,7 +214,7 @@
}
/**
- * Returns the received signal strength in dBm. The valid range is [-127, 127].
+ * Returns the received signal strength in dBm. The valid range is [-127, 126].
*/
public int getRssi() {
return mRssi;
@@ -123,9 +227,79 @@
return mTimestampNanos;
}
+ /**
+ * Returns true if this object represents legacy scan result.
+ * Legacy scan results do not contain advanced advertising information
+ * as specified in the Bluetooth Core Specification v5.
+ */
+ public boolean isLegacy() {
+ return (mEventType & ET_LEGACY_MASK) != 0;
+ }
+
+ /**
+ * Returns true if this object represents connectable scan result.
+ */
+ public boolean isConnectable() {
+ return (mEventType & ET_CONNECTABLE_MASK) != 0;
+ }
+
+ /**
+ * Returns the data status.
+ * Can be one of {@link ScanResult#DATA_COMPLETE} or
+ * {@link ScanResult#DATA_TRUNCATED}.
+ */
+ public int getDataStatus() {
+ // return bit 5 and 6
+ return (mEventType >> 5) & 0x03;
+ }
+
+ /**
+ * Returns the primary Physical Layer
+ * on which this advertisment was received.
+ * Can be one of {@link ScanResult#PHY_LE_1M} or
+ * {@link ScanResult#PHY_LE_CODED}.
+ */
+ public int getPrimaryPhy() { return mPrimaryPhy; }
+
+ /**
+ * Returns the secondary Physical Layer
+ * on which this advertisment was received.
+ * Can be one of {@link ScanResult#PHY_LE_1M},
+ * {@link ScanResult#PHY_LE_2M}, {@link ScanResult#PHY_LE_CODED}
+ * or {@link ScanResult#PHY_UNUSED} - if the advertisement
+ * was not received on a secondary physical channel.
+ */
+ public int getSecondaryPhy() { return mSecondaryPhy; }
+
+ /**
+ * Returns the advertising set id.
+ * May return {@link ScanResult#SID_NOT_PRESENT} if
+ * no set id was is present.
+ */
+ public int getAdvertisingSid() { return mAdvertisingSid; }
+
+ /**
+ * Returns the transmit power in dBm.
+ * Valid range is [-127, 126]. A value of 127 indicates that the
+ * advertisement did not indicate TX power.
+ */
+ public int getTxPower() { return mTxPower; }
+
+ /**
+ * Returns the periodic advertising interval in units of 1.25ms.
+ * Valid range is 6 (7.5ms) to 65536 (81918.75ms). A value of 0 means
+ * periodic advertising is not used for this scan result.
+ */
+ public int getPeriodicAdvertisingInterval() {
+ return mPeriodicAdvertisingInterval;
+ }
+
@Override
public int hashCode() {
- return Objects.hash(mDevice, mRssi, mScanRecord, mTimestampNanos);
+ return Objects.hash(mDevice, mRssi, mScanRecord, mTimestampNanos,
+ mEventType, mPrimaryPhy, mSecondaryPhy,
+ mAdvertisingSid, mTxPower,
+ mPeriodicAdvertisingInterval);
}
@Override
@@ -138,15 +312,24 @@
}
ScanResult other = (ScanResult) obj;
return Objects.equals(mDevice, other.mDevice) && (mRssi == other.mRssi) &&
- Objects.equals(mScanRecord, other.mScanRecord)
- && (mTimestampNanos == other.mTimestampNanos);
+ Objects.equals(mScanRecord, other.mScanRecord) &&
+ (mTimestampNanos == other.mTimestampNanos) &&
+ mEventType == other.mEventType &&
+ mPrimaryPhy == other.mPrimaryPhy &&
+ mSecondaryPhy == other.mSecondaryPhy &&
+ mAdvertisingSid == other.mAdvertisingSid &&
+ mTxPower == other.mTxPower &&
+ mPeriodicAdvertisingInterval == other.mPeriodicAdvertisingInterval;
}
@Override
public String toString() {
- return "ScanResult{" + "mDevice=" + mDevice + ", mScanRecord="
- + Objects.toString(mScanRecord) + ", mRssi=" + mRssi + ", mTimestampNanos="
- + mTimestampNanos + '}';
+ return "ScanResult{" + "device=" + mDevice + ", scanRecord=" +
+ Objects.toString(mScanRecord) + ", rssi=" + mRssi +
+ ", timestampNanos=" + mTimestampNanos + ", eventType=" + mEventType +
+ ", primaryPhy=" + mPrimaryPhy + ", secondaryPhy=" + mSecondaryPhy +
+ ", advertisingSid=" + mAdvertisingSid + ", txPower=" + mTxPower +
+ ", periodicAdvertisingInterval=" + mPeriodicAdvertisingInterval + '}';
}
public static final Parcelable.Creator<ScanResult> CREATOR = new Creator<ScanResult>() {
diff --git a/core/java/android/bluetooth/le/ScanSettings.java b/core/java/android/bluetooth/le/ScanSettings.java
index d616624..69c9a8c 100644
--- a/core/java/android/bluetooth/le/ScanSettings.java
+++ b/core/java/android/bluetooth/le/ScanSettings.java
@@ -122,6 +122,24 @@
@SystemApi
public static final int SCAN_RESULT_TYPE_ABBREVIATED = 1;
+ /**
+ * Use the Bluetooth LE 1Mbit PHY for scanning.
+ */
+ public static final int PHY_LE_1M = 1;
+
+ /**
+ * Use Bluetooth LE Coded PHY for scanning.
+ */
+ public static final int PHY_LE_CODED = 3;
+
+ /**
+ * Use all supported PHYs for scanning.
+ * This will check the controller capabilities, and start
+ * the scan on 1Mbit and LE Coded PHYs if supported, or on
+ * the 1Mbit PHY only.
+ */
+ public static final int PHY_LE_ALL_SUPPORTED = 255;
+
// Bluetooth LE scan mode.
private int mScanMode;
@@ -138,6 +156,11 @@
private int mNumOfMatchesPerFilter;
+ // Include only legacy advertising results
+ private boolean mLegacy;
+
+ private int mPhy;
+
public int getScanMode() {
return mScanMode;
}
@@ -165,6 +188,22 @@
}
/**
+ * Returns whether only legacy advertisements will be returned.
+ * Legacy advertisements include advertisements as specified
+ * by the Bluetooth core specification 4.2 and below.
+ */
+ public boolean getLegacy() {
+ return mLegacy;
+ }
+
+ /**
+ * Returns the physical layer used during a scan.
+ */
+ public int getPhy() {
+ return mPhy;
+ }
+
+ /**
* Returns report delay timestamp based on the device clock.
*/
public long getReportDelayMillis() {
@@ -172,13 +211,16 @@
}
private ScanSettings(int scanMode, int callbackType, int scanResultType,
- long reportDelayMillis, int matchMode, int numOfMatchesPerFilter) {
+ long reportDelayMillis, int matchMode,
+ int numOfMatchesPerFilter, boolean legacy, int phy) {
mScanMode = scanMode;
mCallbackType = callbackType;
mScanResultType = scanResultType;
mReportDelayMillis = reportDelayMillis;
mNumOfMatchesPerFilter = numOfMatchesPerFilter;
mMatchMode = matchMode;
+ mLegacy = legacy;
+ mPhy = phy;
}
private ScanSettings(Parcel in) {
@@ -188,6 +230,8 @@
mReportDelayMillis = in.readLong();
mMatchMode = in.readInt();
mNumOfMatchesPerFilter = in.readInt();
+ mLegacy = in.readInt() != 0 ? true : false;
+ mPhy = in.readInt();
}
@Override
@@ -198,6 +242,8 @@
dest.writeLong(mReportDelayMillis);
dest.writeInt(mMatchMode);
dest.writeInt(mNumOfMatchesPerFilter);
+ dest.writeInt(mLegacy ? 1 : 0);
+ dest.writeInt(mPhy);
}
@Override
@@ -228,6 +274,9 @@
private long mReportDelayMillis = 0;
private int mMatchMode = MATCH_MODE_AGGRESSIVE;
private int mNumOfMatchesPerFilter = MATCH_NUM_MAX_ADVERTISEMENT;
+ private boolean mLegacy = true;
+ private int mPhy = PHY_LE_ALL_SUPPORTED;
+
/**
* Set scan mode for Bluetooth LE scan.
*
@@ -341,11 +390,44 @@
}
/**
+ * Set whether only legacy advertisments should be returned in scan results.
+ * Legacy advertisements include advertisements as specified by the
+ * Bluetooth core specification 4.2 and below. This is true by default
+ * for compatibility with older apps.
+ *
+ * @param legacy true if only legacy advertisements will be returned
+ */
+ public Builder setLegacy(boolean legacy) {
+ mLegacy = legacy;
+ return this;
+ }
+
+ /**
+ * Set the Physical Layer to use during this scan.
+ * This is used only if {@link ScanSettings.Builder#setLegacy}
+ * is set to false.
+ * {@link android.bluetooth.BluetoothAdapter#isLeCodedPhySupported}
+ * may be used to check whether LE Coded phy is supported by calling
+ * {@link android.bluetooth.BluetoothAdapter#isLeCodedPhySupported}.
+ * Selecting an unsupported phy will result in failure to start scan.
+ *
+ * @param phy Can be one of
+ * {@link ScanSettings#PHY_LE_1M},
+ * {@link ScanSettings#PHY_LE_CODED} or
+ * {@link ScanSettings#PHY_LE_ALL_SUPPORTED}
+ */
+ public Builder setPhy(int phy) {
+ mPhy = phy;
+ return this;
+ }
+
+ /**
* Build {@link ScanSettings}.
*/
public ScanSettings build() {
return new ScanSettings(mScanMode, mCallbackType, mScanResultType,
- mReportDelayMillis, mMatchMode, mNumOfMatchesPerFilter);
+ mReportDelayMillis, mMatchMode,
+ mNumOfMatchesPerFilter, mLegacy, mPhy);
}
}
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 02b86fe..46d3835 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2662,6 +2662,7 @@
VIBRATOR_SERVICE,
//@hide: STATUS_BAR_SERVICE,
CONNECTIVITY_SERVICE,
+ IPSEC_SERVICE,
//@hide: UPDATE_LOCK_SERVICE,
//@hide: NETWORKMANAGEMENT_SERVICE,
NETWORK_STATS_SERVICE,
@@ -2763,6 +2764,9 @@
* <dt> {@link #CONNECTIVITY_SERVICE} ("connection")
* <dd> A {@link android.net.ConnectivityManager ConnectivityManager} for
* handling management of network connections.
+ * <dt> {@link #IPSEC_SERVICE} ("ipsec")
+ * <dd> A {@link android.net.IpSecManager IpSecManager} for managing IPSec on
+ * sockets and networks.
* <dt> {@link #WIFI_SERVICE} ("wifi")
* <dd> A {@link android.net.wifi.WifiManager WifiManager} for management of Wi-Fi
* connectivity. On releases before NYC, it should only be obtained from an application
@@ -3093,6 +3097,15 @@
public static final String CONNECTIVITY_SERVICE = "connectivity";
/**
+ * Use with {@link #getSystemService} to retrieve a
+ * {@link android.net.IpSecManager} for encrypting Sockets or Networks with
+ * IPSec.
+ *
+ * @see #getSystemService
+ */
+ public static final String IPSEC_SERVICE = "ipsec";
+
+ /**
* Use with {@link #getSystemService} to retrieve a {@link
* android.os.IUpdateLock} for managing runtime sequences that
* must not be interrupted by headless OTA application or similar.
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index c6aaa48..b7876d9 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1096,6 +1096,15 @@
public static final String ACTION_SIM_ACTIVATION_REQUEST =
"android.intent.action.SIM_ACTIVATION_REQUEST";
/**
+ * Activity Action: Main entry point for carrier setup apps.
+ * <p>Carrier apps that provide an implementation for this action may be invoked to configure
+ * carrier service and typically require
+ * {@link android.telephony.TelephonyManager#hasCarrierPrivileges() carrier privileges} to
+ * fulfill their duties.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_CARRIER_SETUP = "android.intent.action.CARRIER_SETUP";
+ /**
* Activity Action: Send a message to someone specified by the data.
* <p>Input: {@link #getData} is URI describing the target.
* <p>Output: nothing.
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index c51945e..46b981e 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -482,6 +482,7 @@
*/
boolean performDexOpt(String packageName, boolean checkProfiles,
int compileReason, boolean force);
+
/**
* Ask the package manager to perform a dex-opt with the given compiler filter.
*
@@ -492,6 +493,16 @@
String targetCompilerFilter, boolean force);
/**
+ * Ask the package manager to perform a dex-opt with the given compiler filter on the
+ * secondary dex files belonging to the given package.
+ *
+ * Note: exposed only for the shell command to allow moving packages explicitly to a
+ * definite state.
+ */
+ boolean performDexOptSecondary(String packageName,
+ String targetCompilerFilter, boolean force);
+
+ /**
* Ask the package manager to dump profiles associated with a package.
*/
void dumpProfiles(String packageName);
@@ -499,6 +510,18 @@
void forceDexOpt(String packageName);
/**
+ * Execute the background dexopt job immediately.
+ */
+ boolean runBackgroundDexoptJob();
+
+ /**
+ * Reconcile the information we have about the secondary dex files belonging to
+ * {@code packagName} and the actual dex files. For all dex files that were
+ * deleted, update the internal records and delete the generated oat files.
+ */
+ void reconcileSecondaryDexFiles(String packageName);
+
+ /**
* Update status of external media on the package manager to scan and
* install packages installed on the external media. Like say the
* MountService uses this to call into the package manager to update
diff --git a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
index bb0a042..5423ca9 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
@@ -646,7 +646,7 @@
// 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) {
+ sceneModes.get(0).equals(Parameters.SCENE_MODE_AUTO)) {
supportedSceneModes = null;
}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 82e3093..e163365 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -1052,26 +1052,6 @@
}
/**
- * Request that this callback be invoked at ConnectivityService's earliest
- * convenience with the current satisfying network's LinkProperties.
- * If no such network exists no callback invocation is performed.
- *
- * The callback must have been registered with #requestNetwork() or
- * #registerDefaultNetworkCallback(); callbacks registered with
- * registerNetworkCallback() are not specific to any particular Network so
- * do not cause any updates.
- *
- * @hide
- */
- public void requestLinkProperties(NetworkCallback networkCallback) {
- try {
- mService.requestLinkProperties(networkCallback.networkRequest);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* Get the {@link android.net.NetworkCapabilities} for the given {@link Network}. This
* will return {@code null} if the network is unknown.
* <p>This method requires the caller to hold the permission
@@ -1089,26 +1069,6 @@
}
/**
- * Request that this callback be invoked at ConnectivityService's earliest
- * convenience with the current satisfying network's NetworkCapabilities.
- * If no such network exists no callback invocation is performed.
- *
- * The callback must have been registered with #requestNetwork() or
- * #registerDefaultNetworkCallback(); callbacks registered with
- * registerNetworkCallback() are not specific to any particular Network so
- * do not cause any updates.
- *
- * @hide
- */
- public void requestNetworkCapabilities(NetworkCallback networkCallback) {
- try {
- mService.requestNetworkCapabilities(networkCallback.networkRequest);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* Gets the URL that should be used for resolving whether a captive portal is present.
* 1. This URL should respond with a 204 response to a GET request to indicate no captive
* portal is present.
@@ -1421,8 +1381,8 @@
l.networkCapabilities = netCap;
l.delay = delay;
l.expireSequenceNumber = 0;
- l.networkRequest = sendRequestForNetwork(netCap, l.networkCallback, 0,
- REQUEST, type);
+ l.networkRequest = sendRequestForNetwork(
+ netCap, l.networkCallback, 0, REQUEST, type, getDefaultHandler());
if (l.networkRequest == null) return null;
sLegacyRequests.put(netCap, l);
sendExpireMsgForFeature(netCap, l.expireSequenceNumber, delay);
@@ -1432,8 +1392,9 @@
private void sendExpireMsgForFeature(NetworkCapabilities netCap, int seqNum, int delay) {
if (delay >= 0) {
Log.d(TAG, "sending expire msg with seqNum " + seqNum + " and delay " + delay);
- Message msg = sCallbackHandler.obtainMessage(EXPIRE_LEGACY_REQUEST, seqNum, 0, netCap);
- sCallbackHandler.sendMessageDelayed(msg, delay);
+ CallbackHandler handler = getDefaultHandler();
+ Message msg = handler.obtainMessage(EXPIRE_LEGACY_REQUEST, seqNum, 0, netCap);
+ handler.sendMessageDelayed(msg, delay);
}
}
@@ -2646,10 +2607,12 @@
public void onLost(Network network) {}
/**
- * Called if no network is found in the given timeout time. If no timeout is given,
- * this will not be called. The associated {@link NetworkRequest} will have already
- * been removed and released, as if {@link #unregisterNetworkCallback} had been called.
- * @hide
+ * Called if no network is found in the timeout time specified in
+ * {@link #requestNetwork(NetworkRequest, int, NetworkCallback)} call. This callback is not
+ * called for the version of {@link #requestNetwork(NetworkRequest, NetworkCallback)}
+ * without timeout. When this callback is invoked the associated
+ * {@link NetworkRequest} will have already been removed and released, as if
+ * {@link #unregisterNetworkCallback(NetworkCallback)} had been called.
*/
public void onUnavailable() {}
@@ -2750,6 +2713,10 @@
super(looper);
}
+ CallbackHandler(Handler handler) {
+ this(handler.getLooper());
+ }
+
@Override
public void handleMessage(Message message) {
NetworkRequest request = getObject(message, NetworkRequest.class);
@@ -2859,7 +2826,7 @@
}
}
- private CallbackHandler getHandler() {
+ private CallbackHandler getDefaultHandler() {
synchronized (sCallbacks) {
if (sCallbackHandler == null) {
sCallbackHandler = new CallbackHandler(ConnectivityThread.getInstanceLooper());
@@ -2868,19 +2835,14 @@
}
}
- static final HashMap<NetworkRequest, NetworkCallback> sCallbacks = new HashMap<>();
- static CallbackHandler sCallbackHandler;
+ private static final HashMap<NetworkRequest, NetworkCallback> sCallbacks = new HashMap<>();
+ private static CallbackHandler sCallbackHandler;
- private final static int LISTEN = 1;
- private final static int REQUEST = 2;
+ private static final int LISTEN = 1;
+ private static final int REQUEST = 2;
- private NetworkRequest sendRequestForNetwork(NetworkCapabilities need,
- NetworkCallback callback, int timeoutMs, int action, int legacyType) {
- return sendRequestForNetwork(need, callback, getHandler(), timeoutMs, action, legacyType);
- }
-
- private NetworkRequest sendRequestForNetwork(NetworkCapabilities need,
- NetworkCallback callback, Handler handler, int timeoutMs, int action, int legacyType) {
+ private NetworkRequest sendRequestForNetwork(NetworkCapabilities need, NetworkCallback callback,
+ int timeoutMs, int action, int legacyType, CallbackHandler handler) {
if (callback == null) {
throw new IllegalArgumentException("null NetworkCallback");
}
@@ -2923,16 +2885,19 @@
* @hide
*/
public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback,
- int timeoutMs, int legacyType) {
- sendRequestForNetwork(request.networkCapabilities, networkCallback, timeoutMs, REQUEST,
- legacyType);
+ int timeoutMs, int legacyType, Handler handler) {
+ CallbackHandler cbHandler = new CallbackHandler(handler);
+ NetworkCapabilities nc = request.networkCapabilities;
+ sendRequestForNetwork(nc, networkCallback, timeoutMs, REQUEST, legacyType, cbHandler);
}
/**
* Request a network to satisfy a set of {@link android.net.NetworkCapabilities}.
*
* This {@link NetworkRequest} will live until released via
- * {@link #unregisterNetworkCallback(NetworkCallback)} or the calling application exits.
+ * {@link #unregisterNetworkCallback(NetworkCallback)} or the calling application exits. A
+ * version of the method which takes a timeout is
+ * {@link #requestNetwork(NetworkRequest, int, NetworkCallback)}.
* Status of the request can be followed by listening to the various
* callbacks described in {@link NetworkCallback}. The {@link Network}
* can be used to direct traffic to the network.
@@ -2951,25 +2916,74 @@
* {@link android.provider.Settings.System#canWrite}.</p>
*
* @param request {@link NetworkRequest} describing this request.
- * @param networkCallback The {@link NetworkCallback} to be utilized for this
- * request. Note the callback must not be shared - they
- * uniquely specify this request.
+ * @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note
+ * the callback must not be shared - it uniquely specifies this request.
+ * The callback is invoked on the default internal Handler.
* @throws IllegalArgumentException if {@code request} specifies any mutable
* {@code NetworkCapabilities}.
*/
public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback) {
- requestNetwork(request, networkCallback, 0,
- inferLegacyTypeForNetworkCapabilities(request.networkCapabilities));
+ requestNetwork(request, networkCallback, getDefaultHandler());
}
/**
+ * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}.
+ *
+ * This {@link NetworkRequest} will live until released via
+ * {@link #unregisterNetworkCallback(NetworkCallback)} or the calling application exits. A
+ * version of the method which takes a timeout is
+ * {@link #requestNetwork(NetworkRequest, int, NetworkCallback)}.
+ * Status of the request can be followed by listening to the various
+ * callbacks described in {@link NetworkCallback}. The {@link Network}
+ * can be used to direct traffic to the network.
+ * <p>It is presently unsupported to request a network with mutable
+ * {@link NetworkCapabilities} such as
+ * {@link NetworkCapabilities#NET_CAPABILITY_VALIDATED} or
+ * {@link NetworkCapabilities#NET_CAPABILITY_CAPTIVE_PORTAL}
+ * as these {@code NetworkCapabilities} represent states that a particular
+ * network may never attain, and whether a network will attain these states
+ * is unknown prior to bringing up the network so the framework does not
+ * know how to go about satisfing a request with these capabilities.
+ *
+ * <p>This method requires the caller to hold either the
+ * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+ * or the ability to modify system settings as determined by
+ * {@link android.provider.Settings.System#canWrite}.</p>
+ *
+ * @param request {@link NetworkRequest} describing this request.
+ * @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note
+ * the callback must not be shared - it uniquely specifies this request.
+ * @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
+ * @throws IllegalArgumentException if {@code request} specifies any mutable
+ * {@code NetworkCapabilities}.
+ */
+ public void requestNetwork(
+ NetworkRequest request, NetworkCallback networkCallback, Handler handler) {
+ int legacyType = inferLegacyTypeForNetworkCapabilities(request.networkCapabilities);
+ CallbackHandler cbHandler = new CallbackHandler(handler);
+ requestNetwork(request, networkCallback, 0, legacyType, cbHandler);
+ }
+
+ /**
+ * Note: this is a deprecated version of
+ * {@link #requestNetwork(NetworkRequest, int, NetworkCallback)} - please transition code to use
+ * the unhidden version of the function.
+ * TODO: replace all callers with the new version of the API
+ *
* Request a network to satisfy a set of {@link android.net.NetworkCapabilities}, limited
* by a timeout.
*
- * This function behaves identically to the non-timedout version, but if a suitable
- * network is not found within the given time (in milliseconds) the
- * {@link NetworkCallback#unavailable} callback is called. The request must
- * still be released normally by calling {@link unregisterNetworkCallback(NetworkCallback)}.
+ * This function behaves identically to the non-timed-out version
+ * {@link #requestNetwork(NetworkRequest, NetworkCallback)}, but if a suitable network
+ * is not found within the given time (in milliseconds) the
+ * {@link NetworkCallback#onUnavailable()} callback is called. The request can still be
+ * released normally by calling {@link #unregisterNetworkCallback(NetworkCallback)} but does
+ * not have to be released if timed-out (it is automatically released). Unregistering a
+ * request that timed out is not an error.
+ *
+ * <p>Do not use this method to poll for the existence of specific networks (e.g. with a small
+ * timeout) - the {@link #registerNetworkCallback(NetworkRequest, NetworkCallback)} is provided
+ * for that purpose. Calling this method will attempt to bring up the requested network.
*
* <p>This method requires the caller to hold either the
* {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
@@ -2981,24 +2995,93 @@
* the callbacks must not be shared - they uniquely specify
* this request.
* @param timeoutMs The time in milliseconds to attempt looking for a suitable network
- * before {@link NetworkCallback#unavailable} is called.
- *
- * TODO: Make timeouts work and then unhide this method.
- *
+ * before {@link NetworkCallback#onUnavailable()} is called. The timeout must
+ * be a positive value (i.e. >0).
* @hide
*/
public void requestNetwork(NetworkRequest request, NetworkCallback networkCallback,
int timeoutMs) {
- requestNetwork(request, networkCallback, timeoutMs,
- inferLegacyTypeForNetworkCapabilities(request.networkCapabilities));
+ if (timeoutMs <= 0) {
+ throw new IllegalArgumentException("Non-positive timeoutMs: " + timeoutMs);
+ }
+ int legacyType = inferLegacyTypeForNetworkCapabilities(request.networkCapabilities);
+ requestNetwork(request, networkCallback, timeoutMs, legacyType, getDefaultHandler());
}
/**
- * The maximum number of milliseconds the framework will look for a suitable network
- * during a timeout-equiped call to {@link requestNetwork}.
- * {@hide}
+ * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}, limited
+ * by a timeout.
+ *
+ * This function behaves identically to the non-timed-out version
+ * {@link #requestNetwork(NetworkRequest, NetworkCallback)}, but if a suitable network
+ * is not found within the given time (in milliseconds) the
+ * {@link NetworkCallback#onUnavailable()} callback is called. The request can still be
+ * released normally by calling {@link #unregisterNetworkCallback(NetworkCallback)} but does
+ * not have to be released if timed-out (it is automatically released). Unregistering a
+ * request that timed out is not an error.
+ *
+ * <p>Do not use this method to poll for the existence of specific networks (e.g. with a small
+ * timeout) - {@link #registerNetworkCallback(NetworkRequest, NetworkCallback)} is provided
+ * for that purpose. Calling this method will attempt to bring up the requested network.
+ *
+ * <p>This method requires the caller to hold either the
+ * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+ * or the ability to modify system settings as determined by
+ * {@link android.provider.Settings.System#canWrite}.</p>
+ *
+ * @param request {@link NetworkRequest} describing this request.
+ * @param timeoutMs The time in milliseconds to attempt looking for a suitable network
+ * before {@link NetworkCallback#onUnavailable()} is called. The timeout must
+ * be a positive value (i.e. >0).
+ * @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note
+ * the callback must not be shared - it uniquely specifies this request.
*/
- public final static int MAX_NETWORK_REQUEST_TIMEOUT_MS = 100 * 60 * 1000;
+ public void requestNetwork(NetworkRequest request, int timeoutMs,
+ NetworkCallback networkCallback) {
+ if (timeoutMs <= 0) {
+ throw new IllegalArgumentException("Non-positive timeoutMs: " + timeoutMs);
+ }
+ int legacyType = inferLegacyTypeForNetworkCapabilities(request.networkCapabilities);
+ requestNetwork(request, networkCallback, timeoutMs, legacyType, getDefaultHandler());
+ }
+
+
+ /**
+ * Request a network to satisfy a set of {@link android.net.NetworkCapabilities}, limited
+ * by a timeout.
+ *
+ * This function behaves identically to the non-timedout version, but if a suitable
+ * network is not found within the given time (in milliseconds) the
+ * {@link NetworkCallback#onUnavailable} callback is called. The request can still be
+ * released normally by calling {@link #unregisterNetworkCallback(NetworkCallback)} but does
+ * not have to be released if timed-out (it is automatically released). Unregistering a
+ * request that timed out is not an error.
+ *
+ * <p>Do not use this method to poll for the existence of specific networks (e.g. with a small
+ * timeout) - {@link #registerNetworkCallback(NetworkRequest, NetworkCallback)} is provided
+ * for that purpose. Calling this method will attempt to bring up the requested network.
+ *
+ * <p>This method requires the caller to hold either the
+ * {@link android.Manifest.permission#CHANGE_NETWORK_STATE} permission
+ * or the ability to modify system settings as determined by
+ * {@link android.provider.Settings.System#canWrite}.</p>
+ *
+ * @param request {@link NetworkRequest} describing this request.
+ * @param timeoutMs The time in milliseconds to attempt looking for a suitable network
+ * before {@link NetworkCallback#onUnavailable} is called.
+ * @param networkCallback The {@link NetworkCallback} to be utilized for this request. Note
+ * the callback must not be shared - it uniquely specifies this request.
+ * @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
+ */
+ public void requestNetwork(NetworkRequest request, int timeoutMs,
+ NetworkCallback networkCallback, Handler handler) {
+ if (timeoutMs <= 0) {
+ throw new IllegalArgumentException("Non-positive timeoutMs");
+ }
+ int legacyType = inferLegacyTypeForNetworkCapabilities(request.networkCapabilities);
+ CallbackHandler cbHandler = new CallbackHandler(handler);
+ requestNetwork(request, networkCallback, timeoutMs, legacyType, cbHandler);
+ }
/**
* The lookup key for a {@link Network} object included with the intent after
@@ -3111,9 +3194,29 @@
* @param request {@link NetworkRequest} describing this request.
* @param networkCallback The {@link NetworkCallback} that the system will call as suitable
* networks change state.
+ * The callback is invoked on the default internal Handler.
*/
public void registerNetworkCallback(NetworkRequest request, NetworkCallback networkCallback) {
- sendRequestForNetwork(request.networkCapabilities, networkCallback, 0, LISTEN, TYPE_NONE);
+ registerNetworkCallback(request, networkCallback, getDefaultHandler());
+ }
+
+ /**
+ * Registers to receive notifications about all networks which satisfy the given
+ * {@link NetworkRequest}. The callbacks will continue to be called until
+ * either the application exits or link #unregisterNetworkCallback(NetworkCallback)} is called.
+ * <p>This method requires the caller to hold the permission
+ * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
+ *
+ * @param request {@link NetworkRequest} describing this request.
+ * @param networkCallback The {@link NetworkCallback} that the system will call as suitable
+ * networks change state.
+ * @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
+ */
+ public void registerNetworkCallback(
+ NetworkRequest request, NetworkCallback networkCallback, Handler handler) {
+ CallbackHandler cbHandler = new CallbackHandler(handler);
+ NetworkCapabilities nc = request.networkCapabilities;
+ sendRequestForNetwork(nc, networkCallback, 0, LISTEN, TYPE_NONE, cbHandler);
}
/**
@@ -3165,8 +3268,24 @@
*
* @param networkCallback The {@link NetworkCallback} that the system will call as the
* system default network changes.
+ * The callback is invoked on the default internal Handler.
*/
public void registerDefaultNetworkCallback(NetworkCallback networkCallback) {
+ registerDefaultNetworkCallback(networkCallback, getDefaultHandler());
+ }
+
+ /**
+ * Registers to receive notifications about changes in the system default network. The callbacks
+ * will continue to be called until either the application exits or
+ * {@link #unregisterNetworkCallback(NetworkCallback)} is called.
+ * <p>This method requires the caller to hold the permission
+ * {@link android.Manifest.permission#ACCESS_NETWORK_STATE}.
+ *
+ * @param networkCallback The {@link NetworkCallback} that the system will call as the
+ * system default network changes.
+ * @param handler {@link Handler} to specify the thread upon which the callback will be invoked.
+ */
+ public void registerDefaultNetworkCallback(NetworkCallback networkCallback, Handler handler) {
// This works because if the NetworkCapabilities are null,
// ConnectivityService takes them from the default request.
//
@@ -3174,7 +3293,8 @@
// capabilities, this request is guaranteed, at all times, to be
// satisfied by the same network, if any, that satisfies the default
// request, i.e., the system default network.
- sendRequestForNetwork(null, networkCallback, 0, REQUEST, TYPE_NONE);
+ CallbackHandler cbHandler = new CallbackHandler(handler);
+ sendRequestForNetwork(null, networkCallback, 0, REQUEST, TYPE_NONE, cbHandler);
}
/**
diff --git a/core/java/android/net/ConnectivityMetricsLogger.java b/core/java/android/net/ConnectivityMetricsLogger.java
index 9a2d4e0..67b6908 100644
--- a/core/java/android/net/ConnectivityMetricsLogger.java
+++ b/core/java/android/net/ConnectivityMetricsLogger.java
@@ -46,32 +46,7 @@
public static final String DATA_KEY_EVENTS_COUNT = "count";
- /** {@hide} */ protected IConnectivityMetricsLogger mService;
- /** {@hide} */ protected volatile long mServiceUnblockedTimestampMillis;
- private int mNumSkippedEvents;
-
public ConnectivityMetricsLogger() {
- // TODO: consider not initializing mService in constructor
- this(IConnectivityMetricsLogger.Stub.asInterface(
- ServiceManager.getService(CONNECTIVITY_METRICS_LOGGER_SERVICE)));
- }
-
- /** {@hide} */
- @VisibleForTesting
- public ConnectivityMetricsLogger(IConnectivityMetricsLogger service) {
- mService = service;
- }
-
- /** {@hide} */
- protected boolean checkLoggerService() {
- if (mService != null) {
- return true;
- }
- // Two threads racing here will write the same pointer because getService
- // is idempotent once MetricsLoggerService is initialized.
- mService = IConnectivityMetricsLogger.Stub.asInterface(
- ServiceManager.getService(CONNECTIVITY_METRICS_LOGGER_SERVICE));
- return mService != null;
}
/**
@@ -88,62 +63,6 @@
* @param data is a Parcelable instance representing the event.
*/
public void logEvent(long timestamp, int componentTag, int eventTag, Parcelable data) {
- if (mService == null) {
- if (DBG) {
- Log.d(TAG, "logEvent(" + componentTag + "," + eventTag + ") Service not ready");
- }
- return;
- }
-
- if (mServiceUnblockedTimestampMillis > 0) {
- if (System.currentTimeMillis() < mServiceUnblockedTimestampMillis) {
- // Service is throttling events.
- // Don't send new events because they will be dropped.
- mNumSkippedEvents++;
- return;
- }
- }
-
- ConnectivityMetricsEvent skippedEventsEvent = null;
- if (mNumSkippedEvents > 0) {
- // Log number of skipped events
- Bundle b = new Bundle();
- b.putInt(DATA_KEY_EVENTS_COUNT, mNumSkippedEvents);
-
- // Log the skipped event.
- // TODO: Note that some of the clients push all states events into the server,
- // If we lose some states logged here, we might mess up the statistics happened at the
- // backend. One of the options is to introduce a non-skippable flag for important events
- // that are logged.
- skippedEventsEvent = new ConnectivityMetricsEvent(mServiceUnblockedTimestampMillis,
- componentTag, TAG_SKIPPED_EVENTS, b);
-
- mServiceUnblockedTimestampMillis = 0;
- }
-
- ConnectivityMetricsEvent event = new ConnectivityMetricsEvent(timestamp, componentTag,
- eventTag, data);
-
- try {
- long result;
- if (skippedEventsEvent == null) {
- result = mService.logEvent(event);
- } else {
- result = mService.logEvents(new ConnectivityMetricsEvent[]
- {skippedEventsEvent, event});
- }
-
- if (result == 0) {
- mNumSkippedEvents = 0;
- } else {
- mNumSkippedEvents++;
- if (result > 0) { // events are throttled
- mServiceUnblockedTimestampMillis = result;
- }
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Error logging event", e);
- }
}
/**
@@ -157,33 +76,17 @@
* @return events
*/
public ConnectivityMetricsEvent[] getEvents(ConnectivityMetricsEvent.Reference reference) {
- try {
- return mService.getEvents(reference);
- } catch (RemoteException e) {
- Log.e(TAG, "IConnectivityMetricsLogger.getEvents", e);
- return null;
- }
+ return new ConnectivityMetricsEvent[0];
}
/**
* Register PendingIntent which will be sent when new events are ready to be retrieved.
*/
public boolean register(PendingIntent newEventsIntent) {
- try {
- return mService.register(newEventsIntent);
- } catch (RemoteException e) {
- Log.e(TAG, "IConnectivityMetricsLogger.register", e);
- return false;
- }
+ return false;
}
public boolean unregister(PendingIntent newEventsIntent) {
- try {
- mService.unregister(newEventsIntent);
- return true;
- } catch (RemoteException e) {
- Log.e(TAG, "IConnectivityMetricsLogger.unregister", e);
- return false;
- }
+ return false;
}
}
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 4aabda9..b123c28 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -156,8 +156,6 @@
void pendingListenForNetwork(in NetworkCapabilities networkCapabilities,
in PendingIntent operation);
- void requestLinkProperties(in NetworkRequest networkRequest);
- void requestNetworkCapabilities(in NetworkRequest networkRequest);
void releaseNetworkRequest(in NetworkRequest networkRequest);
void setAcceptUnvalidated(in Network network, boolean accept, boolean always);
diff --git a/core/java/android/net/IpSecAlgorithm.java b/core/java/android/net/IpSecAlgorithm.java
new file mode 100644
index 0000000..da5cb37
--- /dev/null
+++ b/core/java/android/net/IpSecAlgorithm.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2017 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.annotation.StringDef;
+import android.os.Parcel;
+import android.os.Parcelable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * IpSecAlgorithm specifies a single algorithm that can be applied to an IpSec Transform. Refer to
+ * RFC 4301.
+ */
+public final class IpSecAlgorithm implements Parcelable {
+
+ /**
+ * AES-CBC Encryption/Ciphering Algorithm.
+ *
+ * <p>Valid lengths for this key are {128, 192, 256}.
+ */
+ public static final String ALGO_CRYPT_AES_CBC = "cbc(aes)";
+
+ /**
+ * MD5 HMAC Authentication/Integrity Algorithm. This algorithm is not recommended for use in new
+ * applications and is provided for legacy compatibility with 3gpp infrastructure.
+ *
+ * <p>Valid truncation lengths are multiples of 8 bits from 96 to (default) 128.
+ */
+ public static final String ALGO_AUTH_HMAC_MD5 = "hmac(md5)";
+
+ /**
+ * SHA1 HMAC Authentication/Integrity Algorithm. This algorithm is not recommended for use in
+ * new applications and is provided for legacy compatibility with 3gpp infrastructure.
+ *
+ * <p>Valid truncation lengths are multiples of 8 bits from 96 to (default) 160.
+ */
+ public static final String ALGO_AUTH_HMAC_SHA1 = "hmac(sha1)";
+
+ /**
+ * SHA256 HMAC Authentication/Integrity Algorithm.
+ *
+ * <p>Valid truncation lengths are multiples of 8 bits from 96 to (default) 256.
+ */
+ public static final String ALGO_AUTH_HMAC_SHA256 = "hmac(sha256)";
+
+ /**
+ * SHA384 HMAC Authentication/Integrity Algorithm.
+ *
+ * <p>Valid truncation lengths are multiples of 8 bits from 192 to (default) 384.
+ */
+ public static final String ALGO_AUTH_HMAC_SHA384 = "hmac(sha384)";
+ /**
+ * SHA512 HMAC Authentication/Integrity Algorithm
+ *
+ * <p>Valid truncation lengths are multiples of 8 bits from 256 to (default) 512.
+ */
+ public static final String ALGO_AUTH_HMAC_SHA512 = "hmac(sha512)";
+
+ /** @hide */
+ @StringDef({
+ ALGO_CRYPT_AES_CBC,
+ ALGO_AUTH_HMAC_MD5,
+ ALGO_AUTH_HMAC_SHA1,
+ ALGO_AUTH_HMAC_SHA256,
+ ALGO_AUTH_HMAC_SHA512
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AlgorithmName {}
+
+ private final String mName;
+ private final byte[] mKey;
+ private final int mTruncLenBits;
+
+ /**
+ * Specify a IpSecAlgorithm of one of the supported types including the truncation length of the
+ * algorithm
+ *
+ * @param algorithm type for IpSec.
+ * @param key non-null Key padded to a multiple of 8 bits.
+ */
+ public IpSecAlgorithm(String algorithm, byte[] key) {
+ this(algorithm, key, key.length * 8);
+ }
+
+ /**
+ * Specify a IpSecAlgorithm of one of the supported types including the truncation length of the
+ * algorithm
+ *
+ * @param algoName precise name of the algorithm to be used.
+ * @param key non-null Key padded to a multiple of 8 bits.
+ * @param truncLenBits the number of bits of output hash to use; only meaningful for
+ * Authentication.
+ */
+ public IpSecAlgorithm(@AlgorithmName String algoName, byte[] key, int truncLenBits) {
+ if (!isTruncationLengthValid(algoName, truncLenBits)) {
+ throw new IllegalArgumentException("Unknown algorithm or invalid length");
+ }
+ mName = algoName;
+ mKey = key.clone();
+ mTruncLenBits = Math.min(truncLenBits, key.length * 8);
+ }
+
+ /** Retrieve the algorithm name */
+ public String getName() {
+ return mName;
+ }
+
+ /** Retrieve the key for this algorithm */
+ public byte[] getKey() {
+ return mKey.clone();
+ }
+
+ /**
+ * Retrieve the truncation length, in bits, for the key in this algo. By default this will be
+ * the length in bits of the key.
+ */
+ public int getTruncationLengthBits() {
+ return mTruncLenBits;
+ }
+
+ /* Parcelable Implementation */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Write to parcel */
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeString(mName);
+ out.writeByteArray(mKey);
+ out.writeInt(mTruncLenBits);
+ }
+
+ /** Parcelable Creator */
+ public static final Parcelable.Creator<IpSecAlgorithm> CREATOR =
+ new Parcelable.Creator<IpSecAlgorithm>() {
+ public IpSecAlgorithm createFromParcel(Parcel in) {
+ return new IpSecAlgorithm(in);
+ }
+
+ public IpSecAlgorithm[] newArray(int size) {
+ return new IpSecAlgorithm[size];
+ }
+ };
+
+ private IpSecAlgorithm(Parcel in) {
+ mName = in.readString();
+ mKey = in.createByteArray();
+ mTruncLenBits = in.readInt();
+ }
+
+ private static boolean isTruncationLengthValid(String algo, int truncLenBits) {
+ switch (algo) {
+ case ALGO_AUTH_HMAC_MD5:
+ return (truncLenBits >= 96 && truncLenBits <= 128);
+ case ALGO_AUTH_HMAC_SHA1:
+ return (truncLenBits >= 96 && truncLenBits <= 160);
+ case ALGO_AUTH_HMAC_SHA256:
+ return (truncLenBits >= 96 && truncLenBits <= 256);
+ case ALGO_AUTH_HMAC_SHA384:
+ return (truncLenBits >= 192 && truncLenBits <= 384);
+ case ALGO_AUTH_HMAC_SHA512:
+ return (truncLenBits >= 256 && truncLenBits <= 512);
+ default:
+ return false;
+ }
+ }
+};
diff --git a/core/java/android/net/IpSecConfig.aidl b/core/java/android/net/IpSecConfig.aidl
new file mode 100644
index 0000000..eaefca7
--- /dev/null
+++ b/core/java/android/net/IpSecConfig.aidl
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2017 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;
+
+/** @hide */
+parcelable IpSecConfig;
diff --git a/core/java/android/net/IpSecConfig.java b/core/java/android/net/IpSecConfig.java
new file mode 100644
index 0000000..b58bf42
--- /dev/null
+++ b/core/java/android/net/IpSecConfig.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2017 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.Parcel;
+import android.os.Parcelable;
+import android.util.Log;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/** @hide */
+public final class IpSecConfig implements Parcelable {
+ private static final String TAG = IpSecConfig.class.getSimpleName();
+
+ //MODE_TRANSPORT or MODE_TUNNEL
+ int mode;
+
+ // For tunnel mode
+ InetAddress localAddress;
+
+ InetAddress remoteAddress;
+
+ // Limit selection by network interface
+ Network network;
+
+ public static class Flow {
+ // Minimum requirements for identifying a transform
+ // SPI identifying the IPsec flow in packet processing
+ // and a remote IP address
+ int spi;
+
+ // Encryption Algorithm
+ IpSecAlgorithm encryptionAlgo;
+
+ // Authentication Algorithm
+ IpSecAlgorithm authenticationAlgo;
+ }
+
+ Flow[] flow = new Flow[2];
+
+ // For tunnel mode IPv4 UDP Encapsulation
+ // IpSecTransform#ENCAP_ESP_*, such as ENCAP_ESP_OVER_UDP_IKE
+ int encapType;
+ int encapLocalPort;
+ int encapRemotePort;
+
+ // An optional protocol to match with the selector
+ int selectorProto;
+
+ // A bitmask of FEATURE_* indicating which of the fields
+ // of this class are valid.
+ long features;
+
+ // An interval, in seconds between the NattKeepalive packets
+ int nattKeepaliveInterval;
+
+ public InetAddress getLocalIp() {
+ return localAddress;
+ }
+
+ public int getSpi(int direction) {
+ return flow[direction].spi;
+ }
+
+ public InetAddress getRemoteIp() {
+ return remoteAddress;
+ }
+
+ public IpSecAlgorithm getEncryptionAlgo(int direction) {
+ return flow[direction].encryptionAlgo;
+ }
+
+ public IpSecAlgorithm getAuthenticationAlgo(int direction) {
+ return flow[direction].authenticationAlgo;
+ }
+
+ Network getNetwork() {
+ return network;
+ }
+
+ public int getEncapType() {
+ return encapType;
+ }
+
+ public int getEncapLocalPort() {
+ return encapLocalPort;
+ }
+
+ public int getEncapRemotePort() {
+ return encapRemotePort;
+ }
+
+ public int getSelectorProto() {
+ return selectorProto;
+ }
+
+ int getNattKeepaliveInterval() {
+ return nattKeepaliveInterval;
+ }
+
+ public boolean hasProperty(int featureBits) {
+ return (features & featureBits) == featureBits;
+ }
+
+ // Parcelable Methods
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeLong(features);
+ // TODO: Use a byte array or other better method for storing IPs that can also include scope
+ out.writeString((localAddress != null) ? localAddress.getHostAddress() : null);
+ // TODO: Use a byte array or other better method for storing IPs that can also include scope
+ out.writeString((remoteAddress != null) ? remoteAddress.getHostAddress() : null);
+ out.writeParcelable(network, flags);
+ out.writeInt(flow[IpSecTransform.DIRECTION_IN].spi);
+ out.writeParcelable(flow[IpSecTransform.DIRECTION_IN].encryptionAlgo, flags);
+ out.writeParcelable(flow[IpSecTransform.DIRECTION_IN].authenticationAlgo, flags);
+ out.writeInt(flow[IpSecTransform.DIRECTION_OUT].spi);
+ out.writeParcelable(flow[IpSecTransform.DIRECTION_OUT].encryptionAlgo, flags);
+ out.writeParcelable(flow[IpSecTransform.DIRECTION_OUT].authenticationAlgo, flags);
+ out.writeInt(encapType);
+ out.writeInt(encapLocalPort);
+ out.writeInt(encapRemotePort);
+ out.writeInt(selectorProto);
+ }
+
+ // Package Private: Used by the IpSecTransform.Builder;
+ // there should be no public constructor for this object
+ IpSecConfig() {
+ flow[IpSecTransform.DIRECTION_IN].spi = 0;
+ flow[IpSecTransform.DIRECTION_OUT].spi = 0;
+ nattKeepaliveInterval = 0; //FIXME constant
+ }
+
+ private static InetAddress readInetAddressFromParcel(Parcel in) {
+ String addrString = in.readString();
+ if (addrString == null) {
+ return null;
+ }
+ try {
+ return InetAddress.getByName(addrString);
+ } catch (UnknownHostException e) {
+ Log.wtf(TAG, "Invalid IpAddress " + addrString);
+ return null;
+ }
+ }
+
+ private IpSecConfig(Parcel in) {
+ features = in.readLong();
+ localAddress = readInetAddressFromParcel(in);
+ remoteAddress = readInetAddressFromParcel(in);
+ network = (Network) in.readParcelable(Network.class.getClassLoader());
+ flow[IpSecTransform.DIRECTION_IN].spi = in.readInt();
+ flow[IpSecTransform.DIRECTION_IN].encryptionAlgo =
+ (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
+ flow[IpSecTransform.DIRECTION_IN].authenticationAlgo =
+ (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
+ flow[IpSecTransform.DIRECTION_OUT].spi = in.readInt();
+ flow[IpSecTransform.DIRECTION_OUT].encryptionAlgo =
+ (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
+ flow[IpSecTransform.DIRECTION_OUT].authenticationAlgo =
+ (IpSecAlgorithm) in.readParcelable(IpSecAlgorithm.class.getClassLoader());
+ encapType = in.readInt();
+ encapLocalPort = in.readInt();
+ encapRemotePort = in.readInt();
+ selectorProto = in.readInt();
+ }
+
+ public static final Parcelable.Creator<IpSecConfig> CREATOR =
+ new Parcelable.Creator<IpSecConfig>() {
+ public IpSecConfig createFromParcel(Parcel in) {
+ return new IpSecConfig(in);
+ }
+
+ public IpSecConfig[] newArray(int size) {
+ return new IpSecConfig[size];
+ }
+ };
+}
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
new file mode 100644
index 0000000..2c544e9
--- /dev/null
+++ b/core/java/android/net/IpSecManager.java
@@ -0,0 +1,379 @@
+/*
+ * Copyright (C) 2017 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 static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.os.INetworkManagementService;
+import android.os.ParcelFileDescriptor;
+import android.util.AndroidException;
+import dalvik.system.CloseGuard;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.Socket;
+
+/**
+ * This class contains methods for managing IPsec sessions, which will perform kernel-space
+ * encryption and decryption of socket or Network traffic.
+ *
+ * <p>An IpSecManager may be obtained by calling {@link
+ * android.content.Context#getSystemService(String) Context#getSystemService(String)} with {@link
+ * android.content.Context#IPSEC_SERVICE Context#IPSEC_SERVICE}
+ */
+public final class IpSecManager {
+ private static final String TAG = "IpSecManager";
+
+ /**
+ * Indicates that the combination of remote InetAddress and SPI was non-unique for a given
+ * request. If encountered, selection of a new SPI is required before a transform may be
+ * created. Note, this should happen very rarely if the SPI is chosen to be sufficiently random
+ * or reserved using reserveSecurityParameterIndex.
+ */
+ public static final class SpiUnavailableException extends AndroidException {
+ private final int mSpi;
+
+ /**
+ * Construct an exception indicating that a transform with the given SPI is already in use
+ * or otherwise unavailable.
+ *
+ * @param msg Description indicating the colliding SPI
+ * @param spi the SPI that could not be used due to a collision
+ */
+ SpiUnavailableException(String msg, int spi) {
+ super(msg + "(spi: " + spi + ")");
+ mSpi = spi;
+ }
+
+ /** Retrieve the SPI that caused a collision */
+ public int getSpi() {
+ return mSpi;
+ }
+ }
+
+ /**
+ * Indicates that the requested system resource for IPsec, such as a socket or other system
+ * resource is unavailable. If this exception is thrown, try releasing allocated objects of the
+ * type requested.
+ */
+ public static final class ResourceUnavailableException extends AndroidException {
+
+ ResourceUnavailableException(String msg) {
+ super(msg);
+ }
+ }
+
+ private final Context mContext;
+ private final INetworkManagementService mService;
+
+ public static final class SecurityParameterIndex implements AutoCloseable {
+ private final Context mContext;
+ private final InetAddress mDestinationAddress;
+ private final CloseGuard mCloseGuard = CloseGuard.get();
+ private int mSpi;
+
+ /** Return the underlying SPI held by this object */
+ public int getSpi() {
+ return mSpi;
+ }
+
+ private SecurityParameterIndex(Context context, InetAddress destinationAddress, int spi)
+ throws ResourceUnavailableException, SpiUnavailableException {
+ mContext = context;
+ mDestinationAddress = destinationAddress;
+ mSpi = spi;
+ mCloseGuard.open("open");
+ }
+
+ /**
+ * Release an SPI that was previously reserved.
+ *
+ * <p>Release an SPI for use by other users in the system. This will fail if the SPI is
+ * currently in use by an IpSecTransform.
+ *
+ * @param destinationAddress SPIs must be unique for each combination of SPI and destination
+ * address. Thus, the destinationAddress to which the SPI will communicate must be
+ * supplied.
+ * @param spi the previously reserved SPI to be freed.
+ */
+ @Override
+ public void close() {
+ mSpi = INVALID_SECURITY_PARAMETER_INDEX; // TODO: Invalid SPI
+ mCloseGuard.close();
+ }
+
+ @Override
+ protected void finalize() {
+ if (mCloseGuard != null) {
+ mCloseGuard.warnIfOpen();
+ }
+
+ close();
+ }
+ }
+
+ /**
+ * The Security Parameter Index, SPI, 0 indicates an unknown or invalid index.
+ *
+ * <p>No IPsec packet may contain an SPI of 0.
+ */
+ public static final int INVALID_SECURITY_PARAMETER_INDEX = 0;
+
+ /**
+ * Reserve an SPI for traffic bound towards the specified destination address.
+ *
+ * <p>If successful, this SPI is guaranteed available until released by a call to {@link
+ * SecurityParameterIndex#close()}.
+ *
+ * @param destinationAddress SPIs must be unique for each combination of SPI and destination
+ * address.
+ * @param requestedSpi the requested SPI, or '0' to allocate a random SPI.
+ * @return the reserved SecurityParameterIndex
+ * @throws ResourceUnavailableException indicating that too many SPIs are currently allocated
+ * for this user
+ * @throws SpiUnavailableException indicating that a particular SPI cannot be reserved
+ */
+ public SecurityParameterIndex reserveSecurityParameterIndex(
+ InetAddress destinationAddress, int requestedSpi)
+ throws SpiUnavailableException, ResourceUnavailableException {
+ return new SecurityParameterIndex(mContext, destinationAddress, requestedSpi);
+ }
+
+ /**
+ * Apply an active Transport Mode IPsec Transform to a stream socket to perform IPsec
+ * encapsulation of the traffic flowing between the socket and the remote InetAddress of that
+ * transform. For security reasons, attempts to send traffic to any IP address other than the
+ * address associated with that transform will throw an IOException. In addition, if the
+ * IpSecTransform is later deactivated, the socket will throw an IOException on any calls to
+ * send() or receive() until the transform is removed from the socket by calling {@link
+ * #removeTransportModeTransform(Socket, IpSecTransform)};
+ *
+ * @param socket a stream socket
+ * @param transform an {@link IpSecTransform}, which must be an active Transport Mode transform.
+ */
+ public void applyTransportModeTransform(Socket socket, IpSecTransform transform)
+ throws IOException {
+ applyTransportModeTransform(ParcelFileDescriptor.fromSocket(socket), transform);
+ }
+
+ /**
+ * Apply an active Transport Mode IPsec Transform to a datagram socket to perform IPsec
+ * encapsulation of the traffic flowing between the socket and the remote InetAddress of that
+ * transform. For security reasons, attempts to send traffic to any IP address other than the
+ * address associated with that transform will throw an IOException. In addition, if the
+ * IpSecTransform is later deactivated, the socket will throw an IOException on any calls to
+ * send() or receive() until the transform is removed from the socket by calling {@link
+ * #removeTransportModeTransform(DatagramSocket, IpSecTransform)};
+ *
+ * @param socket a datagram socket
+ * @param transform an {@link IpSecTransform}, which must be an active Transport Mode transform.
+ */
+ public void applyTransportModeTransform(DatagramSocket socket, IpSecTransform transform)
+ throws IOException {
+ applyTransportModeTransform(ParcelFileDescriptor.fromDatagramSocket(socket), transform);
+ }
+
+ /* Call down to activate a transform */
+ private void applyTransportModeTransform(ParcelFileDescriptor pfd, IpSecTransform transform) {}
+
+ /**
+ * Apply an active Tunnel Mode IPsec Transform to a network, which will tunnel all traffic to
+ * and from that network's interface with IPsec (applies an outer IP header and IPsec Header to
+ * all traffic, and expects an additional IP header and IPsec Header on all inbound traffic).
+ * Applications should probably not use this API directly. Instead, they should use {@link
+ * VpnService} to provide VPN capability in a more generic fashion.
+ *
+ * @param net a {@link Network} that will be tunneled via IP Sec.
+ * @param transform an {@link IpSecTransform}, which must be an active Tunnel Mode transform.
+ * @hide
+ */
+ @SystemApi
+ public void applyTunnelModeTransform(Network net, IpSecTransform transform) {}
+
+ /**
+ * Remove a transform from a given stream socket. Once removed, traffic on the socket will not
+ * be encypted. This allows sockets that have been used for IPsec to be reclaimed for
+ * communication in the clear in the event socket reuse is desired. This operation will succeed
+ * regardless of the underlying state of a transform. If a transform is removed, communication
+ * on all sockets to which that transform was applied will fail until this method is called.
+ *
+ * @param socket a socket that previously had a transform applied to it.
+ * @param transform the IPsec Transform that was previously applied to the given socket
+ */
+ public void removeTransportModeTransform(Socket socket, IpSecTransform transform) {
+ removeTransportModeTransform(ParcelFileDescriptor.fromSocket(socket), transform);
+ }
+
+ /**
+ * Remove a transform from a given datagram socket. Once removed, traffic on the socket will not
+ * be encypted. This allows sockets that have been used for IPsec to be reclaimed for
+ * communication in the clear in the event socket reuse is desired. This operation will succeed
+ * regardless of the underlying state of a transform. If a transform is removed, communication
+ * on all sockets to which that transform was applied will fail until this method is called.
+ *
+ * @param socket a socket that previously had a transform applied to it.
+ * @param transform the IPsec Transform that was previously applied to the given socket
+ */
+ public void removeTransportModeTransform(DatagramSocket socket, IpSecTransform transform) {
+ removeTransportModeTransform(ParcelFileDescriptor.fromDatagramSocket(socket), transform);
+ }
+
+ /* Call down to activate a transform */
+ private void removeTransportModeTransform(ParcelFileDescriptor pfd, IpSecTransform transform) {}
+
+ /**
+ * Remove a Tunnel Mode IPsec Transform from a {@link Network}. This must be used as part of
+ * cleanup if a tunneled Network experiences a change in default route. The Network will drop
+ * all traffic that cannot be routed to the Tunnel's outbound interface. If that interface is
+ * lost, all traffic will drop.
+ *
+ * @param net a network that currently has transform applied to it.
+ * @param transform a Tunnel Mode IPsec Transform that has been previously applied to the given
+ * network
+ * @hide
+ */
+ @SystemApi
+ public void removeTunnelModeTransform(Network net, IpSecTransform transform) {}
+
+ /**
+ * Class providing access to a system-provided UDP Encapsulation Socket, which may be used for
+ * IKE signalling as well as for inbound and outbound UDP encapsulated IPsec traffic.
+ *
+ * <p>The socket provided by this class cannot be re-bound or closed via the inner
+ * FileDescriptor. Instead, disposing of this socket requires a call to close().
+ */
+ public static final class UdpEncapsulationSocket implements AutoCloseable {
+ private final FileDescriptor mFd;
+ private final Context mContext;
+ private final CloseGuard mCloseGuard = CloseGuard.get();
+
+ private UdpEncapsulationSocket(Context context, int port)
+ throws ResourceUnavailableException {
+ mContext = context;
+ mCloseGuard.open("constructor");
+ // TODO: go down to the kernel and get a socket on the specified
+ mFd = new FileDescriptor();
+ }
+
+ private UdpEncapsulationSocket(Context context) throws ResourceUnavailableException {
+ mContext = context;
+ mCloseGuard.open("constructor");
+ // TODO: go get a random socket on a random port
+ mFd = new FileDescriptor();
+ }
+
+ /** Access the inner UDP Encapsulation Socket */
+ public FileDescriptor getSocket() {
+ return mFd;
+ }
+
+ /** Retrieve the port number of the inner encapsulation socket */
+ public int getPort() {
+ return 0; // TODO get the port number from the Socket;
+ }
+
+ @Override
+ /**
+ * Release the resources that have been reserved for this Socket.
+ *
+ * <p>This method closes the underlying socket, reducing a user's allocated sockets in the
+ * system. This must be done as part of cleanup following use of a socket. Failure to do so
+ * will cause the socket to count against a total allocation limit for IpSec and eventually
+ * fail due to resource limits.
+ *
+ * @param fd a file descriptor previously returned as a UDP Encapsulation socket.
+ */
+ public void close() {
+ // TODO: Go close the socket
+ mCloseGuard.close();
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ if (mCloseGuard != null) {
+ mCloseGuard.warnIfOpen();
+ }
+
+ close();
+ }
+ };
+
+ /**
+ * Open a socket that is bound to a free UDP port on the system.
+ *
+ * <p>By binding in this manner and holding the FileDescriptor, the socket cannot be un-bound by
+ * the caller. This provides safe access to a socket on a port that can later be used as a UDP
+ * Encapsulation port.
+ *
+ * <p>This socket reservation works in conjunction with IpSecTransforms, which may re-use the
+ * socket port. Explicitly opening this port is only necessary if communication is desired on
+ * that port.
+ *
+ * @param port a local UDP port to be reserved for UDP Encapsulation. is provided, then this
+ * method will bind to the specified port or fail. To retrieve the port number, call {@link
+ * android.system.Os#getsockname(FileDescriptor)}.
+ * @return a {@link UdpEncapsulationSocket} that is bound to the requested port for the lifetime
+ * of the object.
+ */
+ // Returning a socket in this fashion that has been created and bound by the system
+ // is the only safe way to ensure that a socket is both accessible to the user and
+ // safely usable for Encapsulation without allowing a user to possibly unbind from/close
+ // the port, which could potentially impact the traffic of the next user who binds to that
+ // socket.
+ public UdpEncapsulationSocket openUdpEncapsulationSocket(int port)
+ throws IOException, ResourceUnavailableException {
+ // Temporary code
+ return new UdpEncapsulationSocket(mContext, port);
+ }
+
+ /**
+ * Open a socket that is bound to a port selected by the system.
+ *
+ * <p>By binding in this manner and holding the FileDescriptor, the socket cannot be un-bound by
+ * the caller. This provides safe access to a socket on a port that can later be used as a UDP
+ * Encapsulation port.
+ *
+ * <p>This socket reservation works in conjunction with IpSecTransforms, which may re-use the
+ * socket port. Explicitly opening this port is only necessary if communication is desired on
+ * that port.
+ *
+ * @return a {@link UdpEncapsulationSocket} that is bound to an arbitrarily selected port
+ */
+ // Returning a socket in this fashion that has been created and bound by the system
+ // is the only safe way to ensure that a socket is both accessible to the user and
+ // safely usable for Encapsulation without allowing a user to possibly unbind from/close
+ // the port, which could potentially impact the traffic of the next user who binds to that
+ // socket.
+ public UdpEncapsulationSocket openUdpEncapsulationSocket()
+ throws IOException, ResourceUnavailableException {
+ // Temporary code
+ return new UdpEncapsulationSocket(mContext);
+ }
+
+ /**
+ * Retrieve an instance of an IpSecManager within you application context
+ *
+ * @param context the application context for this manager
+ * @hide
+ */
+ public IpSecManager(Context context, INetworkManagementService service) {
+ mContext = checkNotNull(context, "missing context");
+ mService = checkNotNull(service, "missing service");
+ }
+}
diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java
new file mode 100644
index 0000000..d6dd28b
--- /dev/null
+++ b/core/java/android/net/IpSecTransform.java
@@ -0,0 +1,471 @@
+/*
+ * Copyright (C) 2017 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.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.content.Context;
+import android.system.ErrnoException;
+import android.util.Log;
+import dalvik.system.CloseGuard;
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.net.InetAddress;
+
+/**
+ * This class represents an IpSecTransform, which encapsulates both properties and state of IPsec.
+ *
+ * <p>IpSecTransforms must be built from an IpSecTransform.Builder, and they must persist throughout
+ * the lifetime of the underlying transform. If a transform object leaves scope, the underlying
+ * transform may be disabled automatically, with likely undesirable results.
+ *
+ * <p>An IpSecTransform may either represent a tunnel mode transform that operates on a wide array
+ * of traffic or may represent a transport mode transform operating on a Socket or Sockets.
+ */
+public final class IpSecTransform implements AutoCloseable {
+ private static final String TAG = "IpSecTransform";
+
+ /**
+ * For direction-specific attributes of an IpSecTransform, indicates that an attribute applies
+ * to traffic towards the host.
+ */
+ public static final int DIRECTION_IN = 0;
+
+ /**
+ * For direction-specific attributes of an IpSecTransform, indicates that an attribute applies
+ * to traffic from the host.
+ */
+ public static final int DIRECTION_OUT = 1;
+
+ /** @hide */
+ @IntDef(value = {DIRECTION_IN, DIRECTION_OUT})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface TransformDirection {}
+
+ /** @hide */
+ private static final int MODE_TUNNEL = 0;
+
+ /** @hide */
+ private static final int MODE_TRANSPORT = 1;
+
+ /** @hide */
+ public static final int ENCAP_NONE = 0;
+
+ /**
+ * IpSec traffic will be encapsulated within UDP as per <a
+ * href="https://tools.ietf.org/html/rfc3948">RFC3498</a>.
+ *
+ * @hide
+ */
+ public static final int ENCAP_ESPINUDP = 1;
+
+ /**
+ * IpSec traffic will be encapsulated within a UDP header with an additional 8-byte header pad
+ * (of '0'-value bytes) that prevents traffic from being interpreted as IKE or as ESP over UDP.
+ *
+ * @hide
+ */
+ public static final int ENCAP_ESPINUDP_NONIKE = 2;
+
+ /** @hide */
+ @IntDef(value = {ENCAP_NONE, ENCAP_ESPINUDP, ENCAP_ESPINUDP_NONIKE})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface EncapType {}
+
+ /**
+ * Sentinel for an invalid transform (means that this transform is inactive).
+ *
+ * @hide
+ */
+ public static final int INVALID_TRANSFORM_ID = -1;
+
+ private IpSecTransform(Context context, IpSecConfig config) {
+ mContext = context;
+ mConfig = config;
+ mTransformId = INVALID_TRANSFORM_ID;
+ }
+
+ private IpSecTransform activate()
+ throws IOException, IpSecManager.ResourceUnavailableException,
+ IpSecManager.SpiUnavailableException {
+ int transformId;
+ synchronized (this) {
+ //try {
+ transformId = INVALID_TRANSFORM_ID;
+ //} catch (RemoteException e) {
+ // throw e.rethrowFromSystemServer();
+ //}
+
+ if (transformId < 0) {
+ throw new ErrnoException("addTransform", -transformId).rethrowAsIOException();
+ }
+
+ startKeepalive(mContext); // Will silently fail if not required
+ mTransformId = transformId;
+ Log.d(TAG, "Added Transform with Id " + transformId);
+ }
+ mCloseGuard.open("build");
+
+ return this;
+ }
+
+ /**
+ * Deactivate an IpSecTransform and free all resources for that transform that are managed by
+ * the system for this Transform.
+ *
+ * <p>Deactivating a transform while it is still applied to any Socket will result in sockets
+ * refusing to send or receive data. This method will silently succeed if the specified
+ * transform has already been removed; thus, it is always safe to attempt cleanup when a
+ * transform is no longer needed.
+ */
+ public void close() {
+ Log.d(TAG, "Removing Transform with Id " + mTransformId);
+
+ // Always safe to attempt cleanup
+ if (mTransformId == INVALID_TRANSFORM_ID) {
+ return;
+ }
+ //try {
+ stopKeepalive();
+ //} catch (RemoteException e) {
+ // transform.setTransformId(transformId);
+ // throw e.rethrowFromSystemServer();
+ //} finally {
+ mTransformId = INVALID_TRANSFORM_ID;
+ //}
+ mCloseGuard.close();
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ if (mCloseGuard != null) {
+ mCloseGuard.warnIfOpen();
+ }
+ close();
+ }
+
+ /* Package */
+ IpSecConfig getConfig() {
+ return mConfig;
+ }
+
+ private final IpSecConfig mConfig;
+ private int mTransformId;
+ private final Context mContext;
+ private final CloseGuard mCloseGuard = CloseGuard.get();
+ private ConnectivityManager.PacketKeepalive mKeepalive;
+ private int mKeepaliveStatus = ConnectivityManager.PacketKeepalive.NO_KEEPALIVE;
+ private Object mKeepaliveSyncLock = new Object();
+ private ConnectivityManager.PacketKeepaliveCallback mKeepaliveCallback =
+ new ConnectivityManager.PacketKeepaliveCallback() {
+
+ @Override
+ public void onStarted() {
+ synchronized (mKeepaliveSyncLock) {
+ mKeepaliveStatus = ConnectivityManager.PacketKeepalive.SUCCESS;
+ mKeepaliveSyncLock.notifyAll();
+ }
+ }
+
+ @Override
+ public void onStopped() {
+ synchronized (mKeepaliveSyncLock) {
+ mKeepaliveStatus = ConnectivityManager.PacketKeepalive.NO_KEEPALIVE;
+ mKeepaliveSyncLock.notifyAll();
+ }
+ }
+
+ @Override
+ public void onError(int error) {
+ synchronized (mKeepaliveSyncLock) {
+ mKeepaliveStatus = error;
+ mKeepaliveSyncLock.notifyAll();
+ }
+ }
+ };
+
+ /* Package */
+ void startKeepalive(Context c) {
+ if (mConfig.getNattKeepaliveInterval() == 0) {
+ return;
+ }
+
+ ConnectivityManager cm =
+ (ConnectivityManager) c.getSystemService(Context.CONNECTIVITY_SERVICE);
+
+ if (mKeepalive != null) {
+ Log.e(TAG, "Keepalive already started for this IpSecTransform.");
+ return;
+ }
+
+ synchronized (mKeepaliveSyncLock) {
+ mKeepalive =
+ cm.startNattKeepalive(
+ mConfig.getNetwork(),
+ mConfig.getNattKeepaliveInterval(),
+ mKeepaliveCallback,
+ mConfig.getLocalIp(),
+ mConfig.getEncapLocalPort(),
+ mConfig.getRemoteIp());
+ try {
+ mKeepaliveSyncLock.wait(2000);
+ } catch (InterruptedException e) {
+ }
+ }
+ if (mKeepaliveStatus != ConnectivityManager.PacketKeepalive.SUCCESS) {
+ throw new UnsupportedOperationException("Packet Keepalive cannot be started");
+ }
+ }
+
+ /* Package */
+ void stopKeepalive() {
+ if (mKeepalive == null) {
+ return;
+ }
+ mKeepalive.stop();
+ synchronized (mKeepaliveSyncLock) {
+ if (mKeepaliveStatus == ConnectivityManager.PacketKeepalive.SUCCESS) {
+ try {
+ mKeepaliveSyncLock.wait(2000);
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ }
+
+ /* Package */
+ void setTransformId(int transformId) {
+ mTransformId = transformId;
+ }
+
+ /* Package */
+ int getTransformId() {
+ return mTransformId;
+ }
+
+ /**
+ * Builder object to facilitate the creation of IpSecTransform objects.
+ *
+ * <p>Apply additional properties to the transform and then call a build() method to return an
+ * IpSecTransform object.
+ *
+ * @see Builder#buildTransportModeTransform(InetAddress)
+ */
+ public static class Builder {
+ private Context mContext;
+ private IpSecConfig mConfig;
+
+ /**
+ * Add an encryption algorithm to the transform for the given direction.
+ *
+ * <p>If encryption is set for a given direction without also providing an SPI for that
+ * direction, creation of an IpSecTransform will fail upon calling a build() method.
+ *
+ * @param direction either {@link #DIRECTION_IN or #DIRECTION_OUT}
+ * @param algo {@link IpSecAlgorithm} specifying the encryption to be applied.
+ */
+ public IpSecTransform.Builder setEncryption(
+ @TransformDirection int direction, IpSecAlgorithm algo) {
+ mConfig.flow[direction].encryptionAlgo = algo;
+ return this;
+ }
+
+ /**
+ * Add an authentication/integrity algorithm to the transform.
+ *
+ * <p>If authentication is set for a given direction without also providing an SPI for that
+ * direction, creation of an IpSecTransform will fail upon calling a build() method.
+ *
+ * @param direction either {@link #DIRECTION_IN or #DIRECTION_OUT}
+ * @param algo {@link IpSecAlgorithm} specifying the authentication to be applied.
+ */
+ public IpSecTransform.Builder setAuthentication(
+ @TransformDirection int direction, IpSecAlgorithm algo) {
+ mConfig.flow[direction].authenticationAlgo = algo;
+ return this;
+ }
+
+ /**
+ * Set the SPI, which uniquely identifies a particular IPsec session from others. Because
+ * IPsec operates at the IP layer, this 32-bit identifier uniquely identifies packets to a
+ * given destination address.
+ *
+ * <p>Care should be chosen when selecting an SPI to ensure that is is as unique as
+ * possible. Random number generation is a reasonable approach to selecting an SPI. For
+ * outbound SPIs, they must be reserved by calling {@link
+ * IpSecManager#reserveSecurityParameterIndex(InetAddress, int)}. Otherwise, Transforms will
+ * fail to build.
+ *
+ * <p>Unless an SPI is set for a given direction, traffic in that direction will be
+ * sent/received without any IPsec applied.
+ *
+ * @param direction either {@link #DIRECTION_IN or #DIRECTION_OUT}
+ * @param spi a unique 32-bit integer to identify transformed traffic
+ */
+ public IpSecTransform.Builder setSpi(@TransformDirection int direction, int spi) {
+ mConfig.flow[direction].spi = spi;
+ return this;
+ }
+
+ /**
+ * Set the SPI, which uniquely identifies a particular IPsec session from others. Because
+ * IPsec operates at the IP layer, this 32-bit identifier uniquely identifies packets to a
+ * given destination address.
+ *
+ * <p>Care should be chosen when selecting an SPI to ensure that is is as unique as
+ * possible. Random number generation is a reasonable approach to selecting an SPI. For
+ * outbound SPIs, they must be reserved by calling {@link
+ * IpSecManager#reserveSecurityParameterIndex(InetAddress, int)}. Otherwise, Transforms will
+ * fail to activate.
+ *
+ * <p>Unless an SPI is set for a given direction, traffic in that direction will be
+ * sent/received without any IPsec applied.
+ *
+ * @param direction either {@link #DIRECTION_IN or #DIRECTION_OUT}
+ * @param spi a unique {@link IpSecManager.SecurityParameterIndex} to identify transformed
+ * traffic
+ */
+ public IpSecTransform.Builder setSpi(
+ @TransformDirection int direction, IpSecManager.SecurityParameterIndex spi) {
+ mConfig.flow[direction].spi = spi.getSpi();
+ return this;
+ }
+
+ /**
+ * Specify the network on which this transform will emit its traffic; (otherwise it will
+ * emit on the default network).
+ *
+ * <p>Restricts the transformed traffic to a particular {@link Network}. This is required in
+ * tunnel mode.
+ *
+ * @hide
+ */
+ @SystemApi
+ public IpSecTransform.Builder setUnderlyingNetwork(Network net) {
+ mConfig.network = net;
+ return this;
+ }
+
+ /**
+ * Add UDP encapsulation to an IPv4 transform
+ *
+ * <p>This option allows IPsec traffic to pass through NAT. Refer to RFC 3947 and 3948 for
+ * details on how UDP should be applied to IPsec.
+ *
+ * @param localSocket a {@link IpSecManager.UdpEncapsulationSocket} for sending and
+ * receiving encapsulating traffic.
+ * @param remotePort the UDP port number of the remote that will send and receive
+ * encapsulated traffic. In the case of IKE, this is likely port 4500.
+ */
+ public IpSecTransform.Builder setIpv4Encapsulation(
+ IpSecManager.UdpEncapsulationSocket localSocket, int remotePort) {
+ // TODO: check encap type is valid.
+ mConfig.encapType = ENCAP_ESPINUDP;
+ mConfig.encapLocalPort = localSocket.getPort(); // TODO: plug in the encap socket
+ mConfig.encapRemotePort = remotePort;
+ return this;
+ }
+
+ // TODO: Decrease the minimum keepalive to maybe 10?
+ // TODO: Probably a better exception to throw for NATTKeepalive failure
+ // TODO: Specify the needed NATT keepalive permission.
+ /**
+ * Send a NATT Keepalive packet with a given maximum interval. This will create an offloaded
+ * request to do power-efficient NATT Keepalive. If NATT keepalive is requested but cannot
+ * be activated, then the transform will fail to activate and throw an IOException.
+ *
+ * @param intervalSeconds the maximum number of seconds between keepalive packets, no less
+ * than 20s and no more than 3600s.
+ * @hide
+ */
+ @SystemApi
+ public IpSecTransform.Builder setNattKeepalive(int intervalSeconds) {
+ mConfig.nattKeepaliveInterval = intervalSeconds;
+ return this;
+ }
+
+ /**
+ * Build and return an active {@link IpSecTransform} object as a Transport Mode Transform.
+ * Some parameters have interdependencies that are checked at build time. If a well-formed
+ * transform cannot be created from the supplied parameters, this method will throw an
+ * Exception.
+ *
+ * <p>Upon a successful return from this call, the provided IpSecTransform will be active
+ * and may be applied to sockets. If too many IpSecTransform objects are active for a given
+ * user this operation will fail and throw ResourceUnavailableException. To avoid these
+ * exceptions, unused Transform objects must be cleaned up by calling {@link
+ * IpSecTransform#close()} when they are no longer needed.
+ *
+ * @param remoteAddress the {@link InetAddress} that, when matched on traffic to/from this
+ * socket will cause the transform to be applied.
+ * <p>Note that an active transform will not impact any network traffic until it has
+ * been applied to one or more Sockets. Calling this method is a necessary precondition
+ * for applying it to a socket, but is not sufficient to actually apply IPsec.
+ * @throws IllegalArgumentException indicating that a particular combination of transform
+ * properties is invalid.
+ * @throws IpSecManager.ResourceUnavailableException in the event that no more Transforms
+ * may be allocated
+ * @throws SpiUnavailableException if the SPI collides with an existing transform
+ * (unlikely).
+ * @throws ResourceUnavailableException if the current user currently has exceeded the
+ * number of allowed active transforms.
+ */
+ public IpSecTransform buildTransportModeTransform(InetAddress remoteAddress)
+ throws IpSecManager.ResourceUnavailableException,
+ IpSecManager.SpiUnavailableException, IOException {
+ //FIXME: argument validation here
+ //throw new IllegalArgumentException("Natt Keepalive requires UDP Encapsulation");
+ mConfig.mode = MODE_TRANSPORT;
+ mConfig.remoteAddress = remoteAddress;
+ return new IpSecTransform(mContext, mConfig).activate();
+ }
+
+ /**
+ * Build and return an {@link IpSecTransform} object as a Tunnel Mode Transform. Some
+ * parameters have interdependencies that are checked at build time.
+ *
+ * @param localAddress the {@link InetAddress} that provides the local endpoint for this
+ * IPsec tunnel. This is almost certainly an address belonging to the {@link Network}
+ * that will originate the traffic, which is set as the {@link #setUnderlyingNetwork}.
+ * @param remoteAddress the {@link InetAddress} representing the remote endpoint of this
+ * IPsec tunnel.
+ * @throws IllegalArgumentException indicating that a particular combination of transform
+ * properties is invalid.
+ * @hide
+ */
+ @SystemApi
+ public IpSecTransform buildTunnelModeTransform(
+ InetAddress localAddress, InetAddress remoteAddress) {
+ //FIXME: argument validation here
+ //throw new IllegalArgumentException("Natt Keepalive requires UDP Encapsulation");
+ mConfig.localAddress = localAddress;
+ mConfig.remoteAddress = remoteAddress;
+ mConfig.mode = MODE_TUNNEL;
+ return new IpSecTransform(mContext, mConfig);
+ }
+
+ /**
+ * Create a new IpSecTransform.Builder to construct an IpSecTransform
+ *
+ * @param context current Context
+ */
+ public Builder(Context context) {
+ mContext = context;
+ mConfig = new IpSecConfig();
+ }
+ }
+}
diff --git a/core/java/android/nfc/NdefRecord.java b/core/java/android/nfc/NdefRecord.java
index bd32314..2c9ce3f 100644
--- a/core/java/android/nfc/NdefRecord.java
+++ b/core/java/android/nfc/NdefRecord.java
@@ -839,7 +839,7 @@
if (cf && !inChunk) {
// first chunk
- if (typeLength == 0) {
+ if (typeLength == 0 && tnf != NdefRecord.TNF_UNKNOWN) {
throw new FormatException("expected non-zero type length in first chunk");
}
chunks.clear();
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index f6edee0..0d269af 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -245,7 +245,7 @@
* Magic version number for a current development build, which has
* not yet turned into an official release.
*/
- public static final int CUR_DEVELOPMENT = 10000;
+ public static final int CUR_DEVELOPMENT = VMRuntime.SDK_VERSION_CUR_DEVELOPMENT;
/**
* October 2008: The original, first, version of Android. Yay!
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 4616af8..30d8e2d 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -296,11 +296,6 @@
}
/** {@hide} */
- public static File getDataProfilesDeForeignDexDirectory(int userId) {
- return buildPath(getDataProfilesDeDirectory(userId), "foreign-dex");
- }
-
- /** {@hide} */
public static File getDataAppDirectory(String volumeUuid) {
return new File(getDataDirectory(volumeUuid), "app");
}
diff --git a/core/java/android/os/HwBinder.java b/core/java/android/os/HwBinder.java
index e025494..b09c51c 100644
--- a/core/java/android/os/HwBinder.java
+++ b/core/java/android/os/HwBinder.java
@@ -43,9 +43,7 @@
int code, HwParcel request, HwParcel reply, int flags)
throws RemoteException;
- public native final void registerService(
- ArrayList<String> interfaceChain,
- String serviceName)
+ public native final void registerService(String serviceName)
throws RemoteException;
public static native final IHwBinder getService(
diff --git a/core/java/android/os/ShellCommand.java b/core/java/android/os/ShellCommand.java
index fc804e5..0b4c4c1 100644
--- a/core/java/android/os/ShellCommand.java
+++ b/core/java/android/os/ShellCommand.java
@@ -274,7 +274,7 @@
/**
* Implement parsing and execution of a command. If it isn't a command you understand,
* call {@link #handleDefaultCommands(String)} and return its result as a last resort.
- * User {@link #getNextOption()}, {@link #getNextArg()}, and {@link #getNextArgRequired()}
+ * Use {@link #getNextOption()}, {@link #getNextArg()}, and {@link #getNextArgRequired()}
* to process additional command line arguments. Command output can be written to
* {@link #getOutPrintWriter()} and errors to {@link #getErrPrintWriter()}.
*
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java
index ff69cf6..fa7aebf 100644
--- a/core/java/android/os/StrictMode.java
+++ b/core/java/android/os/StrictMode.java
@@ -477,7 +477,7 @@
}
/**
- * Detect unbuffered input/output operations.
+ * Disable detection of unbuffered input/output operations.
*/
public Builder permitUnbufferedIo() {
return disable(DETECT_UNBUFFERED_IO);
diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java
index 6a751e8..2bf3c2c 100644
--- a/core/java/android/os/SystemProperties.java
+++ b/core/java/android/os/SystemProperties.java
@@ -35,7 +35,6 @@
private static final String TAG = "SystemProperties";
private static final boolean TRACK_KEY_ACCESS = false;
- public static final int PROP_NAME_MAX = 31;
public static final int PROP_VALUE_MAX = 91;
private static final ArrayList<Runnable> sChangeCallbacks = new ArrayList<Runnable>();
@@ -82,12 +81,8 @@
/**
* Get the value for the given key.
* @return an empty string if the key isn't found
- * @throws IllegalArgumentException if the key exceeds 32 characters
*/
public static String get(String key) {
- if (key.length() > PROP_NAME_MAX) {
- throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
- }
if (TRACK_KEY_ACCESS) onKeyAccess(key);
return native_get(key);
}
@@ -95,12 +90,8 @@
/**
* Get the value for the given key.
* @return if the key isn't found, return def if it isn't null, or an empty string otherwise
- * @throws IllegalArgumentException if the key exceeds 32 characters
*/
public static String get(String key, String def) {
- if (key.length() > PROP_NAME_MAX) {
- throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
- }
if (TRACK_KEY_ACCESS) onKeyAccess(key);
return native_get(key, def);
}
@@ -111,12 +102,8 @@
* @param def a default value to return
* @return the key parsed as an integer, or def if the key isn't found or
* cannot be parsed
- * @throws IllegalArgumentException if the key exceeds 32 characters
*/
public static int getInt(String key, int def) {
- if (key.length() > PROP_NAME_MAX) {
- throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
- }
if (TRACK_KEY_ACCESS) onKeyAccess(key);
return native_get_int(key, def);
}
@@ -127,12 +114,8 @@
* @param def a default value to return
* @return the key parsed as a long, or def if the key isn't found or
* cannot be parsed
- * @throws IllegalArgumentException if the key exceeds 32 characters
*/
public static long getLong(String key, long def) {
- if (key.length() > PROP_NAME_MAX) {
- throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
- }
if (TRACK_KEY_ACCESS) onKeyAccess(key);
return native_get_long(key, def);
}
@@ -148,25 +131,17 @@
* @param def a default value to return
* @return the key parsed as a boolean, or def if the key isn't found or is
* not able to be parsed as a boolean.
- * @throws IllegalArgumentException if the key exceeds 32 characters
*/
public static boolean getBoolean(String key, boolean def) {
- if (key.length() > PROP_NAME_MAX) {
- throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
- }
if (TRACK_KEY_ACCESS) onKeyAccess(key);
return native_get_boolean(key, def);
}
/**
* Set the value for the given key.
- * @throws IllegalArgumentException if the key exceeds 32 characters
* @throws IllegalArgumentException if the value exceeds 92 characters
*/
public static void set(String key, String val) {
- if (key.length() > PROP_NAME_MAX) {
- throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
- }
if (val != null && val.length() > PROP_VALUE_MAX) {
throw new IllegalArgumentException("val.length > " +
PROP_VALUE_MAX);
diff --git a/core/java/android/os/UpdateEngine.java b/core/java/android/os/UpdateEngine.java
index bf03cce..8549cff 100644
--- a/core/java/android/os/UpdateEngine.java
+++ b/core/java/android/os/UpdateEngine.java
@@ -26,7 +26,18 @@
/**
* UpdateEngine handles calls to the update engine which takes care of A/B OTA
* updates. It wraps up the update engine Binder APIs and exposes them as
- * SystemApis, which will be called by system apps like GmsCore.
+ * SystemApis, which will be called by the system app responsible for OTAs.
+ * On a Google device, this will be GmsCore.
+ *
+ * The minimal flow is:
+ * <ol>
+ * <li>Create a new UpdateEngine instance.
+ * <li>Call {@link #bind}, optionally providing callbacks.
+ * <li>Call {@link #applyPayload}.
+ * </ol>
+ *
+ * In addition, methods are provided to {@link #cancel} or
+ * {@link #suspend}/{@link #resume} application of an update.
*
* The APIs defined in this class and UpdateEngineCallback class must be in
* sync with the ones in
@@ -80,12 +91,20 @@
private IUpdateEngine mUpdateEngine;
+ /**
+ * Creates a new instance.
+ */
@SystemApi
public UpdateEngine() {
mUpdateEngine = IUpdateEngine.Stub.asInterface(
ServiceManager.getService(UPDATE_ENGINE_SERVICE));
}
+ /**
+ * Prepares this instance for use. The callback will be notified on any
+ * status change, and when the update completes. A handler can be supplied
+ * to control which thread runs the callback, or null.
+ */
@SystemApi
public boolean bind(final UpdateEngineCallback callback, final Handler handler) {
IUpdateEngineCallback updateEngineCallback = new IUpdateEngineCallback.Stub() {
@@ -125,11 +144,42 @@
}
}
+ /**
+ * Equivalent to {@code bind(callback, null)}.
+ */
@SystemApi
public boolean bind(final UpdateEngineCallback callback) {
return bind(callback, null);
}
+ /**
+ * Applies the payload found at the given {@code url}. For non-streaming
+ * updates, the URL can be a local file using the {@code file://} scheme.
+ *
+ * <p>The {@code offset} and {@code size} parameters specify the location
+ * of the payload within the file represented by the URL. This is useful
+ * if the downloadable package at the URL contains more than just the
+ * update_engine payload (such as extra metadata). This is true for
+ * Google's OTA system, where the URL points to a zip file in which the
+ * payload is stored uncompressed within the zip file alongside other
+ * data.
+ *
+ * <p>The {@code headerKeyValuePairs} parameter is used to pass metadata
+ * to update_engine. In Google's implementation, this is stored as
+ * {@code payload_properties.txt} in the zip file. It's generated by the
+ * script {@code system/update_engine/scripts/brillo_update_payload}.
+ * The complete list of keys and their documentation is in
+ * {@code system/update_engine/common/constants.cc}, but an example
+ * might be:
+ * <pre>
+ * String[] pairs = {
+ * "FILE_HASH=lURPCIkIAjtMOyB/EjQcl8zDzqtD6Ta3tJef6G/+z2k=",
+ * "FILE_SIZE=871903868",
+ * "METADATA_HASH=tBvj43QOB0Jn++JojcpVdbRLz0qdAuL+uTkSy7hokaw=",
+ * "METADATA_SIZE=70604"
+ * };
+ * </pre>
+ */
@SystemApi
public void applyPayload(String url, long offset, long size, String[] headerKeyValuePairs) {
try {
@@ -139,6 +189,15 @@
}
}
+ /**
+ * Permanently cancels an in-progress update.
+ *
+ * <p>See {@link #resetStatus} to undo a finshed update (only available
+ * before the updated system has been rebooted).
+ *
+ * <p>See {@link #suspend} for a way to temporarily stop an in-progress
+ * update with the ability to resume it later.
+ */
@SystemApi
public void cancel() {
try {
@@ -148,6 +207,10 @@
}
}
+ /**
+ * Suspends an in-progress update. This can be undone by calling
+ * {@link #resume}.
+ */
@SystemApi
public void suspend() {
try {
@@ -157,6 +220,9 @@
}
}
+ /**
+ * Resumes a suspended update.
+ */
@SystemApi
public void resume() {
try {
@@ -166,6 +232,15 @@
}
}
+ /**
+ * Resets the bootable flag on the non-current partition and all internal
+ * update_engine state. This can be used after an unwanted payload has been
+ * successfully applied and the device has not yet been rebooted to signal
+ * that we no longer want to boot into that updated system. After this call
+ * completes, update_engine will no longer report
+ * {@code UPDATED_NEED_REBOOT}, so your callback can remove any outstanding
+ * notification that rebooting into the new system is possible.
+ */
@SystemApi
public void resetStatus() {
try {
diff --git a/core/java/android/os/UpdateEngineCallback.java b/core/java/android/os/UpdateEngineCallback.java
index b3b856f..afff60a 100644
--- a/core/java/android/os/UpdateEngineCallback.java
+++ b/core/java/android/os/UpdateEngineCallback.java
@@ -19,7 +19,8 @@
import android.annotation.SystemApi;
/**
- * Callback function for UpdateEngine.
+ * Callback function for UpdateEngine. Used to keep the caller up to date
+ * with progress, so the UI (if any) can be updated.
*
* The APIs defined in this class and UpdateEngine class must be in sync with
* the ones in
@@ -31,9 +32,19 @@
@SystemApi
public abstract class UpdateEngineCallback {
+ /**
+ * Invoked when anything changes. The value of {@code status} will
+ * be one of the values from {@link UpdateEngine.UpdateStatusConstants},
+ * and {@code percent} will be valid [TODO: in which cases?].
+ */
@SystemApi
public abstract void onStatusUpdate(int status, float percent);
+ /**
+ * Invoked when the payload has been applied, whether successfully or
+ * unsuccessfully. The value of {@code errorCode} will be one of the
+ * values from {@link UpdateEngine.ErrorCodeConstants}.
+ */
@SystemApi
public abstract void onPayloadApplicationComplete(int errorCode);
}
diff --git a/core/java/android/preference/SeekBarVolumizer.java b/core/java/android/preference/SeekBarVolumizer.java
index 1ec00db..f1d59e2 100644
--- a/core/java/android/preference/SeekBarVolumizer.java
+++ b/core/java/android/preference/SeekBarVolumizer.java
@@ -348,8 +348,8 @@
if (msg.what == UPDATE_SLIDER) {
if (mSeekBar != null) {
mLastProgress = msg.arg1;
- mLastAudibleStreamVolume = Math.abs(msg.arg2);
- final boolean muted = msg.arg2 < 0;
+ mLastAudibleStreamVolume = msg.arg2;
+ final boolean muted = ((Boolean)msg.obj).booleanValue();
if (muted != mMuted) {
mMuted = muted;
if (mCallback != null) {
@@ -362,8 +362,7 @@
}
public void postUpdateSlider(int volume, int lastAudibleVolume, boolean mute) {
- final int arg2 = lastAudibleVolume * (mute ? -1 : 1);
- obtainMessage(UPDATE_SLIDER, volume, arg2).sendToTarget();
+ obtainMessage(UPDATE_SLIDER, volume, lastAudibleVolume, new Boolean(mute)).sendToTarget();
}
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index b1dcb81..38ad68d 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6416,6 +6416,12 @@
public static final String DEVICE_PAIRED = "device_paired";
/**
+ * Specifies additional package name for broadcasting the CMAS messages.
+ * @hide
+ */
+ public static final String CMAS_ADDITIONAL_BROADCAST_PKG = "cmas_additional_broadcast_pkg";
+
+ /**
* This are the settings to be backed up.
*
* NOTE: Settings are backed up and restored in the order they appear
@@ -7692,6 +7698,16 @@
public static final String NETWORK_RECOMMENDATION_REQUEST_TIMEOUT_MS =
"network_recommendation_request_timeout_ms";
+ /**
+ * The expiration time in milliseconds for the {@link android.net.WifiKey} request cache in
+ * {@link com.android.server.wifi.RecommendedNetworkEvaluator}.
+ *
+ * Type: long
+ * @hide
+ */
+ public static final String RECOMMENDED_NETWORK_EVALUATOR_CACHE_EXPIRY_MS =
+ "recommended_network_evaluator_cache_expiry_ms";
+
/**
* Settings to allow BLE scans to be enabled even when Bluetooth is turned off for
* connectivity.
diff --git a/core/java/android/text/Hyphenator.java b/core/java/android/text/Hyphenator.java
index 356804e..80ec03e 100644
--- a/core/java/android/text/Hyphenator.java
+++ b/core/java/android/text/Hyphenator.java
@@ -189,7 +189,9 @@
// TODO: replace this with a discovery-based method that looks into /system/usr/hyphen-data
String[] availableLanguages = {
"as",
+ "bg",
"bn",
+ "cu",
"cy",
"da",
"de-1901", "de-1996", "de-CH-1901",
diff --git a/core/java/android/util/Patterns.java b/core/java/android/util/Patterns.java
index 0a452db..86434b2 100644
--- a/core/java/android/util/Patterns.java
+++ b/core/java/android/util/Patterns.java
@@ -243,12 +243,12 @@
public static final String GOOD_IRI_CHAR =
"a-zA-Z0-9\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF";
- public static final Pattern IP_ADDRESS
- = Pattern.compile(
+ private static final String IP_ADDRESS_STRING =
"((25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.(25[0-5]|2[0-4]"
+ "[0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1]"
+ "[0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}"
- + "|[1-9][0-9]|[0-9]))");
+ + "|[1-9][0-9]|[0-9]))";
+ public static final Pattern IP_ADDRESS = Pattern.compile(IP_ADDRESS_STRING);
/**
* Valid UCS characters defined in RFC 3987. Excludes space characters.
@@ -298,8 +298,8 @@
private static final String HOST_NAME = "(" + IRI_LABEL + "\\.)+" + TLD;
- public static final Pattern DOMAIN_NAME
- = Pattern.compile("(" + HOST_NAME + "|" + IP_ADDRESS + ")");
+ private static final String DOMAIN_NAME_STR = "(" + HOST_NAME + "|" + IP_ADDRESS_STRING + ")";
+ public static final Pattern DOMAIN_NAME = Pattern.compile(DOMAIN_NAME_STR);
private static final String PROTOCOL = "(?i:http|https|rtsp):\\/\\/";
@@ -323,7 +323,7 @@
public static final Pattern WEB_URL = Pattern.compile("("
+ "("
+ "(?:" + PROTOCOL + "(?:" + USER_INFO + ")?" + ")?"
- + "(?:" + DOMAIN_NAME + ")"
+ + "(?:" + DOMAIN_NAME_STR + ")"
+ "(?:" + PORT_NUMBER + ")?"
+ ")"
+ "(" + PATH_AND_QUERY + ")?"
@@ -346,14 +346,14 @@
* Regular expression that matches domain names using either {@link #STRICT_HOST_NAME} or
* {@link #IP_ADDRESS}
*/
- private static final Pattern STRICT_DOMAIN_NAME
- = Pattern.compile("(?:" + STRICT_HOST_NAME + "|" + IP_ADDRESS + ")");
+ private static final String STRICT_DOMAIN_NAME = "(?:" + STRICT_HOST_NAME + "|"
+ + IP_ADDRESS_STRING + ")";
/**
* Regular expression that matches domain names without a TLD
*/
private static final String RELAXED_DOMAIN_NAME =
- "(?:" + "(?:" + IRI_LABEL + "(?:\\.(?=\\S))" +"?)+" + "|" + IP_ADDRESS + ")";
+ "(?:" + "(?:" + IRI_LABEL + "(?:\\.(?=\\S))" +"?)+" + "|" + IP_ADDRESS_STRING + ")";
/**
* Regular expression to match strings that do not start with a supported protocol. The TLDs
diff --git a/core/java/android/webkit/WebViewZygote.java b/core/java/android/webkit/WebViewZygote.java
index 2d6f443..f9d7332 100644
--- a/core/java/android/webkit/WebViewZygote.java
+++ b/core/java/android/webkit/WebViewZygote.java
@@ -176,7 +176,7 @@
// paths and pass them to the zygote as strings.
final List<String> zipPaths = new ArrayList<>(10);
final List<String> libPaths = new ArrayList<>(10);
- LoadedApk.makePaths(null, sPackage.applicationInfo, zipPaths, libPaths);
+ LoadedApk.makePaths(null, false, sPackage.applicationInfo, zipPaths, libPaths);
final String librarySearchPath = TextUtils.join(File.pathSeparator, libPaths);
final String zip = (zipPaths.size() == 1) ? zipPaths.get(0) :
TextUtils.join(File.pathSeparator, zipPaths);
diff --git a/core/java/com/android/internal/util/Predicate.java b/core/java/com/android/internal/util/Predicate.java
index bc6d6b3..1b5eaff 100644
--- a/core/java/com/android/internal/util/Predicate.java
+++ b/core/java/com/android/internal/util/Predicate.java
@@ -25,7 +25,10 @@
* <p/>
* Implementors of Predicate which may cause side effects upon evaluation are
* strongly encouraged to state this fact clearly in their API documentation.
+ *
+ * @deprecated Use {@code java.util.function.Predicate} instead.
*/
+@Deprecated
public interface Predicate<T> {
boolean apply(T t);
diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java
index be10608df..d67cef3 100644
--- a/core/java/com/android/internal/util/StateMachine.java
+++ b/core/java/com/android/internal/util/StateMachine.java
@@ -23,6 +23,8 @@
import android.text.TextUtils;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.io.StringWriter;
@@ -1495,7 +1497,7 @@
}
/**
- * @return number of log records
+ * @return the number of log records currently readable
*/
public final int getLogRecSize() {
// mSmHandler can be null if the state machine has quit.
@@ -1505,6 +1507,17 @@
}
/**
+ * @return the number of log records we can store
+ */
+ @VisibleForTesting
+ public final int getLogRecMaxSize() {
+ // mSmHandler can be null if the state machine has quit.
+ SmHandler smh = mSmHandler;
+ if (smh == null) return 0;
+ return smh.mLogRecords.mMaxSize;
+ }
+
+ /**
* @return the total number of records processed
*/
public final int getLogRecCount() {
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index a9ca12b..6986732 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -218,6 +218,8 @@
LOCAL_STATIC_LIBRARIES := \
libseccomp_policy \
+ libselinux \
+ libcrypto \
LOCAL_SHARED_LIBRARIES := \
libmemtrack \
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 3a2df75..a758489 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -1027,17 +1027,13 @@
env->ReleaseStringCritical(fileName, str);
}
- int fd = open(fileName8.string(), O_CREAT | O_WRONLY | O_NOFOLLOW, 0666); /* -rw-rw-rw- */
+ int fd = open(fileName8.string(), O_CREAT | O_WRONLY | O_NOFOLLOW | O_CLOEXEC | O_APPEND, 0666);
if (fd < 0) {
fprintf(stderr, "Can't open %s: %s\n", fileName8.string(), strerror(errno));
return;
}
- if (lseek(fd, 0, SEEK_END) < 0) {
- fprintf(stderr, "lseek: %s\n", strerror(errno));
- } else {
- dump_backtrace_to_file_timeout(pid, fd, timeoutSecs);
- }
+ dump_backtrace_to_file_timeout(pid, fd, timeoutSecs);
close(fd);
}
diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp
index c1d4251..c3978e7 100644
--- a/core/jni/android_os_HwBinder.cpp
+++ b/core/jni/android_os_HwBinder.cpp
@@ -49,12 +49,6 @@
namespace android {
-static jclass gArrayListClass;
-static struct {
- jmethodID size;
- jmethodID get;
-} gArrayListMethods;
-
static jclass gErrorClass;
static struct fields_t {
@@ -237,7 +231,6 @@
static void JHwBinder_native_registerService(
JNIEnv *env,
jobject thiz,
- jobject interfaceChainArrayList,
jstring serviceNameObj) {
if (serviceNameObj == NULL) {
jniThrowException(env, "java/lang/NullPointerException", NULL);
@@ -249,24 +242,6 @@
return; // XXX exception already pending?
}
- jint numInterfaces = env->CallIntMethod(interfaceChainArrayList,
- gArrayListMethods.size);
- hidl_string *strings = new hidl_string[numInterfaces];
-
- for (jint i = 0; i < numInterfaces; i++) {
- jstring strObj = static_cast<jstring>(
- env->CallObjectMethod(interfaceChainArrayList,
- gArrayListMethods.get,
- i)
- );
- const char * str = env->GetStringUTFChars(strObj, nullptr);
- strings[i] = hidl_string(str);
- env->ReleaseStringUTFChars(strObj, str);
- }
-
- hidl_vec<hidl_string> interfaceChain;
- interfaceChain.setToExternal(strings, numInterfaces, true /* shouldOwn */);
-
sp<hardware::IBinder> binder = JHwBinder::GetNativeContext(env, thiz);
/* TODO(b/33440494) this is not right */
@@ -280,7 +255,7 @@
return;
}
- Return<bool> ret = manager->add(interfaceChain, serviceName, base);
+ Return<bool> ret = manager->add(serviceName, base);
env->ReleaseStringUTFChars(serviceNameObj, serviceName);
serviceName = NULL;
@@ -344,7 +319,7 @@
<< serviceName;
::android::vintf::Transport transport =
- ::android::hardware::getTransport(ifaceName);
+ ::android::hardware::getTransport(ifaceName, serviceName);
if ( transport != ::android::vintf::Transport::EMPTY
&& transport != ::android::vintf::Transport::HWBINDER) {
LOG(ERROR) << "service " << ifaceName << " declares transport method "
@@ -383,7 +358,7 @@
"(IL" PACKAGE_PATH "/HwParcel;L" PACKAGE_PATH "/HwParcel;I)V",
(void *)JHwBinder_native_transact },
- { "registerService", "(Ljava/util/ArrayList;Ljava/lang/String;)V",
+ { "registerService", "(Ljava/lang/String;)V",
(void *)JHwBinder_native_registerService },
{ "getService", "(Ljava/lang/String;Ljava/lang/String;)L" PACKAGE_PATH "/IHwBinder;",
@@ -393,11 +368,6 @@
namespace android {
int register_android_os_HwBinder(JNIEnv *env) {
- jclass arrayListClass = FindClassOrDie(env, "java/util/ArrayList");
- gArrayListClass = MakeGlobalRefOrDie(env, arrayListClass);
- gArrayListMethods.size = GetMethodIDOrDie(env, arrayListClass, "size", "()I");
- gArrayListMethods.get = GetMethodIDOrDie(env, arrayListClass, "get", "(I)Ljava/lang/Object;");
-
jclass errorClass = FindClassOrDie(env, "java/lang/Error");
gErrorClass = MakeGlobalRefOrDie(env, errorClass);
diff --git a/core/jni/android_os_seccomp.cpp b/core/jni/android_os_seccomp.cpp
index 45d5061..4502371 100644
--- a/core/jni/android_os_seccomp.cpp
+++ b/core/jni/android_os_seccomp.cpp
@@ -14,233 +14,25 @@
* limitations under the License.
*/
-#include "JNIHelp.h"
#include "core_jni_helpers.h"
#include "JniConstants.h"
#include "utils/Log.h"
-#include "utils/misc.h"
-
-#if defined __arm__ || defined __aarch64__
-
-#include <vector>
-
-#include <sys/prctl.h>
-
-#include <linux/unistd.h>
-#include <linux/audit.h>
-#include <linux/filter.h>
-#include <linux/seccomp.h>
+#include <selinux/selinux.h>
#include "seccomp_policy.h"
-#define syscall_nr (offsetof(struct seccomp_data, nr))
-#define arch_nr (offsetof(struct seccomp_data, arch))
-
-typedef std::vector<sock_filter> filter;
-
-// We want to keep the below inline functions for debugging and future
-// development even though they are not all sed currently.
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunused-function"
-
-static inline void Kill(filter& f) {
- f.push_back(BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL));
-}
-
-static inline void Trap(filter& f) {
- f.push_back(BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRAP));
-}
-
-static inline void Error(filter& f, __u16 retcode) {
- f.push_back(BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ERRNO + retcode));
-}
-
-inline static void Trace(filter& f) {
- f.push_back(BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRACE));
-}
-
-inline static void Allow(filter& f) {
- f.push_back(BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW));
-}
-
-#pragma clang diagnostic pop
-
-inline static void AllowSyscall(filter& f, __u32 num) {
- f.push_back(BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, num, 0, 1));
- Allow(f);
-}
-
-inline static void ExamineSyscall(filter& f) {
- f.push_back(BPF_STMT(BPF_LD|BPF_W|BPF_ABS, syscall_nr));
-}
-
-inline static int SetValidateArchitectureJumpTarget(size_t offset, filter& f) {
- size_t jump_length = f.size() - offset - 1;
- auto u8_jump_length = (__u8) jump_length;
- if (u8_jump_length != jump_length) {
- ALOGE("Can't set jump greater than 255 - actual jump is %zu",
- jump_length);
- return -1;
- }
- f[offset] = BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, AUDIT_ARCH_ARM, u8_jump_length, 0);
- return 0;
-}
-
-inline static size_t ValidateArchitectureAndJumpIfNeeded(filter& f) {
- f.push_back(BPF_STMT(BPF_LD|BPF_W|BPF_ABS, arch_nr));
-
- f.push_back(BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, AUDIT_ARCH_AARCH64, 2, 0));
- f.push_back(BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, AUDIT_ARCH_ARM, 1, 0));
- Trap(f);
- return f.size() - 2;
-}
-
-static bool install_filter(filter const& f) {
- struct sock_fprog prog = {
- (unsigned short) f.size(),
- (struct sock_filter*) &f[0],
- };
-
- if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) < 0) {
- ALOGE("SECCOMP: Could not set seccomp filter of size %zu: %s", f.size(), strerror(errno));
- return false;
- }
-
- ALOGI("SECCOMP: Global filter of size %zu installed", f.size());
- return true;
-}
-
-bool set_seccomp_filter() {
- filter f;
-
- // Note that for mixed 64/32 bit architectures, ValidateArchitecture inserts a
- // jump that must be changed to point to the start of the 32-bit policy
- // 32 bit syscalls will not hit the policy between here and the call to SetJump
- auto offset_to_32bit_filter =
- ValidateArchitectureAndJumpIfNeeded(f);
-
- // 64-bit filter
- ExamineSyscall(f);
-
- // arm64-only filter - autogenerated from bionic syscall usage
- for (size_t i = 0; i < arm64_filter_size; ++i)
- f.push_back(arm64_filter[i]);
-
- // Syscalls needed to boot Android
- AllowSyscall(f, 41); // __NR_pivot_root
- AllowSyscall(f, 31); // __NR_ioprio_get
- AllowSyscall(f, 30); // __NR_ioprio_set
- AllowSyscall(f, 178); // __NR_gettid
- AllowSyscall(f, 98); // __NR_futex
- AllowSyscall(f, 220); // __NR_clone
- AllowSyscall(f, 139); // __NR_rt_sigreturn
- AllowSyscall(f, 240); // __NR_rt_tgsigqueueinfo
- AllowSyscall(f, 128); // __NR_restart_syscall
- AllowSyscall(f, 278); // __NR_getrandom
-
- // Needed for performance tools
- AllowSyscall(f, 241); // __NR_perf_event_open
-
- // Needed for strace
- AllowSyscall(f, 130); // __NR_tkill
-
- // Needed for kernel to restart syscalls
- AllowSyscall(f, 128); // __NR_restart_syscall
-
- // b/35034743
- AllowSyscall(f, 267); // __NR_syncfs
-
- // b/34763393
- AllowSyscall(f, 277); // __NR_seccomp
-
- Trap(f);
-
- if (SetValidateArchitectureJumpTarget(offset_to_32bit_filter, f) != 0)
- return -1;
-
- // 32-bit filter
- ExamineSyscall(f);
-
- // arm32 filter - autogenerated from bionic syscall usage
- for (size_t i = 0; i < arm_filter_size; ++i)
- f.push_back(arm_filter[i]);
-
- // Syscalls needed to boot android
- AllowSyscall(f, 120); // __NR_clone
- AllowSyscall(f, 240); // __NR_futex
- AllowSyscall(f, 119); // __NR_sigreturn
- AllowSyscall(f, 173); // __NR_rt_sigreturn
- AllowSyscall(f, 363); // __NR_rt_tgsigqueueinfo
- AllowSyscall(f, 224); // __NR_gettid
-
- // Syscalls needed to run Chrome
- AllowSyscall(f, 383); // __NR_seccomp - needed to start Chrome
- AllowSyscall(f, 384); // __NR_getrandom - needed to start Chrome
-
- // Syscalls needed to run GFXBenchmark
- AllowSyscall(f, 190); // __NR_vfork
-
- // Needed for strace
- AllowSyscall(f, 238); // __NR_tkill
-
- // Needed for kernel to restart syscalls
- AllowSyscall(f, 0); // __NR_restart_syscall
-
- // Needed for debugging 32-bit Chrome
- AllowSyscall(f, 42); // __NR_pipe
-
- // b/34732712
- AllowSyscall(f, 364); // __NR_perf_event_open
-
- // b/34651972
- AllowSyscall(f, 33); // __NR_access
- AllowSyscall(f, 195); // __NR_stat64
-
- // b/34813887
- AllowSyscall(f, 5); // __NR_open
- AllowSyscall(f, 141); // __NR_getdents
- AllowSyscall(f, 217); // __NR_getdents64
-
- // b/34719286
- AllowSyscall(f, 351); // __NR_eventfd
-
- // b/34817266
- AllowSyscall(f, 252); // __NR_epoll_wait
-
- // Needed by sanitizers (b/34606909)
- // 5 (__NR_open) and 195 (__NR_stat64) are also required, but they are
- // already allowed.
- AllowSyscall(f, 85); // __NR_readlink
-
- // b/34908783
- AllowSyscall(f, 250); // __NR_epoll_create
-
- // b/34979910
- AllowSyscall(f, 8); // __NR_creat
- AllowSyscall(f, 10); // __NR_unlink
-
- // b/35059702
- AllowSyscall(f, 196); // __NR_lstat64
-
- Trap(f);
-
- return install_filter(f);
-}
-
static void Seccomp_setPolicy(JNIEnv* /*env*/) {
+ if (security_getenforce() == 0) {
+ ALOGI("seccomp disabled by setenforce 0");
+ return;
+ }
+
if (!set_seccomp_filter()) {
ALOGE("Failed to set seccomp policy - killing");
exit(1);
}
}
-#else // #if defined __arm__ || defined __aarch64__
-
-static void Seccomp_setPolicy(JNIEnv* /*env*/) {
-}
-
-#endif
-
static const JNINativeMethod method_table[] = {
NATIVE_METHOD(Seccomp, setPolicy, "()V"),
};
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 428159a..afd60f1 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -2161,9 +2161,9 @@
(void*) android_content_AssetManager_readAsset },
{ "seekAsset", "(JJI)J",
(void*) android_content_AssetManager_seekAsset },
- { "getAssetLength", "!(J)J",
+ { "getAssetLength", "(J)J",
(void*) android_content_AssetManager_getAssetLength },
- { "getAssetRemainingLength", "!(J)J",
+ { "getAssetRemainingLength", "(J)J",
(void*) android_content_AssetManager_getAssetRemainingLength },
{ "addAssetPathNative", "(Ljava/lang/String;Z)I",
(void*) android_content_AssetManager_addAssetPath },
@@ -2179,25 +2179,25 @@
(void*) android_content_AssetManager_getNonSystemLocales },
{ "getSizeConfigurations", "()[Landroid/content/res/Configuration;",
(void*) android_content_AssetManager_getSizeConfigurations },
- { "setConfiguration", "!(IILjava/lang/String;IIIIIIIIIIIIII)V",
+ { "setConfiguration", "(IILjava/lang/String;IIIIIIIIIIIIII)V",
(void*) android_content_AssetManager_setConfiguration },
- { "getResourceIdentifier","!(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
+ { "getResourceIdentifier","(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
(void*) android_content_AssetManager_getResourceIdentifier },
- { "getResourceName","!(I)Ljava/lang/String;",
+ { "getResourceName","(I)Ljava/lang/String;",
(void*) android_content_AssetManager_getResourceName },
- { "getResourcePackageName","!(I)Ljava/lang/String;",
+ { "getResourcePackageName","(I)Ljava/lang/String;",
(void*) android_content_AssetManager_getResourcePackageName },
- { "getResourceTypeName","!(I)Ljava/lang/String;",
+ { "getResourceTypeName","(I)Ljava/lang/String;",
(void*) android_content_AssetManager_getResourceTypeName },
- { "getResourceEntryName","!(I)Ljava/lang/String;",
+ { "getResourceEntryName","(I)Ljava/lang/String;",
(void*) android_content_AssetManager_getResourceEntryName },
- { "loadResourceValue","!(ISLandroid/util/TypedValue;Z)I",
+ { "loadResourceValue","(ISLandroid/util/TypedValue;Z)I",
(void*) android_content_AssetManager_loadResourceValue },
- { "loadResourceBagValue","!(IILandroid/util/TypedValue;Z)I",
+ { "loadResourceBagValue","(IILandroid/util/TypedValue;Z)I",
(void*) android_content_AssetManager_loadResourceBagValue },
- { "getStringBlockCount","!()I",
+ { "getStringBlockCount","()I",
(void*) android_content_AssetManager_getStringBlockCount },
- { "getNativeStringBlock","!(I)J",
+ { "getNativeStringBlock","(I)J",
(void*) android_content_AssetManager_getNativeStringBlock },
{ "getCookieName","(I)Ljava/lang/String;",
(void*) android_content_AssetManager_getCookieName },
@@ -2215,21 +2215,21 @@
(void*) android_content_AssetManager_copyTheme },
{ "clearTheme", "(J)V",
(void*) android_content_AssetManager_clearTheme },
- { "loadThemeAttributeValue", "!(JILandroid/util/TypedValue;Z)I",
+ { "loadThemeAttributeValue", "(JILandroid/util/TypedValue;Z)I",
(void*) android_content_AssetManager_loadThemeAttributeValue },
- { "getThemeChangingConfigurations", "!(J)I",
+ { "getThemeChangingConfigurations", "(J)I",
(void*) android_content_AssetManager_getThemeChangingConfigurations },
{ "dumpTheme", "(JILjava/lang/String;Ljava/lang/String;)V",
(void*) android_content_AssetManager_dumpTheme },
- { "applyStyle","!(JIIJ[I[I[I)Z",
+ { "applyStyle","(JIIJ[I[I[I)Z",
(void*) android_content_AssetManager_applyStyle },
- { "resolveAttrs","!(JII[I[I[I[I)Z",
+ { "resolveAttrs","(JII[I[I[I[I)Z",
(void*) android_content_AssetManager_resolveAttrs },
- { "retrieveAttributes","!(J[I[I[I)Z",
+ { "retrieveAttributes","(J[I[I[I)Z",
(void*) android_content_AssetManager_retrieveAttributes },
- { "getArraySize","!(I)I",
+ { "getArraySize","(I)I",
(void*) android_content_AssetManager_getArraySize },
- { "retrieveArray","!(I[I)I",
+ { "retrieveArray","(I[I)I",
(void*) android_content_AssetManager_retrieveArray },
// XML files.
@@ -2239,11 +2239,11 @@
// Arrays.
{ "getArrayStringResource","(I)[Ljava/lang/String;",
(void*) android_content_AssetManager_getArrayStringResource },
- { "getArrayStringInfo","!(I)[I",
+ { "getArrayStringInfo","(I)[I",
(void*) android_content_AssetManager_getArrayStringInfo },
- { "getArrayIntResource","!(I)[I",
+ { "getArrayIntResource","(I)[I",
(void*) android_content_AssetManager_getArrayIntResource },
- { "getStyleAttributes","!(I)[I",
+ { "getStyleAttributes","(I)[I",
(void*) android_content_AssetManager_getStyleAttributes },
// Bookkeeping.
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index b57f2362..a03d3c5 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -252,25 +252,26 @@
if (t_pri >= ANDROID_PRIORITY_BACKGROUND) {
// This task wants to stay at background
// update its cpuset so it doesn't only run on bg core(s)
-#ifdef ENABLE_CPUSETS
- int err = set_cpuset_policy(t_pid, sp);
- if (err != NO_ERROR) {
- signalExceptionForGroupError(env, -err, t_pid);
- break;
+ if (cpusets_enabled()) {
+ int err = set_cpuset_policy(t_pid, sp);
+ if (err != NO_ERROR) {
+ signalExceptionForGroupError(env, -err, t_pid);
+ break;
+ }
}
-#endif
continue;
}
}
int err;
-#ifdef ENABLE_CPUSETS
- // set both cpuset and cgroup for general threads
- err = set_cpuset_policy(t_pid, sp);
- if (err != NO_ERROR) {
- signalExceptionForGroupError(env, -err, t_pid);
- break;
+
+ if (cpusets_enabled()) {
+ // set both cpuset and cgroup for general threads
+ err = set_cpuset_policy(t_pid, sp);
+ if (err != NO_ERROR) {
+ signalExceptionForGroupError(env, -err, t_pid);
+ break;
+ }
}
-#endif
err = set_sched_policy(t_pid, sp);
if (err != NO_ERROR) {
@@ -291,7 +292,6 @@
return (int) sp;
}
-#ifdef ENABLE_CPUSETS
/** Sample CPUset list format:
* 0-3,4,6-8
*/
@@ -367,7 +367,6 @@
}
return;
}
-#endif
/**
@@ -376,22 +375,21 @@
* them in the passed in cpu_set_t
*/
void get_exclusive_cpuset_cores(SchedPolicy policy, cpu_set_t *cpu_set) {
-#ifdef ENABLE_CPUSETS
- int i;
- cpu_set_t tmp_set;
- get_cpuset_cores_for_policy(policy, cpu_set);
- for (i = 0; i < SP_CNT; i++) {
- if ((SchedPolicy) i == policy) continue;
- get_cpuset_cores_for_policy((SchedPolicy)i, &tmp_set);
- // First get cores exclusive to one set or the other
- CPU_XOR(&tmp_set, cpu_set, &tmp_set);
- // Then get the ones only in cpu_set
- CPU_AND(cpu_set, cpu_set, &tmp_set);
+ if (cpusets_enabled()) {
+ int i;
+ cpu_set_t tmp_set;
+ get_cpuset_cores_for_policy(policy, cpu_set);
+ for (i = 0; i < SP_CNT; i++) {
+ if ((SchedPolicy) i == policy) continue;
+ get_cpuset_cores_for_policy((SchedPolicy)i, &tmp_set);
+ // First get cores exclusive to one set or the other
+ CPU_XOR(&tmp_set, cpu_set, &tmp_set);
+ // Then get the ones only in cpu_set
+ CPU_AND(cpu_set, cpu_set, &tmp_set);
+ }
+ } else {
+ CPU_ZERO(cpu_set);
}
-#else
- (void) policy;
- CPU_ZERO(cpu_set);
-#endif
return;
}
diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp
index c7998a1..1c6ead0 100644
--- a/core/jni/android_view_InputChannel.cpp
+++ b/core/jni/android_view_InputChannel.cpp
@@ -111,11 +111,12 @@
}
static jobject android_view_InputChannel_createInputChannel(JNIEnv* env,
- NativeInputChannel* nativeInputChannel) {
+ std::unique_ptr<NativeInputChannel> nativeInputChannel) {
jobject inputChannelObj = env->NewObject(gInputChannelClassInfo.clazz,
gInputChannelClassInfo.ctor);
if (inputChannelObj) {
- android_view_InputChannel_setNativeInputChannel(env, inputChannelObj, nativeInputChannel);
+ android_view_InputChannel_setNativeInputChannel(env, inputChannelObj,
+ nativeInputChannel.release());
}
return inputChannelObj;
}
@@ -143,13 +144,13 @@
}
jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
- new NativeInputChannel(serverChannel));
+ std::make_unique<NativeInputChannel>(serverChannel));
if (env->ExceptionCheck()) {
return NULL;
}
jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
- new NativeInputChannel(clientChannel));
+ std::make_unique<NativeInputChannel>(clientChannel));
if (env->ExceptionCheck()) {
return NULL;
}
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index 59a536b..9660de4 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -26,8 +26,8 @@
#include <sys/un.h>
#include <unistd.h>
+#include <android-base/logging.h>
#include <android-base/strings.h>
-#include <cutils/log.h>
// Static whitelist of open paths that the zygote is allowed to keep open.
static const char* kPathWhitelist[] = {
@@ -137,7 +137,7 @@
// This should never happen; the zygote should always have the right set
// of permissions required to stat all its open files.
if (TEMP_FAILURE_RETRY(fstat(fd, &f_stat)) == -1) {
- ALOGE("Unable to stat fd %d : %s", fd, strerror(errno));
+ PLOG(ERROR) << "Unable to stat fd " << fd;
return NULL;
}
@@ -150,7 +150,8 @@
}
if (!whitelist->IsAllowed(socket_name)) {
- ALOGE("Socket name not whitelisted : %s (fd=%d)", socket_name.c_str(), fd);
+ LOG(ERROR) << "Socket name not whitelisted : " << socket_name
+ << " (fd=" << fd << ")";
return NULL;
}
@@ -168,7 +169,7 @@
// with the child process across forks but those should have been closed
// before we got to this point.
if (!S_ISCHR(f_stat.st_mode) && !S_ISREG(f_stat.st_mode)) {
- ALOGE("Unsupported st_mode %d", f_stat.st_mode);
+ LOG(ERROR) << "Unsupported st_mode " << f_stat.st_mode;
return NULL;
}
@@ -178,7 +179,7 @@
}
if (!whitelist->IsAllowed(file_path)) {
- ALOGE("Not whitelisted : %s", file_path.c_str());
+ LOG(ERROR) << "Not whitelisted : " << file_path;
return NULL;
}
@@ -187,7 +188,7 @@
// there won't be any races.
const int fd_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD));
if (fd_flags == -1) {
- ALOGE("Failed fcntl(%d, F_GETFD) : %s", fd, strerror(errno));
+ PLOG(ERROR) << "Failed fcntl(" << fd << ", F_GETFD)";
return NULL;
}
@@ -205,7 +206,7 @@
// their presence and pass them in to open().
int fs_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL));
if (fs_flags == -1) {
- ALOGE("Failed fcntl(%d, F_GETFL) : %s", fd, strerror(errno));
+ PLOG(ERROR) << "Failed fcntl(" << fd << ", F_GETFL)";
return NULL;
}
@@ -224,6 +225,7 @@
bool FileDescriptorInfo::Restat() const {
struct stat f_stat;
if (TEMP_FAILURE_RETRY(fstat(fd, &f_stat)) == -1) {
+ PLOG(ERROR) << "Unable to restat fd " << fd;
return false;
}
@@ -241,31 +243,31 @@
const int new_fd = TEMP_FAILURE_RETRY(open(file_path.c_str(), open_flags));
if (new_fd == -1) {
- ALOGE("Failed open(%s, %d) : %s", file_path.c_str(), open_flags, strerror(errno));
+ PLOG(ERROR) << "Failed open(" << file_path << ", " << open_flags << ")";
return false;
}
if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFD, fd_flags)) == -1) {
close(new_fd);
- ALOGE("Failed fcntl(%d, F_SETFD, %x) : %s", new_fd, fd_flags, strerror(errno));
+ PLOG(ERROR) << "Failed fcntl(" << new_fd << ", F_SETFD, " << fd_flags << ")";
return false;
}
if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFL, fs_flags)) == -1) {
close(new_fd);
- ALOGE("Failed fcntl(%d, F_SETFL, %x) : %s", new_fd, fs_flags, strerror(errno));
+ PLOG(ERROR) << "Failed fcntl(" << new_fd << ", F_SETFL, " << fs_flags << ")";
return false;
}
if (offset != -1 && TEMP_FAILURE_RETRY(lseek64(new_fd, offset, SEEK_SET)) == -1) {
close(new_fd);
- ALOGE("Failed lseek64(%d, SEEK_SET) : %s", new_fd, strerror(errno));
+ PLOG(ERROR) << "Failed lseek64(" << new_fd << ", SEEK_SET)";
return false;
}
if (TEMP_FAILURE_RETRY(dup2(new_fd, fd)) == -1) {
close(new_fd);
- ALOGE("Failed dup2(%d, %d) : %s", fd, new_fd, strerror(errno));
+ PLOG(ERROR) << "Failed dup2(" << fd << ", " << new_fd << ")";
return false;
}
@@ -312,7 +314,10 @@
// ext2 and ext4 both have PAGE_SIZE limitations, so we assume that here.
char buf[4096];
ssize_t len = readlink(path, buf, sizeof(buf));
- if (len == -1) return false;
+ if (len == -1) {
+ PLOG(ERROR) << "Readlink on " << fd << " failed.";
+ return false;
+ }
result->assign(buf, len);
return true;
@@ -325,12 +330,12 @@
socklen_t addr_len = sizeof(ss);
if (TEMP_FAILURE_RETRY(getsockname(fd, addr, &addr_len)) == -1) {
- ALOGE("Failed getsockname(%d) : %s", fd, strerror(errno));
+ PLOG(ERROR) << "Failed getsockname(" << fd << ")";
return false;
}
if (addr->sa_family != AF_UNIX) {
- ALOGE("Unsupported socket (fd=%d) with family %d", fd, addr->sa_family);
+ LOG(ERROR) << "Unsupported socket (fd=" << fd << ") with family " << addr->sa_family;
return false;
}
@@ -339,13 +344,13 @@
size_t path_len = addr_len - offsetof(struct sockaddr_un, sun_path);
// This is an unnamed local socket, we do not accept it.
if (path_len == 0) {
- ALOGE("Unsupported AF_UNIX socket (fd=%d) with empty path.", fd);
+ LOG(ERROR) << "Unsupported AF_UNIX socket (fd=" << fd << ") with empty path.";
return false;
}
// This is a local socket with an abstract address, we do not accept it.
if (unix_addr->sun_path[0] == '\0') {
- ALOGE("Unsupported AF_UNIX socket (fd=%d) with abstract address.", fd);
+ LOG(ERROR) << "Unsupported AF_UNIX socket (fd=" << fd << ") with abstract address.";
return false;
}
@@ -363,17 +368,17 @@
bool FileDescriptorInfo::DetachSocket() const {
const int dev_null_fd = open("/dev/null", O_RDWR);
if (dev_null_fd < 0) {
- ALOGE("Failed to open /dev/null : %s", strerror(errno));
+ PLOG(ERROR) << "Failed to open /dev/null";
return false;
}
if (dup2(dev_null_fd, fd) == -1) {
- ALOGE("Failed dup2 on socket descriptor %d : %s", fd, strerror(errno));
+ PLOG(ERROR) << "Failed dup2 on socket descriptor " << fd;
return false;
}
if (close(dev_null_fd) == -1) {
- ALOGE("Failed close(%d) : %s", dev_null_fd, strerror(errno));
+ PLOG(ERROR) << "Failed close(" << dev_null_fd << ")";
return false;
}
@@ -384,7 +389,7 @@
FileDescriptorTable* FileDescriptorTable::Create(const std::vector<int>& fds_to_ignore) {
DIR* d = opendir(kFdPath);
if (d == NULL) {
- ALOGE("Unable to open directory %s: %s", kFdPath, strerror(errno));
+ PLOG(ERROR) << "Unable to open directory " << std::string(kFdPath);
return NULL;
}
int dir_fd = dirfd(d);
@@ -397,14 +402,14 @@
continue;
}
if (std::find(fds_to_ignore.begin(), fds_to_ignore.end(), fd) != fds_to_ignore.end()) {
- ALOGI("Ignoring open file descriptor %d", fd);
+ LOG(INFO) << "Ignoring open file descriptor " << fd;
continue;
}
FileDescriptorInfo* info = FileDescriptorInfo::CreateFromFd(fd);
if (info == NULL) {
if (closedir(d) == -1) {
- ALOGE("Unable to close directory : %s", strerror(errno));
+ PLOG(ERROR) << "Unable to close directory";
}
return NULL;
}
@@ -412,7 +417,7 @@
}
if (closedir(d) == -1) {
- ALOGE("Unable to close directory : %s", strerror(errno));
+ PLOG(ERROR) << "Unable to close directory";
return NULL;
}
return new FileDescriptorTable(open_fd_map);
@@ -424,7 +429,7 @@
// First get the list of open descriptors.
DIR* d = opendir(kFdPath);
if (d == NULL) {
- ALOGE("Unable to open directory %s: %s", kFdPath, strerror(errno));
+ PLOG(ERROR) << "Unable to open directory " << std::string(kFdPath);
return false;
}
@@ -436,7 +441,7 @@
continue;
}
if (std::find(fds_to_ignore.begin(), fds_to_ignore.end(), fd) != fds_to_ignore.end()) {
- ALOGI("Ignoring open file descriptor %d", fd);
+ LOG(INFO) << "Ignoring open file descriptor " << fd;
continue;
}
@@ -444,7 +449,7 @@
}
if (closedir(d) == -1) {
- ALOGE("Unable to close directory : %s", strerror(errno));
+ PLOG(ERROR) << "Unable to close directory";
return false;
}
diff --git a/core/res/res/layout/resolver_list.xml b/core/res/res/layout/resolver_list.xml
index c4e8e9c..40c9941 100644
--- a/core/res/res/layout/resolver_list.xml
+++ b/core/res/res/layout/resolver_list.xml
@@ -45,7 +45,7 @@
android:textColor="?attr/colorAccent"
android:gravity="center_vertical"
android:layout_alignParentTop="true"
- android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
android:singleLine="true" />
<TextView
@@ -59,7 +59,7 @@
android:paddingEnd="?attr/dialogPreferredPadding"
android:paddingTop="8dp"
android:layout_below="@id/profile_button"
- android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
android:paddingBottom="8dp" />
</RelativeLayout>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 4ff78ea..4408bf8 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -236,6 +236,7 @@
<item>"mobile,0,0,0,-1,true"</item>
<item>"mobile_mms,2,0,2,60000,true"</item>
<item>"mobile_supl,3,0,2,60000,true"</item>
+ <item>"mobile_dun,4,0,2,60000,true"</item>
<item>"mobile_hipri,5,0,3,60000,true"</item>
<item>"mobile_fota,10,0,2,60000,true"</item>
<item>"mobile_ims,11,0,2,60000,true"</item>
@@ -461,6 +462,8 @@
<integer translatable="false" name="config_wifi_framework_LAST_SELECTION_AWARD">480</integer>
<integer translatable="false" name="config_wifi_framework_PASSPOINT_SECURITY_AWARD">40</integer>
<integer translatable="false" name="config_wifi_framework_SECURITY_AWARD">80</integer>
+ <!-- Integer specifying the base interval in seconds for the exponential backoff scan for autojoin -->
+ <integer translatable="false" name="config_wifi_framework_exponential_backoff_scan_base_interval">20</integer>
<!-- Integer parameters of the wifi to cellular handover feature
wifi should not stick to bad networks -->
<integer translatable="false" name="config_wifi_framework_wifi_score_bad_rssi_threshold_5GHz">-82</integer>
@@ -2683,6 +2686,6 @@
<!-- An array of packages for which notifications cannot be blocked. -->
<string-array translatable="false" name="config_nonBlockableNotificationPackages" />
- <!-- Component name of the default cell broadcast receiver -->
- <string name="config_defaultCellBroadcastReceiverComponent" translatable="false">com.android.cellbroadcastreceiver/.PrivilegedCellBroadcastReceiver</string>
+ <!-- Package name of the default cell broadcast receiver -->
+ <string name="config_defaultCellBroadcastReceiverPkg" translatable="false">com.android.cellbroadcastreceiver</string>
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 426d2eb..4231698 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -101,6 +101,8 @@
<!-- Displayed when the user dialed an MMI code whose function
could not be performed because FDN is enabled. This will be displayed in a toast. -->
<string name="mmiFdnError">Operation is restricted to fixed dialing numbers only.</string>
+ <!-- Displayed when a carrier does not support call forwarding queries when roaming. -->
+ <string name="mmiErrorWhileRoaming">Can not change call forwarding settings from your phone while you are roaming.</string>
<!-- Displayed when a phone feature such as call barring was activated. -->
<string name="serviceEnabled">Service was enabled.</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index bdac134..dbeda0b 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -725,6 +725,7 @@
<java-symbol type="string" name="mmiComplete" />
<java-symbol type="string" name="mmiError" />
<java-symbol type="string" name="mmiFdnError" />
+ <java-symbol type="string" name="mmiErrorWhileRoaming" />
<java-symbol type="string" name="month_day_year" />
<java-symbol type="string" name="more_item_label" />
<java-symbol type="string" name="needPuk" />
@@ -2740,5 +2741,5 @@
<!-- Network Recommendation -->
<java-symbol type="array" name="config_networkRecommendationPackageNames" />
- <java-symbol type="string" name="config_defaultCellBroadcastReceiverComponent" />
+ <java-symbol type="string" name="config_defaultCellBroadcastReceiverPkg" />
</resources>
diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk
index 33a9265..d6877dd 100644
--- a/core/tests/coretests/Android.mk
+++ b/core/tests/coretests/Android.mk
@@ -21,6 +21,7 @@
$(call all-java-files-under, DisabledTestApp/src) \
$(call all-java-files-under, EnabledTestApp/src)
+LOCAL_DX_FLAGS := --core-library
LOCAL_AAPT_FLAGS = -0 dat -0 gld -c fa
LOCAL_STATIC_JAVA_LIBRARIES := \
core-tests-support \
@@ -28,7 +29,6 @@
frameworks-core-util-lib \
mockwebserver \
guava \
- littlemock \
android-support-test \
mockito-target \
espresso-core \
diff --git a/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java b/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
index 29020ba..48f2935 100644
--- a/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
+++ b/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
@@ -32,10 +32,10 @@
import android.test.InstrumentationTestCase;
import com.android.internal.R;
import java.util.List;
-import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import org.mockito.compat.ArgumentMatcher;
public class NetworkScorerAppManagerTest extends InstrumentationTestCase {
@Mock private Context mMockContext;
@@ -218,7 +218,7 @@
when(mMockPm.resolveService(
Mockito.argThat(new ArgumentMatcher<Intent>() {
@Override
- public boolean matches(Object object) {
+ public boolean matchesObject(Object object) {
Intent intent = (Intent) object;
return NetworkScoreManager.ACTION_RECOMMEND_NETWORKS
.equals(intent.getAction())
diff --git a/core/tests/coretests/src/android/net/SSLSessionCacheTest.java b/core/tests/coretests/src/android/net/SSLSessionCacheTest.java
index be19303..ec130e0 100644
--- a/core/tests/coretests/src/android/net/SSLSessionCacheTest.java
+++ b/core/tests/coretests/src/android/net/SSLSessionCacheTest.java
@@ -19,7 +19,7 @@
import com.android.org.conscrypt.ClientSessionContext;
import com.android.org.conscrypt.SSLClientSessionCache;
-import com.google.testing.littlemock.LittleMock;
+import org.mockito.Mockito;
import junit.framework.TestCase;
@@ -39,25 +39,25 @@
public void testInstall_compatibleContext() throws Exception {
final SSLContext ctx = SSLContext.getDefault();
- final SSLClientSessionCache mock = LittleMock.mock(SSLClientSessionCache.class);
+ final SSLClientSessionCache mock = Mockito.mock(SSLClientSessionCache.class);
final ClientSessionContext clientCtx = (ClientSessionContext) ctx.getClientSessionContext();
try {
SSLSessionCache.install(new SSLSessionCache(mock), ctx);
clientCtx.getSession("www.foogle.com", 443);
- LittleMock.verify(mock).getSessionData(LittleMock.anyString(), LittleMock.anyInt());
+ Mockito.verify(mock).getSessionData(Mockito.anyString(), Mockito.anyInt());
} finally {
// Restore cacheless behaviour.
SSLSessionCache.install(null, ctx);
clientCtx.getSession("www.foogle.com", 443);
- LittleMock.verifyNoMoreInteractions(mock);
+ Mockito.verifyNoMoreInteractions(mock);
}
}
public void testInstall_incompatibleContext() {
try {
SSLSessionCache.install(
- new SSLSessionCache(LittleMock.mock(SSLClientSessionCache.class)),
+ new SSLSessionCache(Mockito.mock(SSLClientSessionCache.class)),
new FakeSSLContext());
fail();
} catch (IllegalArgumentException expected) {}
@@ -102,7 +102,7 @@
@Override
protected SSLSessionContext engineGetClientSessionContext() {
- return LittleMock.mock(SSLSessionContext.class);
+ return Mockito.mock(SSLSessionContext.class);
}
}
}
diff --git a/core/tests/coretests/src/android/view/PopupWindowVisibility.java b/core/tests/coretests/src/android/view/PopupWindowVisibility.java
index 7eb0468..6e11ede 100644
--- a/core/tests/coretests/src/android/view/PopupWindowVisibility.java
+++ b/core/tests/coretests/src/android/view/PopupWindowVisibility.java
@@ -82,7 +82,7 @@
"Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde",
"Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
"Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
- "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic",
+ "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czechia",
"Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic",
"East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea",
"Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland",
diff --git a/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java b/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java
index f2eba23..cdfa217 100644
--- a/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java
+++ b/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java
@@ -16,8 +16,8 @@
package android.widget.focus;
-import static com.google.testing.littlemock.LittleMock.inOrder;
-import static com.google.testing.littlemock.LittleMock.mock;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.mock;
import android.os.Handler;
import android.test.ActivityInstrumentationTestCase2;
@@ -31,7 +31,7 @@
import android.widget.Button;
import com.android.frameworks.coretests.R;
-import com.google.testing.littlemock.LittleMock.InOrder;
+import org.mockito.InOrder;
/**
* {@link RequestFocusTest} is set up to exercise cases where the views that
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk
index 836ede6..14b032e 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyAndException/Android.mk
@@ -31,9 +31,28 @@
LOCAL_JAVACFLAGS := -nowarn
+mainDexList:= \
+ $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list
+
+LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex
LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true\
-D jack.preprocessor.file=$(LOCAL_PATH)/test.jpp -D jack.dex.output.multidex.legacy=true
+#################################
+include $(BUILD_SYSTEM)/configure_local_jack.mk
+#################################
+
+ifdef LOCAL_JACK_ENABLED
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/test.jpp
+endif
include $(BUILD_PACKAGE)
+
+ifndef LOCAL_JACK_ENABLED
+$(mainDexList): $(full_classes_proguard_jar) | $(MAINDEXCLASSES)
+ $(hide) mkdir -p $(dir $@)
+ $(MAINDEXCLASSES) $< 1>$@
+ echo "com/android/multidexlegacyandexception/Test.class" >> $@
+
+$(built_dex_intermediate): $(mainDexList)
+endif
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk
index 2915914..208eceb 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestApp/Android.mk
@@ -29,13 +29,32 @@
LOCAL_DEX_PREOPT := false
+mainDexList:= \
+ $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list
+
+LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex
LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true\
-D jack.preprocessor.file=$(LOCAL_PATH)/test.jpp -D jack.dex.output.multidex.legacy=true
+#################################
+include $(BUILD_SYSTEM)/configure_local_jack.mk
+#################################
+
+ifdef LOCAL_JACK_ENABLED
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/test.jpp
+endif
include $(BUILD_PACKAGE)
+ifndef LOCAL_JACK_ENABLED
+$(mainDexList): $(full_classes_proguard_jar) | $(MAINDEXCLASSES)
+ $(hide) mkdir -p $(dir $@)
+ $(MAINDEXCLASSES) $< 1>$@
+ echo "com/android/multidexlegacytestapp/Test.class" >> $@
+
+$(built_dex_intermediate): $(mainDexList)
+endif
+
## The application with a full main dex
include $(CLEAR_VARS)
@@ -51,9 +70,28 @@
LOCAL_DEX_PREOPT := false
+mainDexList2:= \
+ $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list
+
+LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList2)
LOCAL_JACK_FLAGS := -D jack.dex.output.policy=multidex -D jack.preprocessor=true\
-D jack.preprocessor.file=$(LOCAL_PATH)/test.jpp -D jack.dex.output.multidex.legacy=true
+#################################
+include $(BUILD_SYSTEM)/configure_local_jack.mk
+#################################
+
+ifdef LOCAL_JACK_ENABLED
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/test.jpp
+endif
include $(BUILD_PACKAGE)
+
+ifndef LOCAL_JACK_ENABLED
+$(mainDexList2): $(full_classes_proguard_jar) | $(MAINDEXCLASSES)
+ $(hide) mkdir -p $(dir $@)
+ $(MAINDEXCLASSES) $< 1>$@
+ echo "com/android/multidexlegacytestapp/Test.class" >> $@
+
+$(built_dex_intermediate): $(mainDexList2)
+endif
\ No newline at end of file
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk
index 2732372..99bcd6c 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyTestServices/Android.mk
@@ -26,8 +26,20 @@
LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex
+mainDexList:= \
+ $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list
+
+LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex
LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.dex.output.multidex.legacy=true
LOCAL_DEX_PREOPT := false
include $(BUILD_PACKAGE)
+
+ifndef LOCAL_JACK_ENABLED
+$(mainDexList): $(full_classes_proguard_jar) | $(MAINDEXCLASSES)
+ $(hide) mkdir -p $(dir $@)
+ $(MAINDEXCLASSES) $< 1>$@
+
+$(built_dex_intermediate): $(mainDexList)
+endif
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk
index b4a666f..1c7d807 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v1/Android.mk
@@ -28,9 +28,28 @@
LOCAL_DEX_PREOPT := false
+mainDexList:= \
+ $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list
+
+LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex
LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true\
-D jack.preprocessor.file=$(LOCAL_PATH)/test.jpp -D jack.dex.output.multidex.legacy=true
+#################################
+include $(BUILD_SYSTEM)/configure_local_jack.mk
+#################################
+
+ifdef LOCAL_JACK_ENABLED
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/test.jpp
+endif
include $(BUILD_PACKAGE)
+
+ifndef LOCAL_JACK_ENABLED
+$(mainDexList): $(full_classes_proguard_jar) | $(MAINDEXCLASSES)
+ $(hide) mkdir -p $(dir $@)
+ $(MAINDEXCLASSES) $< 1>$@
+ echo "com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.class" >> $@
+
+$(built_dex_intermediate): $(mainDexList)
+endif
\ No newline at end of file
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk
index f38bd4f..b77cf31 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v2/Android.mk
@@ -28,9 +28,28 @@
LOCAL_DEX_PREOPT := false
+mainDexList:= \
+ $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list
+
+LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex
LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true\
-D jack.preprocessor.file=$(LOCAL_PATH)/test.jpp -D jack.dex.output.multidex.legacy=true
+#################################
+include $(BUILD_SYSTEM)/configure_local_jack.mk
+#################################
+
+ifdef LOCAL_JACK_ENABLED
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/test.jpp
+endif
include $(BUILD_PACKAGE)
+
+ifndef LOCAL_JACK_ENABLED
+$(mainDexList): $(full_classes_proguard_jar) | $(MAINDEXCLASSES)
+ $(hide) mkdir -p $(dir $@)
+ $(MAINDEXCLASSES) $< 1>$@
+ echo "com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.class" >> $@
+
+$(built_dex_intermediate): $(mainDexList)
+endif
diff --git a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk
index 5bc2c95..3631626 100644
--- a/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk
+++ b/core/tests/hosttests/test-apps/MultiDexLegacyVersionedTestApp_v3/Android.mk
@@ -26,11 +26,31 @@
LOCAL_STATIC_JAVA_LIBRARIES := android-support-multidex
+mainDexList:= \
+ $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME),$(LOCAL_IS_HOST_MODULE),common)/maindex.list
+
LOCAL_DEX_PREOPT := false
+LOCAL_DX_FLAGS := --multi-dex --main-dex-list=$(mainDexList) --minimal-main-dex
LOCAL_JACK_FLAGS := -D jack.dex.output.policy=minimal-multidex -D jack.preprocessor=true\
-D jack.preprocessor.file=$(LOCAL_PATH)/test.jpp -D jack.dex.output.multidex.legacy=true
+#################################
+include $(BUILD_SYSTEM)/configure_local_jack.mk
+#################################
+
+ifdef LOCAL_JACK_ENABLED
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/test.jpp
+endif
include $(BUILD_PACKAGE)
+
+ifndef LOCAL_JACK_ENABLED
+$(mainDexList): $(full_classes_proguard_jar) | $(MAINDEXCLASSES)
+ $(hide) mkdir -p $(dir $@)
+ $(MAINDEXCLASSES) $< 1>$@
+ echo "com/android/framework/multidexlegacyversionedtestapp/MultiDexUpdateTest.class" >> $@
+
+$(built_dex_intermediate): $(mainDexList)
+endif
+
diff --git a/core/tests/systemproperties/Android.mk b/core/tests/systemproperties/Android.mk
index e16c367..4c2e224 100644
--- a/core/tests/systemproperties/Android.mk
+++ b/core/tests/systemproperties/Android.mk
@@ -8,6 +8,7 @@
LOCAL_SRC_FILES := \
$(call all-java-files-under, src)
+LOCAL_DX_FLAGS := --core-library
LOCAL_STATIC_JAVA_LIBRARIES := android-common frameworks-core-util-lib
LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_PACKAGE_NAME := FrameworksCoreSystemPropertiesTests
diff --git a/legacy-test/Android.mk b/legacy-test/Android.mk
index 0a814f3..05fec5e 100644
--- a/legacy-test/Android.mk
+++ b/legacy-test/Android.mk
@@ -50,5 +50,5 @@
LOCAL_SRC_FILES := src/android/test/PerformanceTestCase.java
LOCAL_MODULE := legacy-performance-test-hostdex
-include $(BUILD_HOST_DALVIK_JAVA_LIBRARY)
+include $(BUILD_HOST_DALVIK_STATIC_JAVA_LIBRARY)
endif # HOST_OS == linux
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index a734401..ce13ebc 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -685,7 +685,9 @@
SignalingRenderTask syncTask(task, &mSyncMutex, &mSyncCondition);
AutoMutex _lock(mSyncMutex);
mRenderThread.queue(&syncTask);
- mSyncCondition.wait(mSyncMutex);
+ while (!syncTask.hasRun()) {
+ mSyncCondition.wait(mSyncMutex);
+ }
return retval;
}
diff --git a/libs/hwui/renderthread/RenderTask.cpp b/libs/hwui/renderthread/RenderTask.cpp
index b14f580..928a4ef 100644
--- a/libs/hwui/renderthread/RenderTask.cpp
+++ b/libs/hwui/renderthread/RenderTask.cpp
@@ -26,6 +26,7 @@
void SignalingRenderTask::run() {
mTask->run();
mLock->lock();
+ mHasRun = true;
mSignal->signal();
mLock->unlock();
}
diff --git a/libs/hwui/renderthread/RenderTask.h b/libs/hwui/renderthread/RenderTask.h
index 9ea671b..a7acf91 100644
--- a/libs/hwui/renderthread/RenderTask.h
+++ b/libs/hwui/renderthread/RenderTask.h
@@ -60,13 +60,15 @@
public:
// Takes ownership of task, caller owns lock and signal
SignalingRenderTask(RenderTask* task, Mutex* lock, Condition* signal)
- : mTask(task), mLock(lock), mSignal(signal) {}
+ : mTask(task), mLock(lock), mSignal(signal), mHasRun(false) {}
virtual void run() override;
+ bool hasRun() const { return mHasRun; }
private:
RenderTask* mTask;
Mutex* mLock;
Condition* mSignal;
+ bool mHasRun;
};
typedef void* (*RunnableMethod)(void* data);
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 9c10c4f..f383adc 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -345,7 +345,9 @@
AutoMutex _lock(mutex);
queue(&syncTask);
- condition.wait(mutex);
+ while (!syncTask.hasRun()) {
+ condition.wait(mutex);
+ }
}
void RenderThread::queueAtFront(RenderTask* task) {
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 6c879b9..a519f74 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
@@ -45,8 +45,8 @@
import com.android.mediaframeworktest.MediaFrameworkIntegrationTestRunner;
-import org.mockito.ArgumentMatcher;
import org.mockito.ArgumentCaptor;
+import org.mockito.compat.ArgumentMatcher;
import static org.mockito.Mockito.*;
public class CameraDeviceBinderTest extends AndroidTestCase {
@@ -158,7 +158,7 @@
class IsMetadataNotEmpty extends ArgumentMatcher<CameraMetadataNative> {
@Override
- public boolean matches(Object obj) {
+ public boolean matchesObject(Object obj) {
return !((CameraMetadataNative) obj).isEmpty();
}
}
diff --git a/obex/javax/obex/ServerSession.java b/obex/javax/obex/ServerSession.java
index acee5dd..3831cf7 100644
--- a/obex/javax/obex/ServerSession.java
+++ b/obex/javax/obex/ServerSession.java
@@ -658,6 +658,11 @@
*/
byte[] sendData = new byte[totalLength];
int maxRxLength = ObexHelper.getMaxRxPacketSize(mTransport);
+ if (maxRxLength > mMaxPacketLength) {
+ if(V) Log.v(TAG,"Set maxRxLength to min of maxRxServrLen:" + maxRxLength +
+ " and MaxNegotiated from Client: " + mMaxPacketLength);
+ maxRxLength = mMaxPacketLength;
+ }
sendData[0] = (byte)code;
sendData[1] = length[2];
sendData[2] = length[3];
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index 23a8655..6394c64 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -77,8 +77,8 @@
mCm = ConnectivityManager.from(this);
mNetwork = getIntent().getParcelableExtra(ConnectivityManager.EXTRA_NETWORK);
mCaptivePortal = getIntent().getParcelableExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL);
- mUserAgent = getIntent().getParcelableExtra(
- ConnectivityManager.EXTRA_CAPTIVE_PORTAL_USER_AGENT);
+ mUserAgent =
+ getIntent().getStringExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_USER_AGENT);
mUrl = getUrl();
if (mUrl == null) {
// getUrl() failed to parse the url provided in the intent: bail out in a way that
@@ -274,8 +274,17 @@
if (mUserAgent != null) {
urlConnection.setRequestProperty("User-Agent", mUserAgent);
}
+ // cannot read request header after connection
+ String requestHeader = urlConnection.getRequestProperties().toString();
+
urlConnection.getInputStream();
httpResponseCode = urlConnection.getResponseCode();
+ if (DBG) {
+ Log.d(TAG, "probe at " + mUrl +
+ " ret=" + httpResponseCode +
+ " request=" + requestHeader +
+ " headers=" + urlConnection.getHeaderFields());
+ }
} catch (IOException e) {
} finally {
if (urlConnection != null) urlConnection.disconnect();
diff --git a/packages/CarrierDefaultApp/AndroidManifest.xml b/packages/CarrierDefaultApp/AndroidManifest.xml
index e2080b0..2e642ec 100644
--- a/packages/CarrierDefaultApp/AndroidManifest.xml
+++ b/packages/CarrierDefaultApp/AndroidManifest.xml
@@ -17,21 +17,21 @@
*/
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.carrierdefaultapp"
- android:sharedUserId="android.uid.phone" >
+ package="com.android.carrierdefaultapp">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
<uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
- <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+ <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
<uses-permission android:name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+ <uses-permission android:name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" />
<application android:label="@string/app_name" >
<receiver android:name="com.android.carrierdefaultapp.CarrierDefaultBroadcastReceiver">
<intent-filter>
- <action android:name="android.intent.action.CARRIER_SIGNAL_REDIRECTED" />
+ <action android:name="com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED" />
</intent-filter>
</receiver>
<activity android:name="com.android.carrierdefaultapp.CaptivePortalLaunchActivity"
diff --git a/packages/CarrierDefaultApp/res/drawable/ic_sim_card.xml b/packages/CarrierDefaultApp/res/drawable/ic_sim_card.xml
index 5896757..dc54fe2 100644
--- a/packages/CarrierDefaultApp/res/drawable/ic_sim_card.xml
+++ b/packages/CarrierDefaultApp/res/drawable/ic_sim_card.xml
@@ -14,13 +14,12 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="@dimen/glif_icon_size"
- android:height="@dimen/glif_icon_size"
- android:viewportWidth="48"
- android:viewportHeight="48">
- <path
- android:fillColor="?android:attr/colorPrimary"
- android:pathData="M39.98,8c0,-2.21 -1.77,-4 -3.98,-4L20,4L8,16v24c0,2.21 1.79,4 4,4h24.02c2.21,0 3.98,-1.79 3.98,-4l-0.02,-32zM18,38h-4v-4h4v4zM34,38h-4v-4h4v4zM18,30h-4v-8h4v8zM26,38h-4v-8h4v8zM26,26h-4v-4h4v4zM34,30h-4v-8h4v8z" />
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+<path
+ android:fillColor="#757575"
+ android:pathData="M18,2h-8L4.02,8 4,20c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,4c0,-1.1 -0.9,-2 -2,-2zM13,17h-2v-2h2v2zM13,13h-2L11,8h2v5z"/>
</vector>
\ No newline at end of file
diff --git a/packages/CarrierDefaultApp/res/values/strings.xml b/packages/CarrierDefaultApp/res/values/strings.xml
index 838ff39..fe5669d 100644
--- a/packages/CarrierDefaultApp/res/values/strings.xml
+++ b/packages/CarrierDefaultApp/res/values/strings.xml
@@ -1,10 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">CarrierDefaultApp</string>
- <string name="portal_notification_id">Activate your service</string>
- <string name="no_data_notification_id">No data service</string>
- <string name="portal_notification_detail">Tap to activate your service</string>
- <string name="no_data_notification_detail">No Service, please contact your service provider</string>
+ <string name="android_system_label">Android System</string>
+ <string name="portal_notification_id">Mobile data has run out</string>
+ <string name="no_data_notification_id">No Mobile data service</string>
+ <string name="portal_notification_detail">Tap to add funds to your %s SIM</string>
+ <string name="no_data_notification_detail">Please contact your service provider %s</string>
<string name="progress_dialogue_network_connection">Connecting to captive portal...</string>
<string name="alert_dialogue_network_timeout">Network timeout, would you like to retry?</string>
<string name="alert_dialogue_network_timeout_title">Network unavailable</string>
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java
index db4890f..d9bd2fc 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CarrierActionUtils.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
+import android.os.Bundle;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.Log;
@@ -146,18 +147,25 @@
private static Notification getNotification(Context context, int titleId, int textId,
PendingIntent pendingIntent) {
- Resources resources = context.getResources();
+ final TelephonyManager telephonyMgr = context.getSystemService(TelephonyManager.class);
+ final Resources resources = context.getResources();
+ final Bundle extras = Bundle.forPair(Notification.EXTRA_SUBSTITUTE_APP_NAME,
+ resources.getString(R.string.android_system_label));
Notification.Builder builder = new Notification.Builder(context)
.setContentTitle(resources.getString(titleId))
- .setContentText(resources.getString(textId))
+ .setContentText(String.format(resources.getString(textId),
+ telephonyMgr.getNetworkOperatorName()))
.setSmallIcon(R.drawable.ic_sim_card)
+ .setColor(context.getColor(
+ com.android.internal.R.color.system_notification_accent_color))
.setOngoing(true)
.setPriority(Notification.PRIORITY_HIGH)
.setDefaults(Notification.DEFAULT_ALL)
.setVisibility(Notification.VISIBILITY_PUBLIC)
.setLocalOnly(true)
.setWhen(System.currentTimeMillis())
- .setShowWhen(false);
+ .setShowWhen(false)
+ .setExtras(extras);
if (pendingIntent != null) {
builder.setContentIntent(pendingIntent);
diff --git a/packages/DocumentsUI/Android.mk b/packages/DocumentsUI/Android.mk
index 9d44a6d..29035ab 100644
--- a/packages/DocumentsUI/Android.mk
+++ b/packages/DocumentsUI/Android.mk
@@ -12,6 +12,7 @@
LOCAL_STATIC_JAVA_LIBRARIES += android-support-v13
# Supplies material design components, e.g. Snackbar.
LOCAL_STATIC_JAVA_LIBRARIES += android-support-design
+LOCAL_STATIC_JAVA_LIBRARIES += android-support-transition
LOCAL_STATIC_JAVA_LIBRARIES += android-support-v7-recyclerview
LOCAL_STATIC_JAVA_LIBRARIES += guava
@@ -22,6 +23,7 @@
LOCAL_RESOURCE_DIR += \
frameworks/support/v7/appcompat/res \
frameworks/support/design/res \
+ frameworks/support/transition/res \
frameworks/support/v7/recyclerview/res
# Again, required to pull in appcompat resources. See abovementioned demo code.
@@ -29,6 +31,7 @@
--auto-add-overlay \
--extra-packages android.support.v7.appcompat \
--extra-packages android.support.design \
+ --extra-packages android.support.transition \
--extra-packages android.support.v7.recyclerview
LOCAL_JACK_FLAGS := \
diff --git a/packages/ExternalStorageProvider/Android.mk b/packages/ExternalStorageProvider/Android.mk
index ec6af2f..fbf3782 100644
--- a/packages/ExternalStorageProvider/Android.mk
+++ b/packages/ExternalStorageProvider/Android.mk
@@ -5,7 +5,10 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-documents-archive
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-documents-archive \
+ android-support-annotations
+
LOCAL_PACKAGE_NAME := ExternalStorageProvider
LOCAL_CERTIFICATE := platform
LOCAL_PRIVILEGED_MODULE := true
diff --git a/packages/PrintSpooler/res/layout/no_print_services_message.xml b/packages/PrintSpooler/res/layout/no_print_services_message.xml
new file mode 100644
index 0000000..7872658
--- /dev/null
+++ b/packages/PrintSpooler/res/layout/no_print_services_message.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="?android:attr/listPreferredItemHeightSmall"
+ android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+ android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+ android:orientation="horizontal"
+ android:gravity="start|center_vertical">
+
+ <RelativeLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="32dip">
+ <HorizontalScrollView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceListItem"
+ android:text="@string/print_no_print_services"
+ android:scrollHorizontally="true"
+ android:singleLine="true" />
+ </HorizontalScrollView>
+ </RelativeLayout>
+
+</LinearLayout>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/AddPrinterActivity.java b/packages/PrintSpooler/src/com/android/printspooler/ui/AddPrinterActivity.java
index c06e849..72004ef 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/ui/AddPrinterActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/ui/AddPrinterActivity.java
@@ -26,6 +26,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.Loader;
+import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.database.DataSetObserver;
import android.net.Uri;
@@ -95,20 +96,39 @@
*/
private RecommendedServicesAdapter mRecommendedServicesAdapter;
+ private static final String PKG_NAME_VENDING = "com.android.vending";
+ private boolean mHasVending;
+ private NoPrintServiceMessageAdapter mNoPrintServiceMessageAdapter;
+
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.add_printer_activity);
+ try {
+ getPackageManager().getPackageInfo(PKG_NAME_VENDING, 0);
+ mHasVending = true;
+ } catch (PackageManager.NameNotFoundException e) {
+ mHasVending = false;
+ }
mEnabledServicesAdapter = new EnabledServicesAdapter();
mDisabledServicesAdapter = new DisabledServicesAdapter();
- mRecommendedServicesAdapter = new RecommendedServicesAdapter();
+ if (mHasVending) {
+ mRecommendedServicesAdapter = new RecommendedServicesAdapter();
+ } else {
+ mNoPrintServiceMessageAdapter = new NoPrintServiceMessageAdapter();
+ }
ArrayList<ActionAdapter> adapterList = new ArrayList<>(3);
adapterList.add(mEnabledServicesAdapter);
- adapterList.add(mRecommendedServicesAdapter);
+ if (mHasVending) {
+ adapterList.add(mRecommendedServicesAdapter);
+ }
adapterList.add(mDisabledServicesAdapter);
+ if (!mHasVending) {
+ adapterList.add(mNoPrintServiceMessageAdapter);
+ }
setListAdapter(new CombinedAdapter(adapterList));
@@ -119,8 +139,10 @@
getLoaderManager().initLoader(LOADER_ID_ENABLED_SERVICES, null, printServiceLoaderCallbacks);
getLoaderManager().initLoader(LOADER_ID_DISABLED_SERVICES, null, printServiceLoaderCallbacks);
- getLoaderManager().initLoader(LOADER_ID_RECOMMENDED_SERVICES, null,
- new PrintServicePrintServiceRecommendationLoaderCallbacks());
+ if (mHasVending) {
+ getLoaderManager().initLoader(LOADER_ID_RECOMMENDED_SERVICES, null,
+ new PrintServicePrintServiceRecommendationLoaderCallbacks());
+ }
getLoaderManager().initLoader(LOADER_ID_ALL_SERVICES, null, printServiceLoaderCallbacks);
}
@@ -162,7 +184,11 @@
mDisabledServicesAdapter.updateData(data);
break;
case LOADER_ID_ALL_SERVICES:
- mRecommendedServicesAdapter.updateInstalledServices(data);
+ if (mHasVending) {
+ mRecommendedServicesAdapter.updateInstalledServices(data);
+ } else {
+ mNoPrintServiceMessageAdapter.updateInstalledServices(data);
+ }
default:
// not reached
}
@@ -179,7 +205,11 @@
mDisabledServicesAdapter.updateData(null);
break;
case LOADER_ID_ALL_SERVICES:
- mRecommendedServicesAdapter.updateInstalledServices(null);
+ if (mHasVending) {
+ mRecommendedServicesAdapter.updateInstalledServices(null);
+ } else {
+ mNoPrintServiceMessageAdapter.updateInstalledServices(null);
+ }
break;
default:
// not reached
@@ -788,4 +818,61 @@
filterRecommendations();
}
}
+
+ private class NoPrintServiceMessageAdapter extends ActionAdapter {
+ private boolean mHasPrintService;
+
+ void updateInstalledServices(@Nullable List<PrintServiceInfo> services) {
+ if (services == null || services.isEmpty()) {
+ mHasPrintService = false;
+ } else {
+ mHasPrintService = true;
+ }
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public int getCount() {
+ return mHasPrintService ? 0 : 1;
+ }
+
+ @Override
+ public int getViewTypeCount() {
+ return 1;
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ return 0;
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return null;
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (convertView == null) {
+ convertView = getLayoutInflater().inflate(R.layout.no_print_services_message,
+ parent, false);
+ }
+ return convertView;
+ }
+
+ @Override
+ public boolean isEnabled(int position) {
+ return position != 0;
+ }
+
+ @Override
+ public void performAction(@IntRange(from = 0) int position) {
+ return;
+ }
+ }
}
diff --git a/packages/SettingsLib/common.mk b/packages/SettingsLib/common.mk
index cf0ba6c..0815534 100644
--- a/packages/SettingsLib/common.mk
+++ b/packages/SettingsLib/common.mk
@@ -24,5 +24,9 @@
LOCAL_STATIC_JAVA_LIBRARIES += \
android-support-annotations \
android-support-v4 \
+ android-support-v7-appcompat \
+ android-support-v7-preference \
+ android-support-v7-recyclerview \
+ android-support-v14-preference \
SettingsLib
endif
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 3627c29..064059b 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -57,6 +57,8 @@
<string name="wifi_disabled_generic">Disabled</string>
<!-- Status for networked disabled from a DNS or DHCP failure -->
<string name="wifi_disabled_network_failure">IP Configuration Failure</string>
+ <!-- Status for networks disabled by the network recommendation provider -->
+ <string name="wifi_disabled_by_recommendation_provider">Not connected due to low quality network</string>
<!-- Status for networked disabled from a wifi association failure -->
<string name="wifi_disabled_wifi_failure">WiFi Connection Failure</string>
<!-- Status for networks disabled from authentication failure (wrong password
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 252aaab..6166cd8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -422,7 +422,8 @@
// This is the active connection on non-passpoint network
summary.append(getSummary(mContext, getDetailedState(),
mInfo != null && mInfo.isEphemeral()));
- } else if (config != null && config.isPasspoint()) {
+ } else if (config != null && config.isPasspoint()
+ && config.getNetworkSelectionStatus().isNetworkEnabled()) {
String format = mContext.getString(R.string.available_via_passpoint);
summary.append(String.format(format, config.providerFriendlyName));
} else if (config != null && config.hasNoInternetAccess()) {
@@ -445,6 +446,8 @@
summary.append(mContext.getString(R.string.wifi_disabled_generic));
break;
}
+ } else if (config != null && config.getNetworkSelectionStatus().isNotRecommended()) {
+ summary.append(mContext.getString(R.string.wifi_disabled_by_recommendation_provider));
} else if (mRssi == Integer.MAX_VALUE) { // Wifi out of range
summary.append(mContext.getString(R.string.wifi_not_in_range));
} else { // In range, not disabled.
@@ -682,11 +685,7 @@
}
void loadConfig(WifiConfiguration config) {
- if (config.isPasspoint())
- ssid = config.providerFriendlyName;
- else
- ssid = (config.SSID == null ? "" : removeDoubleQuotes(config.SSID));
-
+ ssid = (config.SSID == null ? "" : removeDoubleQuotes(config.SSID));
bssid = config.BSSID;
security = getSecurity(config);
networkId = config.networkId;
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 7d279eb..3435d1d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -344,29 +344,22 @@
}
AccessPoint accessPoint = getCachedOrCreate(config, cachedAccessPoints);
if (mLastInfo != null && mLastNetworkInfo != null) {
- if (config.isPasspoint() == false) {
- accessPoint.update(connectionConfig, mLastInfo, mLastNetworkInfo);
- }
+ accessPoint.update(connectionConfig, mLastInfo, mLastNetworkInfo);
}
if (mIncludeSaved) {
- if (!config.isPasspoint() || mIncludePasspoints) {
- // If saved network not present in scan result then set its Rssi to MAX_VALUE
- boolean apFound = false;
- for (ScanResult result : results) {
- if (result.SSID.equals(accessPoint.getSsidStr())) {
- apFound = true;
- break;
- }
+ // If saved network not present in scan result then set its Rssi to MAX_VALUE
+ boolean apFound = false;
+ for (ScanResult result : results) {
+ if (result.SSID.equals(accessPoint.getSsidStr())) {
+ apFound = true;
+ break;
}
- if (!apFound) {
- accessPoint.setRssi(Integer.MAX_VALUE);
- }
- accessPoints.add(accessPoint);
}
-
- if (config.isPasspoint() == false) {
- apMap.put(accessPoint.getSsidStr(), accessPoint);
+ if (!apFound) {
+ accessPoint.setRssi(Integer.MAX_VALUE);
}
+ accessPoints.add(accessPoint);
+ apMap.put(accessPoint.getSsidStr(), accessPoint);
} else {
// If we aren't using saved networks, drop them into the cache so that
// we have access to their saved info.
@@ -397,20 +390,16 @@
}
if (result.isPasspointNetwork()) {
+ // Retrieve a WifiConfiguration for a Passpoint provider that matches
+ // the given ScanResult. This is used for showing that a given AP
+ // (ScanResult) is available via a Passpoint provider (provider friendly
+ // name).
WifiConfiguration config = mWifiManager.getMatchingWifiConfig(result);
if (config != null) {
accessPoint.update(config);
}
}
- if (mLastInfo != null && mLastInfo.getBSSID() != null
- && mLastInfo.getBSSID().equals(result.BSSID)
- && connectionConfig != null && connectionConfig.isPasspoint()) {
- /* This network is connected via this passpoint config */
- /* SSID match is not going to work for it; so update explicitly */
- accessPoint.update(connectionConfig);
- }
-
accessPoints.add(accessPoint);
apMap.put(accessPoint.getSsidStr(), accessPoint);
}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 227d0e9..4f7a826 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -41,6 +41,7 @@
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
+ <uses-permission android:name="android.permission.MANAGE_USB" />
<!-- System tool permissions granted to the shell. -->
<uses-permission android:name="android.permission.REAL_GET_TASKS" />
<uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
diff --git a/packages/SystemUI/res/drawable/stat_sys_signal_in.xml b/packages/SystemUI/res/drawable/stat_sys_signal_in.xml
new file mode 100644
index 0000000..7e6e09b
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_signal_in.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:autoMirrored="true"
+ android:width="17dp"
+ android:height="17dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:name="in"
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M8.7,12.2l-2.0,3.5l-2.0,-3.5z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_signal_inout.xml b/packages/SystemUI/res/drawable/stat_sys_signal_inout.xml
new file mode 100644
index 0000000..b7b6f0f
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_signal_inout.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:autoMirrored="true"
+ android:width="17dp"
+ android:height="17dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:name="in"
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M8.7,12.2l-2.0,3.5l-2.0,-3.5z" />
+ <path
+ android:name="out"
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M0.5,15.7l2.0,-3.5l2.0,3.5z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_signal_out.xml b/packages/SystemUI/res/drawable/stat_sys_signal_out.xml
new file mode 100644
index 0000000..910c035
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_signal_out.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:autoMirrored="true"
+ android:width="17dp"
+ android:height="17dp"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:name="out"
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M0.5,15.7l2.0,-3.5l2.0,3.5z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_wifi_in.xml b/packages/SystemUI/res/drawable/stat_sys_wifi_in.xml
new file mode 100644
index 0000000..666127b
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_wifi_in.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:autoMirrored="true"
+ android:width="18.41dp"
+ android:height="17dp"
+ android:viewportWidth="26.0"
+ android:viewportHeight="24.0">
+ <path
+ android:name="in"
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M8.7,18.3l-2.0,3.5l-2.0,-3.5z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_wifi_inout.xml b/packages/SystemUI/res/drawable/stat_sys_wifi_inout.xml
new file mode 100644
index 0000000..eeba030
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_wifi_inout.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:autoMirrored="true"
+ android:width="18.41dp"
+ android:height="17dp"
+ android:viewportWidth="26.0"
+ android:viewportHeight="24.0">
+ <path
+ android:name="in"
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M8.7,18.3l-2.0,3.5l-2.0,-3.5z" />
+ <path
+ android:name="out"
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M0.5,21.8l2.0,-3.5l2.0,3.5z" />
+</vector>
diff --git a/packages/SystemUI/res/drawable/stat_sys_wifi_out.xml b/packages/SystemUI/res/drawable/stat_sys_wifi_out.xml
new file mode 100644
index 0000000..d577d1a
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_wifi_out.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:autoMirrored="true"
+ android:width="18.41dp"
+ android:height="17dp"
+ android:viewportWidth="26.0"
+ android:viewportHeight="24.0">
+ <path
+ android:name="out"
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M0.5,21.8l2.0,-3.5l2.0,3.5z" />
+</vector>
diff --git a/packages/SystemUI/res/layout/mobile_signal_group.xml b/packages/SystemUI/res/layout/mobile_signal_group.xml
index a20ec8e..f597785 100644
--- a/packages/SystemUI/res/layout/mobile_signal_group.xml
+++ b/packages/SystemUI/res/layout/mobile_signal_group.xml
@@ -43,4 +43,10 @@
android:layout_height="wrap_content"
android:layout_width="wrap_content"
/>
+ <ImageView
+ android:id="@+id/mobile_inout"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:visibility="gone"
+ />
</FrameLayout>
diff --git a/packages/SystemUI/res/layout/signal_cluster_view.xml b/packages/SystemUI/res/layout/signal_cluster_view.xml
index d17601c..f0bfb92 100644
--- a/packages/SystemUI/res/layout/signal_cluster_view.xml
+++ b/packages/SystemUI/res/layout/signal_cluster_view.xml
@@ -70,6 +70,11 @@
android:layout_width="wrap_content"
android:alpha="0.0"
/>
+ <ImageView
+ android:id="@+id/wifi_inout"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ />
</FrameLayout>
<View
android:id="@+id/wifi_signal_spacer"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
index 74caa53..d36f572 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SignalClusterView.java
@@ -75,6 +75,8 @@
private boolean mWifiVisible = false;
private int mWifiStrengthId = 0;
private int mLastWifiStrengthId = -1;
+ private int mWifiActivityId = 0;
+ private int mLastWifiActivityId = -1;
private boolean mIsAirplaneMode = false;
private int mAirplaneIconId = 0;
private int mLastAirplaneIconId = -1;
@@ -89,6 +91,7 @@
ViewGroup mEthernetGroup, mWifiGroup;
View mNoSimsCombo;
ImageView mVpn, mEthernet, mWifi, mAirplane, mNoSims, mEthernetDark, mWifiDark, mNoSimsDark;
+ ImageView mWifiActivity;
View mWifiAirplaneSpacer;
View mWifiSignalSpacer;
LinearLayout mMobileSignalGroup;
@@ -180,6 +183,7 @@
mWifiGroup = (ViewGroup) findViewById(R.id.wifi_combo);
mWifi = (ImageView) findViewById(R.id.wifi_signal);
mWifiDark = (ImageView) findViewById(R.id.wifi_signal_dark);
+ mWifiActivity = (ImageView) findViewById(R.id.wifi_inout);
mAirplane = (ImageView) findViewById(R.id.airplane);
mNoSims = (ImageView) findViewById(R.id.no_sims);
mNoSimsDark = (ImageView) findViewById(R.id.no_sims_dark);
@@ -264,6 +268,10 @@
mWifiVisible = statusIcon.visible && !mBlockWifi;
mWifiStrengthId = statusIcon.icon;
mWifiDescription = statusIcon.contentDescription;
+ mWifiActivityId = activityIn && activityOut ? R.drawable.stat_sys_wifi_inout
+ : activityIn ? R.drawable.stat_sys_wifi_in
+ : activityOut ? R.drawable.stat_sys_wifi_out
+ : 0;
apply();
}
@@ -282,6 +290,10 @@
state.mMobileDescription = statusIcon.contentDescription;
state.mMobileTypeDescription = typeContentDescription;
state.mIsMobileTypeIconWide = statusType != 0 && isWide;
+ state.mMobileActivityId = activityIn && activityOut ? R.drawable.stat_sys_signal_inout
+ : activityIn ? R.drawable.stat_sys_signal_in
+ : activityOut ? R.drawable.stat_sys_signal_out
+ : 0;
apply();
}
@@ -404,6 +416,10 @@
mWifiDark.setImageDrawable(null);
mLastWifiStrengthId = -1;
}
+ if (mWifiActivity != null) {
+ mWifiActivity.setImageDrawable(null);
+ mLastWifiActivityId = -1;
+ }
for (PhoneState state : mPhoneStates) {
if (state.mMobile != null) {
@@ -420,6 +436,10 @@
state.mMobileType.setImageDrawable(null);
state.mLastMobileTypeId = -1;
}
+ if (state.mMobileActivity != null) {
+ state.mMobileActivity.setImageDrawable(null);
+ state.mLastMobileActivityId = -1;
+ }
}
if (mAirplane != null) {
@@ -473,6 +493,12 @@
setIconForView(mWifiDark, mWifiStrengthId);
mLastWifiStrengthId = mWifiStrengthId;
}
+ if (mWifiActivityId != mLastWifiActivityId) {
+ if (mWifiActivityId != 0) {
+ setIconForView(mWifiActivity, mWifiActivityId);
+ }
+ mLastWifiActivityId = mWifiActivityId;
+ }
mWifiGroup.setContentDescription(mWifiDescription);
mWifiGroup.setVisibility(View.VISIBLE);
} else {
@@ -484,6 +510,8 @@
(mWifiVisible ? "VISIBLE" : "GONE"),
mWifiStrengthId));
+ mWifiActivity.setVisibility(mWifiActivityId != 0 ? View.VISIBLE : View.GONE);
+
boolean anyMobileVisible = false;
int firstMobileTypeId = 0;
for (PhoneState state : mPhoneStates) {
@@ -560,6 +588,8 @@
applyDarkIntensity(
StatusBarIconController.getDarkIntensity(mTintArea, mWifi, mDarkIntensity),
mWifi, mWifiDark);
+ setTint(mWifiActivity,
+ StatusBarIconController.getTint(mTintArea, mWifiActivity, mIconTint));
applyDarkIntensity(
StatusBarIconController.getDarkIntensity(mTintArea, mEthernet, mDarkIntensity),
mEthernet, mEthernetDark);
@@ -584,14 +614,16 @@
private class PhoneState {
private final int mSubId;
private boolean mMobileVisible = false;
- private int mMobileStrengthId = 0, mMobileTypeId = 0;
+ private int mMobileStrengthId = 0, mMobileTypeId = 0, mMobileActivityId = 0;
private int mLastMobileStrengthId = -1;
private int mLastMobileTypeId = -1;
+ private int mLastMobileActivityId = -1;
private boolean mIsMobileTypeIconWide;
private String mMobileDescription, mMobileTypeDescription;
private ViewGroup mMobileGroup;
private ImageView mMobile, mMobileDark, mMobileType;
+ private ImageView mMobileActivity;
public PhoneState(int subId, Context context) {
ViewGroup root = (ViewGroup) LayoutInflater.from(context)
@@ -605,6 +637,7 @@
mMobile = (ImageView) root.findViewById(R.id.mobile_signal);
mMobileDark = (ImageView) root.findViewById(R.id.mobile_signal_dark);
mMobileType = (ImageView) root.findViewById(R.id.mobile_type);
+ mMobileActivity = (ImageView) root.findViewById(R.id.mobile_inout);
}
public boolean apply(boolean isSecondaryIcon) {
@@ -619,6 +652,11 @@
mMobileType.setImageResource(mMobileTypeId);
mLastMobileTypeId = mMobileTypeId;
}
+
+ if (mLastMobileActivityId != mMobileActivityId) {
+ mMobileActivity.setImageResource(mMobileActivityId);
+ mLastMobileActivityId = mMobileActivityId;
+ }
mMobileGroup.setContentDescription(mMobileTypeDescription
+ " " + mMobileDescription);
mMobileGroup.setVisibility(View.VISIBLE);
@@ -640,6 +678,7 @@
(mMobileVisible ? "VISIBLE" : "GONE"), mMobileStrengthId, mMobileTypeId));
mMobileType.setVisibility(mMobileTypeId != 0 ? View.VISIBLE : View.GONE);
+ mMobileActivity.setVisibility(mMobileActivityId != 0 ? View.VISIBLE : View.GONE);
return mMobileVisible;
}
@@ -699,6 +738,8 @@
StatusBarIconController.getDarkIntensity(tintArea, mMobile, darkIntensity),
mMobile, mMobileDark);
setTint(mMobileType, StatusBarIconController.getTint(tintArea, mMobileType, tint));
+ setTint(mMobileActivity,
+ StatusBarIconController.getTint(tintArea, mMobileActivity, tint));
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 37e6a2a..9380c45 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -690,6 +690,7 @@
mDemoMode = true;
mDemoInetCondition = mInetCondition;
mDemoWifiState = mWifiSignalController.getState();
+ mDemoWifiState.ssid = "DemoMode";
} else if (mDemoMode && command.equals(COMMAND_EXIT)) {
if (DEBUG) Log.d(TAG, "Exiting demo mode");
mDemoMode = false;
@@ -735,6 +736,25 @@
: Math.min(Integer.parseInt(level), WifiIcons.WIFI_LEVEL_COUNT - 1);
mDemoWifiState.connected = mDemoWifiState.level >= 0;
}
+ String activity = args.getString("activity");
+ if (activity != null) {
+ switch (activity) {
+ case "inout":
+ mWifiSignalController.setActivity(WifiManager.DATA_ACTIVITY_INOUT);
+ break;
+ case "in":
+ mWifiSignalController.setActivity(WifiManager.DATA_ACTIVITY_IN);
+ break;
+ case "out":
+ mWifiSignalController.setActivity(WifiManager.DATA_ACTIVITY_OUT);
+ break;
+ default:
+ mWifiSignalController.setActivity(WifiManager.DATA_ACTIVITY_NONE);
+ break;
+ }
+ } else {
+ mWifiSignalController.setActivity(WifiManager.DATA_ACTIVITY_NONE);
+ }
mDemoWifiState.enabled = show;
mWifiSignalController.notifyListeners();
}
@@ -797,6 +817,26 @@
: Math.min(Integer.parseInt(level), icons[0].length - 1);
controller.getState().connected = controller.getState().level >= 0;
}
+ String activity = args.getString("activity");
+ if (activity != null) {
+ controller.getState().dataConnected = true;
+ switch (activity) {
+ case "inout":
+ controller.setActivity(TelephonyManager.DATA_ACTIVITY_INOUT);
+ break;
+ case "in":
+ controller.setActivity(TelephonyManager.DATA_ACTIVITY_IN);
+ break;
+ case "out":
+ controller.setActivity(TelephonyManager.DATA_ACTIVITY_OUT);
+ break;
+ default:
+ controller.setActivity(TelephonyManager.DATA_ACTIVITY_NONE);
+ break;
+ }
+ } else {
+ controller.setActivity(TelephonyManager.DATA_ACTIVITY_NONE);
+ }
controller.getState().enabled = show;
controller.notifyListeners();
}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java
index 0a3197c..1b520b4 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/DemoModeFragment.java
@@ -163,7 +163,7 @@
intent.putExtra("sims", "1");
intent.putExtra("nosim", "false");
intent.putExtra("level", "4");
- intent.putExtra("datatypel", "");
+ intent.putExtra("datatype", "lte");
getContext().sendBroadcast(intent);
// Need to send this after so that the sim controller already exists.
diff --git a/packages/SystemUI/tests/Android.mk b/packages/SystemUI/tests/Android.mk
index bf4d88c..51c7e55 100644
--- a/packages/SystemUI/tests/Android.mk
+++ b/packages/SystemUI/tests/Android.mk
@@ -18,6 +18,7 @@
LOCAL_MODULE_TAGS := tests
LOCAL_JACK_FLAGS := --multi-dex native
+LOCAL_DX_FLAGS := --multi-dex
LOCAL_PROTOC_OPTIMIZE_TYPE := nano
LOCAL_PROTOC_FLAGS := -I$(LOCAL_PATH)/..
diff --git a/packages/WallpaperCropper/Android.mk b/packages/WallpaperCropper/Android.mk
index d8fb7a4..09b41fd 100644
--- a/packages/WallpaperCropper/Android.mk
+++ b/packages/WallpaperCropper/Android.mk
@@ -6,7 +6,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_JAVA_LIBRARIES := telephony-common
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 junit
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4
LOCAL_PACKAGE_NAME := WallpaperCropper
LOCAL_CERTIFICATE := platform
diff --git a/packages/WallpaperCropper/src/com/android/gallery3d/glrenderer/BitmapTexture.java b/packages/WallpaperCropper/src/com/android/gallery3d/glrenderer/BitmapTexture.java
index 100b0b3b..f8b01cb 100644
--- a/packages/WallpaperCropper/src/com/android/gallery3d/glrenderer/BitmapTexture.java
+++ b/packages/WallpaperCropper/src/com/android/gallery3d/glrenderer/BitmapTexture.java
@@ -18,7 +18,7 @@
import android.graphics.Bitmap;
-import junit.framework.Assert;
+import com.android.gallery3d.common.Utils;
// BitmapTexture is a texture whose content is specified by a fixed Bitmap.
//
@@ -34,7 +34,7 @@
public BitmapTexture(Bitmap bitmap, boolean hasBorder) {
super(hasBorder);
- Assert.assertTrue(bitmap != null && !bitmap.isRecycled());
+ Utils.assertTrue(bitmap != null && !bitmap.isRecycled());
mContentBitmap = bitmap;
}
diff --git a/packages/WallpaperCropper/src/com/android/gallery3d/glrenderer/GLPaint.java b/packages/WallpaperCropper/src/com/android/gallery3d/glrenderer/GLPaint.java
index 16b2206..b26e9ab 100644
--- a/packages/WallpaperCropper/src/com/android/gallery3d/glrenderer/GLPaint.java
+++ b/packages/WallpaperCropper/src/com/android/gallery3d/glrenderer/GLPaint.java
@@ -16,7 +16,7 @@
package com.android.gallery3d.glrenderer;
-import junit.framework.Assert;
+import com.android.gallery3d.common.Utils;
public class GLPaint {
private float mLineWidth = 1f;
@@ -31,7 +31,7 @@
}
public void setLineWidth(float width) {
- Assert.assertTrue(width >= 0);
+ Utils.assertTrue(width >= 0);
mLineWidth = width;
}
diff --git a/packages/WallpaperCropper/src/com/android/gallery3d/glrenderer/UploadedTexture.java b/packages/WallpaperCropper/src/com/android/gallery3d/glrenderer/UploadedTexture.java
index f41a979..417102a 100644
--- a/packages/WallpaperCropper/src/com/android/gallery3d/glrenderer/UploadedTexture.java
+++ b/packages/WallpaperCropper/src/com/android/gallery3d/glrenderer/UploadedTexture.java
@@ -20,7 +20,7 @@
import android.graphics.Bitmap.Config;
import android.opengl.GLUtils;
-import junit.framework.Assert;
+import com.android.gallery3d.common.Utils;
import java.util.HashMap;
@@ -144,7 +144,7 @@
}
private void freeBitmap() {
- Assert.assertTrue(mBitmap != null);
+ Utils.assertTrue(mBitmap != null);
onFreeBitmap(mBitmap);
mBitmap = null;
}
@@ -219,7 +219,7 @@
int texWidth = getTextureWidth();
int texHeight = getTextureHeight();
- Assert.assertTrue(bWidth <= texWidth && bHeight <= texHeight);
+ Utils.assertTrue(bWidth <= texWidth && bHeight <= texHeight);
// Upload the bitmap to a new texture.
mId = canvas.getGLId().generateTexture();
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
new file mode 100644
index 0000000..9aa6490
--- /dev/null
+++ b/proto/src/wifi.proto
@@ -0,0 +1,462 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+
+syntax = "proto2";
+
+package clearcut.connectivity;
+
+option java_package = "com.android.server.wifi";
+option java_outer_classname = "WifiMetricsProto";
+
+// The information about the Wifi events.
+message WifiLog {
+
+ // Session information that gets logged for every Wifi connection.
+ repeated ConnectionEvent connection_event = 1;
+
+ // Number of saved networks in the user profile.
+ optional int32 num_saved_networks = 2;
+
+ // Number of open networks in the saved networks.
+ optional int32 num_open_networks = 3;
+
+ // Number of personal networks.
+ optional int32 num_personal_networks = 4;
+
+ // Number of enterprise networks.
+ optional int32 num_enterprise_networks = 5;
+
+ // Does the user have location setting enabled.
+ optional bool is_location_enabled = 6;
+
+ // Does the user have scanning enabled.
+ optional bool is_scanning_always_enabled = 7;
+
+ // Number of times user toggled wifi using the settings menu.
+ optional int32 num_wifi_toggled_via_settings = 8;
+
+ // Number of times user toggled wifi using the airplane menu.
+ optional int32 num_wifi_toggled_via_airplane = 9;
+
+ // Number of networks added by the user.
+ optional int32 num_networks_added_by_user = 10;
+
+ // Number of networks added by applications.
+ optional int32 num_networks_added_by_apps = 11;
+
+ // Number scans that returned empty results.
+ optional int32 num_empty_scan_results = 12;
+
+ // Number scans that returned at least one result.
+ optional int32 num_non_empty_scan_results = 13;
+
+ // Number of scans that were one time.
+ optional int32 num_oneshot_scans = 14;
+
+ // Number of repeated background scans that were scheduled to the chip.
+ optional int32 num_background_scans = 15;
+
+ // Error codes that a scan can result in.
+ enum ScanReturnCode {
+
+ // Return Code is unknown.
+ SCAN_UNKNOWN = 0;
+
+ // Scan was successful.
+ SCAN_SUCCESS = 1;
+
+ // Scan was successfully started, but was interrupted.
+ SCAN_FAILURE_INTERRUPTED = 2;
+
+ // Scan failed to start because of invalid configuration
+ // (bad channel, etc).
+ SCAN_FAILURE_INVALID_CONFIGURATION = 3;
+
+ // Could not start a scan because wifi is disabled.
+ FAILURE_WIFI_DISABLED = 4;
+
+ }
+
+ // Mapping of error codes to the number of times that scans resulted
+ // in that error.
+ repeated ScanReturnEntry scan_return_entries = 16;
+
+ message ScanReturnEntry {
+
+ // Return code of the scan.
+ optional ScanReturnCode scan_return_code = 1;
+
+ // Number of entries that were found in the scan.
+ optional int32 scan_results_count = 2;
+ }
+
+ // State of the Wifi.
+ enum WifiState {
+
+ // State is unknown.
+ WIFI_UNKNOWN = 0;
+
+ // Wifi is disabled.
+ WIFI_DISABLED = 1;
+
+ // Wifi is enabled.
+ WIFI_DISCONNECTED = 2;
+
+ // Wifi is enabled and associated with an AP.
+ WIFI_ASSOCIATED = 3;
+ }
+
+ // Mapping of system state to the number of times that scans were requested in
+ // that state
+ repeated WifiSystemStateEntry wifi_system_state_entries = 17;
+
+ message WifiSystemStateEntry {
+
+ // Current WiFi state.
+ optional WifiState wifi_state = 1;
+
+ // Count of scans in state.
+ optional int32 wifi_state_count = 2;
+
+ // Is screen on.
+ optional bool is_screen_on = 3;
+ }
+
+ // Mapping of Error/Success codes to the number of background scans that resulted in it
+ repeated ScanReturnEntry background_scan_return_entries = 18;
+
+ // Mapping of system state to the number of times that Background scans were requested in that
+ // state
+ repeated WifiSystemStateEntry background_scan_request_state = 19;
+
+ // Total number of times the Watchdog of Last Resort triggered, resetting the wifi stack
+ optional int32 num_last_resort_watchdog_triggers = 20;
+
+ // Total number of networks over bad association threshold when watchdog triggered
+ optional int32 num_last_resort_watchdog_bad_association_networks_total = 21;
+
+ // Total number of networks over bad authentication threshold when watchdog triggered
+ optional int32 num_last_resort_watchdog_bad_authentication_networks_total = 22;
+
+ // Total number of networks over bad dhcp threshold when watchdog triggered
+ optional int32 num_last_resort_watchdog_bad_dhcp_networks_total = 23;
+
+ // Total number of networks over bad other threshold when watchdog triggered
+ optional int32 num_last_resort_watchdog_bad_other_networks_total = 24;
+
+ // Total count of networks seen when watchdog triggered
+ optional int32 num_last_resort_watchdog_available_networks_total = 25;
+
+ // Total count of triggers with atleast one bad association network
+ optional int32 num_last_resort_watchdog_triggers_with_bad_association = 26;
+
+ // Total count of triggers with atleast one bad authentication network
+ optional int32 num_last_resort_watchdog_triggers_with_bad_authentication = 27;
+
+ // Total count of triggers with atleast one bad dhcp network
+ optional int32 num_last_resort_watchdog_triggers_with_bad_dhcp = 28;
+
+ // Total count of triggers with atleast one bad other network
+ optional int32 num_last_resort_watchdog_triggers_with_bad_other = 29;
+
+ // Count of times connectivity watchdog confirmed pno is working
+ optional int32 num_connectivity_watchdog_pno_good = 30;
+
+ // Count of times connectivity watchdog found pno not working
+ optional int32 num_connectivity_watchdog_pno_bad = 31;
+
+ // Count of times connectivity watchdog confirmed background scan is working
+ optional int32 num_connectivity_watchdog_background_good = 32;
+
+ // Count of times connectivity watchdog found background scan not working
+ optional int32 num_connectivity_watchdog_background_bad = 33;
+
+ // The time duration represented by this wifi log, from start to end of capture
+ optional int32 record_duration_sec = 34;
+
+ // Counts the occurrences of each individual RSSI poll level
+ repeated RssiPollCount rssi_poll_rssi_count = 35;
+
+ // Total number of times WiFi connected immediately after a Last Resort Watchdog trigger,
+ // without new networks becoming available.
+ optional int32 num_last_resort_watchdog_successes = 36;
+
+ // Total number of saved hidden networks
+ optional int32 num_hidden_networks = 37;
+
+ // Total number of saved passpoint / hotspot 2.0 networks
+ optional int32 num_passpoint_networks = 38;
+
+ // Total number of scan results
+ optional int32 num_total_scan_results = 39;
+
+ // Total number of scan results for open networks
+ optional int32 num_open_network_scan_results = 40;
+
+ // Total number of scan results for personal networks
+ optional int32 num_personal_network_scan_results = 41;
+
+ // Total number of scan results for enterprise networks
+ optional int32 num_enterprise_network_scan_results = 42;
+
+ // Total number of scan results for hidden networks
+ optional int32 num_hidden_network_scan_results = 43;
+
+ // Total number of scan results for hotspot 2.0 r1 networks
+ optional int32 num_hotspot2_r1_network_scan_results = 44;
+
+ // Total number of scan results for hotspot 2.0 r2 networks
+ optional int32 num_hotspot2_r2_network_scan_results = 45;
+
+ // Total number of scans handled by framework (oneshot or otherwise)
+ optional int32 num_scans = 46;
+
+ // Counts the occurrences of each alert reason.
+ repeated AlertReasonCount alert_reason_count = 47;
+
+ // Counts the occurrences of each Wifi score
+ repeated WifiScoreCount wifi_score_count = 48;
+
+ // Histogram of Soft AP Durations
+ repeated SoftApDurationBucket soft_ap_duration = 49;
+
+ // Histogram of Soft AP ReturnCode
+ repeated SoftApReturnCodeCount soft_ap_return_code = 50;
+
+ // Histogram of the delta between scan result RSSI and RSSI polls
+ repeated RssiPollCount rssi_poll_delta_count = 51;
+}
+
+// Information that gets logged for every WiFi connection.
+message RouterFingerPrint {
+
+ enum RoamType {
+
+ // Type is unknown.
+ ROAM_TYPE_UNKNOWN = 0;
+
+ // No roaming - usually happens on a single band (2.4 GHz) router.
+ ROAM_TYPE_NONE = 1;
+
+ // Enterprise router.
+ ROAM_TYPE_ENTERPRISE = 2;
+
+ // DBDC => Dual Band Dual Concurrent essentially a router that
+ // supports both 2.4 GHz and 5 GHz bands.
+ ROAM_TYPE_DBDC = 3;
+ }
+
+ enum Auth {
+
+ // Auth is unknown.
+ AUTH_UNKNOWN = 0;
+
+ // No authentication.
+ AUTH_OPEN = 1;
+
+ // If the router uses a personal authentication.
+ AUTH_PERSONAL = 2;
+
+ // If the router is setup for enterprise authentication.
+ AUTH_ENTERPRISE = 3;
+ }
+
+ enum RouterTechnology {
+
+ // Router is unknown.
+ ROUTER_TECH_UNKNOWN = 0;
+
+ // Router Channel A.
+ ROUTER_TECH_A = 1;
+
+ // Router Channel B.
+ ROUTER_TECH_B = 2;
+
+ // Router Channel G.
+ ROUTER_TECH_G = 3;
+
+ // Router Channel N.
+ ROUTER_TECH_N = 4;
+
+ // Router Channel AC.
+ ROUTER_TECH_AC = 5;
+
+ // When the channel is not one of the above.
+ ROUTER_TECH_OTHER = 6;
+ }
+
+ optional RoamType roam_type = 1;
+
+ // Channel on which the connection takes place.
+ optional int32 channel_info = 2;
+
+ // DTIM setting of the router.
+ optional int32 dtim = 3;
+
+ // Authentication scheme of the router.
+ optional Auth authentication = 4;
+
+ // If the router is hidden.
+ optional bool hidden = 5;
+
+ // Channel information.
+ optional RouterTechnology router_technology = 6;
+
+ // whether ipv6 is supported.
+ optional bool supports_ipv6 = 7;
+
+ // If the router is a passpoint / hotspot 2.0 network
+ optional bool passpoint = 8;
+}
+
+message ConnectionEvent {
+
+ // Roam Type.
+ enum RoamType {
+
+ // Type is unknown.
+ ROAM_UNKNOWN = 0;
+
+ // No roaming.
+ ROAM_NONE = 1;
+
+ // DBDC roaming.
+ ROAM_DBDC = 2;
+
+ // Enterprise roaming.
+ ROAM_ENTERPRISE = 3;
+
+ // User selected roaming.
+ ROAM_USER_SELECTED = 4;
+
+ // Unrelated.
+ ROAM_UNRELATED = 5;
+ }
+
+ // Connectivity Level Failure.
+ enum ConnectivityLevelFailure {
+
+ // Failure is unknown.
+ HLF_UNKNOWN = 0;
+
+ // No failure.
+ HLF_NONE = 1;
+
+ // DHCP failure.
+ HLF_DHCP = 2;
+
+ // No internet connection.
+ HLF_NO_INTERNET = 3;
+
+ // No internet connection.
+ HLF_UNWANTED = 4;
+ }
+
+ // Start time of the connection.
+ optional int64 start_time_millis = 1;// [(datapol.semantic_type) = ST_TIMESTAMP];
+
+ // Duration to connect.
+ optional int32 duration_taken_to_connect_millis = 2;
+
+ // Router information.
+ optional RouterFingerPrint router_fingerprint = 3;
+
+ // RSSI at the start of the connection.
+ optional int32 signal_strength = 4;
+
+ // Roam Type.
+ optional RoamType roam_type = 5;
+
+ // Result of the connection.
+ optional int32 connection_result = 6;
+
+ // Reasons for level 2 failure (needs to be coordinated with wpa-supplicant).
+ optional int32 level_2_failure_code = 7;
+
+ // Failures that happen at the connectivity layer.
+ optional ConnectivityLevelFailure connectivity_level_failure_code = 8;
+
+ // Has bug report been taken.
+ optional bool automatic_bug_report_taken = 9;
+}
+
+// Number of occurrences of a specific RSSI poll rssi value
+message RssiPollCount {
+ // RSSI
+ optional int32 rssi = 1;
+
+ // Number of RSSI polls with 'rssi'
+ optional int32 count = 2;
+}
+
+// Number of occurrences of a specific alert reason value
+message AlertReasonCount {
+ // Alert reason
+ optional int32 reason = 1;
+
+ // Number of alerts with |reason|.
+ optional int32 count = 2;
+}
+
+// Counts the number of instances of a specific Wifi Score calculated by WifiScoreReport
+message WifiScoreCount {
+ // Wifi Score
+ optional int32 score = 1;
+
+ // Number of Wifi score reports with this score
+ optional int32 count = 2;
+}
+
+// Number of occurrences of Soft AP session durations
+message SoftApDurationBucket {
+ // Bucket covers duration : [duration_sec, duration_sec + bucket_size_sec)
+ // The (inclusive) lower bound of Soft AP session duration represented by this bucket
+ optional int32 duration_sec = 1;
+
+ // The size of this bucket
+ optional int32 bucket_size_sec = 2;
+
+ // Number of soft AP session durations that fit into this bucket
+ optional int32 count = 3;
+}
+
+// Number of occurrences of a soft AP session return code
+message SoftApReturnCodeCount {
+
+ enum SoftApStartResult {
+
+ // SoftApManager return code unknown
+ SOFT_AP_RETURN_CODE_UNKNOWN = 0;
+
+ // SoftAp started successfully
+ SOFT_AP_STARTED_SUCCESSFULLY = 1;
+
+ // Catch all for failures with no specific failure reason
+ SOFT_AP_FAILED_GENERAL_ERROR = 2;
+
+ // SoftAp failed to start due to NO_CHANNEL error
+ SOFT_AP_FAILED_NO_CHANNEL = 3;
+ }
+
+ // Historical, no longer used for writing as of 01/2017.
+ optional int32 return_code = 1 [deprecated = true];
+
+ // Occurences of this soft AP return code
+ optional int32 count = 2;
+
+ // Result of attempt to start SoftAp
+ optional SoftApStartResult start_result = 3;
+}
diff --git a/rs/java/android/renderscript/Element.java b/rs/java/android/renderscript/Element.java
index 9d2f750..667bf71 100644
--- a/rs/java/android/renderscript/Element.java
+++ b/rs/java/android/renderscript/Element.java
@@ -1071,7 +1071,6 @@
mSize += mElements[ct].mSize * mArraySizes[ct];
}
updateVisibleSubElements();
- guard.open("destroy");
}
Element(long id, RenderScript rs, DataType dt, DataKind dk, boolean norm, int size) {
@@ -1091,7 +1090,6 @@
mKind = dk;
mNormalized = norm;
mVectorSize = size;
- guard.open("destroy");
}
Element(long id, RenderScript rs) {
diff --git a/rs/java/android/renderscript/Type.java b/rs/java/android/renderscript/Type.java
index 9252898..dc23785 100644
--- a/rs/java/android/renderscript/Type.java
+++ b/rs/java/android/renderscript/Type.java
@@ -227,7 +227,6 @@
Type(long id, RenderScript rs) {
super(id, rs);
- guard.open("destroy");
}
@Override
diff --git a/rs/jni/Android.mk b/rs/jni/Android.mk
index bf3681b..4040db3 100644
--- a/rs/jni/Android.mk
+++ b/rs/jni/Android.mk
@@ -5,6 +5,7 @@
android_renderscript_RenderScript.cpp
LOCAL_SHARED_LIBRARIES := \
+ libandroid \
libandroid_runtime \
libandroidfw \
libnativehelper \
@@ -19,22 +20,17 @@
LOCAL_STATIC_LIBRARIES :=
-rs_generated_include_dir := $(call intermediates-dir-for,SHARED_LIBRARIES,libRS,,)
-
LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE) \
frameworks/rs \
frameworks/base/core/jni \
- frameworks/base/libs/hwui \
- $(rs_generated_include_dir)
+ frameworks/base/libs/hwui
LOCAL_CFLAGS += -Wno-unused-parameter
LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
-LOCAL_ADDITIONAL_DEPENDENCIES := $(addprefix $(rs_generated_include_dir)/,rsgApiFuncDecl.h)
LOCAL_MODULE:= librs_jni
-LOCAL_ADDITIONAL_DEPENDENCIES += $(rs_generated_source)
LOCAL_MODULE_TAGS := optional
-LOCAL_REQUIRED_MODULES := libRS libRSDriver
+LOCAL_REQUIRED_MODULES := libRS
include $(BUILD_SHARED_LIBRARY)
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index af370ff..b4630ef 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -34,9 +34,11 @@
#include "android_runtime/android_view_Surface.h"
#include "android_runtime/android_util_AssetManager.h"
#include "android/graphics/GraphicsJNI.h"
+#include "android/native_window.h"
+#include "android/native_window_jni.h"
-#include <rs.h>
#include <rsEnv.h>
+#include <rsApiStubs.h>
#include <gui/Surface.h>
#include <gui/GLConsumer.h>
#include <android_runtime/android_graphics_SurfaceTexture.h>
@@ -1134,7 +1136,7 @@
// we will pack mType; mKind; mNormalized; mVectorSize; NumSubElements
assert(dataSize == 5);
- uintptr_t elementData[5];
+ uint32_t elementData[5];
rsaElementGetNativeData((RsContext)con, (RsElement)id, elementData, dataSize);
for(jint i = 0; i < dataSize; i ++) {
@@ -1157,7 +1159,7 @@
uintptr_t *ids = (uintptr_t*)malloc(dataSize * sizeof(uintptr_t));
const char **names = (const char **)malloc(dataSize * sizeof(const char *));
- uint32_t *arraySizes = (uint32_t *)malloc(dataSize * sizeof(uint32_t));
+ size_t *arraySizes = (size_t *)malloc(dataSize * sizeof(size_t));
rsaElementGetSubElements((RsContext)con, (RsElement)id, ids, names, arraySizes,
(uint32_t)dataSize);
@@ -1264,10 +1266,10 @@
ALOGD("nAllocationGetSurface, con(%p), a(%p)", (RsContext)con, (RsAllocation)a);
}
- IGraphicBufferProducer *v = (IGraphicBufferProducer *)rsAllocationGetSurface((RsContext)con,
- (RsAllocation)a);
- sp<IGraphicBufferProducer> bp = v;
- v->decStrong(nullptr);
+ ANativeWindow *anw = (ANativeWindow *)rsAllocationGetSurface((RsContext)con, (RsAllocation)a);
+
+ sp<Surface> surface(static_cast<Surface*>(anw));
+ sp<IGraphicBufferProducer> bp = surface->getIGraphicBufferProducer();
jobject o = android_view_Surface_createFromIGraphicBufferProducer(_env, bp);
return o;
@@ -1281,13 +1283,12 @@
(RsAllocation)alloc, (Surface *)sur);
}
- sp<Surface> s;
+ ANativeWindow *anw = nullptr;
if (sur != 0) {
- s = android_view_Surface_getSurface(_env, sur);
+ anw = ANativeWindow_fromSurface(_env, sur);
}
- rsAllocationSetSurface((RsContext)con, (RsAllocation)alloc,
- static_cast<ANativeWindow *>(s.get()));
+ rsAllocationSetSurface((RsContext)con, (RsAllocation)alloc, anw);
}
static void
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 3b3ce07..5e9cf74 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -81,6 +81,8 @@
private static final String SECURE_SETTINGS_BLUETOOTH_ADDR_VALID="bluetooth_addr_valid";
private static final String SECURE_SETTINGS_BLUETOOTH_ADDRESS="bluetooth_address";
private static final String SECURE_SETTINGS_BLUETOOTH_NAME="bluetooth_name";
+ private static final String REASON_AIRPLANE_MODE = "airplane mode";
+ private static final String REASON_SYSTEM_BOOT = "system boot";
private static final int TIMEOUT_BIND_MS = 3000; //Maximum msec to wait for a bind
private static final int TIMEOUT_SAVE_MS = 500; //Maximum msec to wait for a save
//Maximum msec to wait for service restart
@@ -166,7 +168,7 @@
}
public String toString() {
- return android.text.format.DateFormat.format("MM-dd hh:mm:ss ", mTimestamp) +
+ return android.text.format.DateFormat.format("MM-dd HH:mm:ss ", mTimestamp) +
(mEnable ? " Enabled " : " Disabled ") + " by " + mPackageName;
}
@@ -194,19 +196,6 @@
private final boolean mPermissionReviewRequired;
- private void registerForAirplaneMode(IntentFilter filter) {
- final ContentResolver resolver = mContext.getContentResolver();
- final String airplaneModeRadios = Settings.Global.getString(resolver,
- Settings.Global.AIRPLANE_MODE_RADIOS);
- final String toggleableRadios = Settings.Global.getString(resolver,
- Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS);
- boolean mIsAirplaneSensitive = airplaneModeRadios == null ? true :
- airplaneModeRadios.contains(Settings.Global.RADIO_BLUETOOTH);
- if (mIsAirplaneSensitive) {
- filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- }
- }
-
private final IBluetoothCallback mBluetoothCallback = new IBluetoothCallback.Stub() {
@Override
public void onBluetoothStateChange(int prevState, int newState) throws RemoteException {
@@ -239,6 +228,62 @@
}
};
+ private final ContentObserver mAirplaneModeObserver = new ContentObserver(null) {
+ @Override
+ public void onChange(boolean unused) {
+ synchronized(this) {
+ if (isBluetoothPersistedStateOn()) {
+ if (isAirplaneModeOn()) {
+ persistBluetoothSetting(BLUETOOTH_ON_AIRPLANE);
+ } else {
+ persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
+ }
+ }
+
+ int st = BluetoothAdapter.STATE_OFF;
+ try {
+ mBluetoothLock.readLock().lock();
+ if (mBluetooth != null) {
+ st = mBluetooth.getState();
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to call getState", e);
+ return;
+ } finally {
+ mBluetoothLock.readLock().unlock();
+ }
+
+ Slog.d(TAG, "Airplane Mode change - current state: " +
+ BluetoothAdapter.nameForState(st));
+
+ if (isAirplaneModeOn()) {
+ // Clear registered LE apps to force shut-off
+ clearBleApps();
+
+ // If state is BLE_ON make sure we trigger disableBLE
+ if (st == BluetoothAdapter.STATE_BLE_ON) {
+ try {
+ mBluetoothLock.readLock().lock();
+ if (mBluetooth != null) {
+ mBluetooth.onBrEdrDown();
+ mEnable = false;
+ mEnableExternal = false;
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG,"Unable to call onBrEdrDown", e);
+ } finally {
+ mBluetoothLock.readLock().unlock();
+ }
+ } else if (st == BluetoothAdapter.STATE_ON){
+ sendDisableMsg(REASON_AIRPLANE_MODE);
+ }
+ } else if (mEnableExternal) {
+ sendEnableMsg(mQuietEnableExternal, REASON_AIRPLANE_MODE);
+ }
+ }
+ }
+ };
+
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -249,57 +294,6 @@
if (newName != null) {
storeNameAndAddress(newName, null);
}
- } else if (Intent.ACTION_AIRPLANE_MODE_CHANGED.equals(action)) {
- synchronized(mReceiver) {
- if (isBluetoothPersistedStateOn()) {
- if (isAirplaneModeOn()) {
- persistBluetoothSetting(BLUETOOTH_ON_AIRPLANE);
- } else {
- persistBluetoothSetting(BLUETOOTH_ON_BLUETOOTH);
- }
- }
-
- int st = BluetoothAdapter.STATE_OFF;
- try {
- mBluetoothLock.readLock().lock();
- if (mBluetooth != null) {
- st = mBluetooth.getState();
- }
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to call getState", e);
- } finally {
- mBluetoothLock.readLock().unlock();
- }
- Slog.d(TAG, "State " + BluetoothAdapter.nameForState(st));
-
- if (isAirplaneModeOn()) {
- // Clear registered LE apps to force shut-off
- clearBleApps();
- if (st == BluetoothAdapter.STATE_BLE_ON) {
- //if state is BLE_ON make sure you trigger disableBLE part
- try {
- mBluetoothLock.readLock().lock();
- if (mBluetooth != null) {
- mBluetooth.onBrEdrDown();
- mEnable = false;
- mEnableExternal = false;
- }
- } catch (RemoteException e) {
- Slog.e(TAG,"Unable to call onBrEdrDown", e);
- } finally {
- mBluetoothLock.readLock().unlock();
- }
- } else if (st == BluetoothAdapter.STATE_ON){
- // disable without persisting the setting
- Slog.d(TAG, "Calling disable");
- sendDisableMsg("airplane mode");
- }
- } else if (mEnableExternal) {
- // enable without persisting the setting
- Slog.d(TAG, "Calling enable");
- sendEnableMsg(mQuietEnableExternal, "airplane mode");
- }
- }
}
}
};
@@ -332,7 +326,6 @@
mCallbacks = new RemoteCallbackList<IBluetoothManagerCallback>();
mStateChangeCallbacks = new RemoteCallbackList<IBluetoothStateChangeCallback>();
IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_LOCAL_NAME_CHANGED);
- registerForAirplaneMode(filter);
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
mContext.registerReceiver(mReceiver, filter);
loadStoredNameAndAddress();
@@ -340,6 +333,15 @@
mEnableExternal = true;
}
+ String airplaneModeRadios = Settings.Global.getString(mContentResolver,
+ Settings.Global.AIRPLANE_MODE_RADIOS);
+ if (airplaneModeRadios == null ||
+ airplaneModeRadios.contains(Settings.Global.RADIO_BLUETOOTH)) {
+ mContentResolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON),
+ true, mAirplaneModeObserver);
+ }
+
int systemUiUid = -1;
try {
systemUiUid = mContext.getPackageManager().getPackageUidAsUser("com.android.systemui",
@@ -636,6 +638,9 @@
if (appCount == 0 && mEnable) {
disableBleScanMode();
}
+ if (appCount == 0 && !mEnableExternal) {
+ sendBrEdrDownCallback();
+ }
return appCount;
}
@@ -691,7 +696,14 @@
return;
}
- if (isBleAppPresent() == false) {
+ if (isBleAppPresent()) {
+ // Need to stay at BLE ON. Disconnect all Gatt connections
+ try {
+ mBluetoothGatt.unregAll();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to disconnect all apps.", e);
+ }
+ } else {
try {
mBluetoothLock.readLock().lock();
if (mBluetooth != null) mBluetooth.onBrEdrDown();
@@ -700,14 +712,8 @@
} finally {
mBluetoothLock.readLock().unlock();
}
- } else {
- // Need to stay at BLE ON. Disconnect all Gatt connections
- try {
- mBluetoothGatt.unregAll();
- } catch (RemoteException e) {
- Slog.e(TAG, "Unable to disconnect all apps.", e);
- }
}
+
}
public boolean enableNoAutoConnect(String packageName)
@@ -958,7 +964,7 @@
}
if (mEnableExternal && isBluetoothPersistedStateOnBluetooth()) {
if (DBG) Slog.d(TAG, "Auto-enabling Bluetooth.");
- sendEnableMsg(mQuietEnableExternal, "system boot");
+ sendEnableMsg(mQuietEnableExternal, REASON_SYSTEM_BOOT);
} else if (!isNameAndAddressSet()) {
if (DBG) Slog.d(TAG, "Getting adapter name and address");
Message getMsg = mHandler.obtainMessage(MESSAGE_GET_NAME_AND_ADDRESS);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index dfca02e..7466f54 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -395,16 +395,6 @@
*/
private static final int EVENT_REGISTER_NETWORK_LISTENER_WITH_INTENT = 31;
- /**
- * Indicates a caller has requested to have its callback invoked with
- * the latest LinkProperties or NetworkCapabilities.
- *
- * arg1 = UID of caller
- * obj = NetworkRequest
- */
- private static final int EVENT_REQUEST_LINKPROPERTIES = 32;
- private static final int EVENT_REQUEST_NETCAPABILITIES = 33;
-
/** Handler thread used for both of the handlers below. */
@VisibleForTesting
protected final HandlerThread mHandlerThread;
@@ -815,6 +805,8 @@
intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
mContext.registerReceiverAsUser(
mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);
+ mContext.registerReceiverAsUser(mUserPresentReceiver, UserHandle.SYSTEM,
+ new IntentFilter(Intent.ACTION_USER_PRESENT), null, null);
try {
mNetd.registerObserver(mTethering);
@@ -2572,34 +2564,6 @@
return nri;
}
- private void handleRequestCallbackUpdate(NetworkRequest request, int callingUid,
- String description, int callbackType) {
- final NetworkRequestInfo nri = getNriForAppRequest(request, callingUid, description);
- if (nri == null) return;
-
- final NetworkAgentInfo nai = mNetworkForRequestId.get(nri.request.requestId);
- // The network that is satisfying this request may have changed since
- // the application requested the update.
- //
- // - If the request is no longer satisfied, don't send any updates.
- // - If the request is satisfied by a different network, it is the
- // caller's responsibility to check that the Network object in the
- // callback matches the network that was returned in the last
- // onAvailable() callback for this request.
- if (nai == null) return;
- callCallbackForRequest(nri, nai, callbackType, 0);
- }
-
- private void handleRequestLinkProperties(NetworkRequest request, int callingUid) {
- handleRequestCallbackUpdate(request, callingUid,
- "request LinkProperties", ConnectivityManager.CALLBACK_IP_CHANGED);
- }
-
- private void handleRequestNetworkCapabilities(NetworkRequest request, int callingUid) {
- handleRequestCallbackUpdate(request, callingUid,
- "request NetworkCapabilities", ConnectivityManager.CALLBACK_CAP_CHANGED);
- }
-
private void handleTimedOutNetworkRequest(final NetworkRequestInfo nri) {
if (mNetworkRequests.get(nri.request) != null && mNetworkForRequestId.get(
nri.request.requestId) == null) {
@@ -2981,12 +2945,6 @@
handleMobileDataAlwaysOn();
break;
}
- case EVENT_REQUEST_LINKPROPERTIES:
- handleRequestLinkProperties((NetworkRequest) msg.obj, msg.arg1);
- break;
- case EVENT_REQUEST_NETCAPABILITIES:
- handleRequestNetworkCapabilities((NetworkRequest) msg.obj, msg.arg1);
- break;
// Sent by KeepaliveTracker to process an app request on the state machine thread.
case NetworkAgent.CMD_START_PACKET_KEEPALIVE: {
mKeepaliveTracker.handleStartKeepalive(msg);
@@ -3129,8 +3087,7 @@
}
@Override
- public void startTethering(int type, ResultReceiver receiver,
- boolean showProvisioningUi) {
+ public void startTethering(int type, ResultReceiver receiver, boolean showProvisioningUi) {
ConnectivityManager.enforceTetherChangePermission(mContext);
if (!isTetheringSupported()) {
receiver.send(ConnectivityManager.TETHER_ERROR_UNSUPPORTED, null);
@@ -3661,7 +3618,12 @@
// Tear down existing lockdown if profile was removed
mLockdownEnabled = LockdownVpnTracker.isEnabled();
if (mLockdownEnabled) {
- final String profileName = new String(mKeyStore.get(Credentials.LOCKDOWN_VPN));
+ byte[] profileTag = mKeyStore.get(Credentials.LOCKDOWN_VPN);
+ if (profileTag == null) {
+ Slog.e(TAG, "Lockdown VPN configured but cannot be read from keystore");
+ return false;
+ }
+ String profileName = new String(profileTag);
final VpnProfile profile = VpnProfile.decode(
profileName, mKeyStore.get(Credentials.VPN + profileName));
if (profile == null) {
@@ -4000,6 +3962,16 @@
}
};
+ private BroadcastReceiver mUserPresentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ // Try creating lockdown tracker, since user present usually means
+ // unlocked keystore.
+ updateLockdownVpn();
+ mContext.unregisterReceiver(this);
+ }
+ };
+
private final HashMap<Messenger, NetworkFactoryInfo> mNetworkFactoryInfos =
new HashMap<Messenger, NetworkFactoryInfo>();
private final HashMap<NetworkRequest, NetworkRequestInfo> mNetworkRequests =
@@ -4163,7 +4135,7 @@
}
ensureRequestableCapabilities(networkCapabilities);
- if (timeoutMs < 0 || timeoutMs > ConnectivityManager.MAX_NETWORK_REQUEST_TIMEOUT_MS) {
+ if (timeoutMs < 0) {
throw new IllegalArgumentException("Bad timeout specified");
}
@@ -4332,22 +4304,6 @@
}
@Override
- public void requestLinkProperties(NetworkRequest networkRequest) {
- ensureNetworkRequestHasType(networkRequest);
- if (networkRequest.type == NetworkRequest.Type.LISTEN) return;
- mHandler.sendMessage(mHandler.obtainMessage(
- EVENT_REQUEST_LINKPROPERTIES, getCallingUid(), 0, networkRequest));
- }
-
- @Override
- public void requestNetworkCapabilities(NetworkRequest networkRequest) {
- ensureNetworkRequestHasType(networkRequest);
- if (networkRequest.type == NetworkRequest.Type.LISTEN) return;
- mHandler.sendMessage(mHandler.obtainMessage(
- EVENT_REQUEST_NETCAPABILITIES, getCallingUid(), 0, networkRequest));
- }
-
- @Override
public void releaseNetworkRequest(NetworkRequest networkRequest) {
ensureNetworkRequestHasType(networkRequest);
mHandler.sendMessage(mHandler.obtainMessage(
@@ -4839,7 +4795,7 @@
if (!nr.isListen()) continue;
if (nai.satisfies(nr) && !nai.isSatisfyingRequest(nr.requestId)) {
nai.addRequest(nr);
- notifyNetworkCallback(nai, nri);
+ notifyNetworkAvailable(nai, nri);
}
}
}
@@ -5021,7 +4977,7 @@
// do this after the default net is switched, but
// before LegacyTypeTracker sends legacy broadcasts
- for (NetworkRequestInfo nri : addedRequests) notifyNetworkCallback(newNetwork, nri);
+ for (NetworkRequestInfo nri : addedRequests) notifyNetworkAvailable(newNetwork, nri);
// Linger any networks that are no longer needed. This should be done after sending the
// available callback for newNetwork.
@@ -5184,7 +5140,7 @@
}
private void updateNetworkInfo(NetworkAgentInfo networkAgent, NetworkInfo newInfo) {
- NetworkInfo.State state = newInfo.getState();
+ final NetworkInfo.State state = newInfo.getState();
NetworkInfo oldInfo = null;
final int oldScore = networkAgent.getCurrentScore();
synchronized (networkAgent) {
@@ -5311,15 +5267,27 @@
sendUpdatedScoreToFactories(nai);
}
- // notify only this one new request of the current state
- protected void notifyNetworkCallback(NetworkAgentInfo nai, NetworkRequestInfo nri) {
- int notifyType = ConnectivityManager.CALLBACK_AVAILABLE;
+ // Notify only this one new request of the current state. Transfer all the
+ // current state by calling NetworkCapabilities and LinkProperties callbacks
+ // so that callers can be guaranteed to have as close to atomicity in state
+ // transfer as can be supported by this current API.
+ protected void notifyNetworkAvailable(NetworkAgentInfo nai, NetworkRequestInfo nri) {
mHandler.removeMessages(EVENT_TIMEOUT_NETWORK_REQUEST, nri);
- if (nri.mPendingIntent == null) {
- callCallbackForRequest(nri, nai, notifyType, 0);
- } else {
- sendPendingIntentForRequest(nri, nai, notifyType);
+ if (nri.mPendingIntent != null) {
+ sendPendingIntentForRequest(nri, nai, ConnectivityManager.CALLBACK_AVAILABLE);
+ // Attempt no subsequent state pushes where intents are involved.
+ return;
}
+
+ callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_AVAILABLE, 0);
+ // Whether a network is currently suspended is also an important
+ // element of state to be transferred (it would not otherwise be
+ // delivered by any currently available mechanism).
+ if (nai.networkInfo.getState() == NetworkInfo.State.SUSPENDED) {
+ callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_SUSPENDED, 0);
+ }
+ callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_CAP_CHANGED, 0);
+ callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_IP_CHANGED, 0);
}
private void sendLegacyNetworkBroadcast(NetworkAgentInfo nai, DetailedState state, int type) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 00fe337..1feaa72 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -22278,4 +22278,29 @@
// before the profile user is unlocked.
return rInfo != null && rInfo.activityInfo != null;
}
+
+ /**
+ * Attach an agent to the specified process (proces name or PID)
+ */
+ public void attachAgent(String process, String path) {
+ try {
+ synchronized (this) {
+ ProcessRecord proc = findProcessLocked(process, UserHandle.USER_SYSTEM, "attachAgent");
+ if (proc == null || proc.thread == null) {
+ throw new IllegalArgumentException("Unknown process: " + process);
+ }
+
+ boolean isDebuggable = "1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"));
+ if (!isDebuggable) {
+ if ((proc.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0) {
+ throw new SecurityException("Process not debuggable: " + proc);
+ }
+ }
+
+ proc.thread.attachAgent(path);
+ }
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Process disappeared");
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index adf6d36..2d0ccbd 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -66,6 +66,8 @@
return runLenientBackgroundCheck(pw);
case "get-uid-state":
return getUidState(pw);
+ case "attach-agent":
+ return runAttachAgent(pw);
default:
return handleDefaultCommands(cmd);
}
@@ -183,6 +185,21 @@
return 0;
}
+ int runAttachAgent(PrintWriter pw) {
+ // TODO: revisit the permissions required for attaching agents
+ mInternal.enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
+ "attach-agent");
+ String process = getNextArgRequired();
+ String agent = getNextArgRequired();
+ String opt;
+ if ((opt = getNextArg()) != null) {
+ pw.println("Error: Unknown option: " + opt);
+ return -1;
+ }
+ mInternal.attachAgent(process, agent);
+ return 0;
+ }
+
@Override
public void onHelp() {
PrintWriter pw = getOutPrintWriter();
@@ -241,6 +258,8 @@
pw.println(" Optionally controls lenient background check mode, returns current mode.");
pw.println(" get-uid-state <UID>");
pw.println(" Gets the process state of an app given its <UID>.");
+ pw.println(" attach-agent <PROCESS> <FILE>");
+ pw.println(" Attach an agent to the specified <PROCESS>, which may be either a process name or a PID.");
}
}
}
diff --git a/services/core/java/com/android/server/connectivity/MetricsLoggerService.java b/services/core/java/com/android/server/connectivity/MetricsLoggerService.java
deleted file mode 100644
index 1c9feb2..0000000
--- a/services/core/java/com/android/server/connectivity/MetricsLoggerService.java
+++ /dev/null
@@ -1,375 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.connectivity;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.SystemService;
-
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.net.ConnectivityMetricsEvent;
-import android.net.ConnectivityMetricsLogger;
-import android.net.IConnectivityMetricsLogger;
-import android.os.Binder;
-import android.os.Parcel;
-import android.text.format.DateUtils;
-import android.util.Log;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.ArrayDeque;
-import java.util.ArrayList;
-
-/** {@hide} */
-public class MetricsLoggerService extends SystemService {
- private static String TAG = "ConnectivityMetricsLoggerService";
- private static final boolean DBG = true;
- private static final boolean VDBG = false;
-
- public MetricsLoggerService(Context context) {
- super(context);
- }
-
- @Override
- public void onStart() {
- resetThrottlingCounters(System.currentTimeMillis());
- }
-
- @Override
- public void onBootPhase(int phase) {
- if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
- if (DBG) Log.d(TAG, "onBootPhase: PHASE_SYSTEM_SERVICES_READY");
- publishBinderService(ConnectivityMetricsLogger.CONNECTIVITY_METRICS_LOGGER_SERVICE,
- mBinder);
- }
- }
-
- // TODO: read these constants from system property
- private final int EVENTS_NOTIFICATION_THRESHOLD = 300;
- private final int MAX_NUMBER_OF_EVENTS = 1000;
- private final int THROTTLING_MAX_NUMBER_OF_MESSAGES_PER_COMPONENT = 1000;
- private final long THROTTLING_TIME_INTERVAL_MILLIS = DateUtils.HOUR_IN_MILLIS;
-
- private int mEventCounter = 0;
-
- /**
- * Reference of the last event in the list of cached events.
- *
- * When client of this service retrieves events by calling getEvents, it is passing
- * ConnectivityMetricsEvent.Reference object. After getEvents returns, that object will
- * contain this reference. The client can save it and use next time it calls getEvents.
- * This way only new events will be returned.
- */
- private long mLastEventReference = 0;
-
- private final int mThrottlingCounters[] =
- new int[ConnectivityMetricsLogger.NUMBER_OF_COMPONENTS];
-
- private long mThrottlingIntervalBoundaryMillis;
-
- private final ArrayDeque<ConnectivityMetricsEvent> mEvents = new ArrayDeque<>();
-
- private void enforceConnectivityInternalPermission() {
- getContext().enforceCallingOrSelfPermission(
- android.Manifest.permission.CONNECTIVITY_INTERNAL,
- "MetricsLoggerService");
- }
-
- private void enforceDumpPermission() {
- getContext().enforceCallingOrSelfPermission(
- android.Manifest.permission.DUMP,
- "MetricsLoggerService");
- }
-
- private void resetThrottlingCounters(long currentTimeMillis) {
- synchronized (mThrottlingCounters) {
- for (int i = 0; i < mThrottlingCounters.length; i++) {
- mThrottlingCounters[i] = 0;
- }
- mThrottlingIntervalBoundaryMillis =
- currentTimeMillis + THROTTLING_TIME_INTERVAL_MILLIS;
- }
- }
-
- private void addEvent(ConnectivityMetricsEvent e) {
- if (VDBG) {
- Log.v(TAG, "writeEvent(" + e.toString() + ")");
- }
-
- while (mEvents.size() >= MAX_NUMBER_OF_EVENTS) {
- mEvents.removeFirst();
- }
-
- mEvents.addLast(e);
- }
-
- @VisibleForTesting
- final MetricsLoggerImpl mBinder = new MetricsLoggerImpl();
-
- /**
- * Implementation of the IConnectivityMetricsLogger interface.
- */
- final class MetricsLoggerImpl extends IConnectivityMetricsLogger.Stub {
-
- private final ArrayList<PendingIntent> mPendingIntents = new ArrayList<>();
-
- @Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
- != PackageManager.PERMISSION_GRANTED) {
- pw.println("Permission Denial: can't dump ConnectivityMetricsLoggerService " +
- "from from pid=" + Binder.getCallingPid() + ", uid=" +
- Binder.getCallingUid());
- return;
- }
-
- boolean dumpSerializedSize = false;
- boolean dumpEvents = false;
- boolean dumpDebugInfo = false;
- for (String arg : args) {
- switch (arg) {
- case "--debug":
- dumpDebugInfo = true;
- break;
-
- case "--events":
- dumpEvents = true;
- break;
-
- case "--size":
- dumpSerializedSize = true;
- break;
-
- case "--all":
- dumpDebugInfo = true;
- dumpEvents = true;
- dumpSerializedSize = true;
- break;
- }
- }
-
- synchronized (mEvents) {
- pw.println("Number of events: " + mEvents.size());
- pw.println("Counter: " + mEventCounter);
- if (mEvents.size() > 0) {
- pw.println("Time span: " +
- DateUtils.formatElapsedTime(
- (System.currentTimeMillis() - mEvents.peekFirst().timestamp)
- / 1000));
- }
-
- if (dumpSerializedSize) {
- Parcel p = Parcel.obtain();
- for (ConnectivityMetricsEvent e : mEvents) {
- p.writeParcelable(e, 0);
- }
- pw.println("Serialized data size: " + p.dataSize());
- p.recycle();
- }
-
- if (dumpEvents) {
- pw.println();
- pw.println("Events:");
- for (ConnectivityMetricsEvent e : mEvents) {
- pw.println(e.toString());
- }
- }
- }
-
- if (dumpDebugInfo) {
- synchronized (mThrottlingCounters) {
- pw.println();
- for (int i = 0; i < ConnectivityMetricsLogger.NUMBER_OF_COMPONENTS; i++) {
- if (mThrottlingCounters[i] > 0) {
- pw.println("Throttling Counter #" + i + ": " + mThrottlingCounters[i]);
- }
- }
- pw.println("Throttling Time Remaining: " +
- DateUtils.formatElapsedTime(
- (mThrottlingIntervalBoundaryMillis - System.currentTimeMillis())
- / 1000));
- }
- }
-
- synchronized (mPendingIntents) {
- if (!mPendingIntents.isEmpty()) {
- pw.println();
- pw.println("Pending intents:");
- for (PendingIntent pi : mPendingIntents) {
- pw.println(pi.toString());
- }
- }
- }
- }
-
- public long logEvent(ConnectivityMetricsEvent event) {
- ConnectivityMetricsEvent[] events = new ConnectivityMetricsEvent[]{event};
- return logEvents(events);
- }
-
- /**
- * @param events
- *
- * Note: All events must belong to the same component.
- *
- * @return 0 on success
- * <0 if error happened
- * >0 timestamp after which new events will be accepted
- */
- public long logEvents(ConnectivityMetricsEvent[] events) {
- enforceConnectivityInternalPermission();
-
- if (events == null || events.length == 0) {
- Log.wtf(TAG, "No events passed to logEvents()");
- return -1;
- }
-
- int componentTag = events[0].componentTag;
- if (componentTag < 0 ||
- componentTag >= ConnectivityMetricsLogger.NUMBER_OF_COMPONENTS) {
- Log.wtf(TAG, "Unexpected tag: " + componentTag);
- return -1;
- }
-
- synchronized (mThrottlingCounters) {
- long currentTimeMillis = System.currentTimeMillis();
- if (currentTimeMillis > mThrottlingIntervalBoundaryMillis) {
- resetThrottlingCounters(currentTimeMillis);
- }
-
- mThrottlingCounters[componentTag] += events.length;
-
- if (mThrottlingCounters[componentTag] >
- THROTTLING_MAX_NUMBER_OF_MESSAGES_PER_COMPONENT) {
- Log.w(TAG, "Too many events from #" + componentTag +
- ". Block until " + mThrottlingIntervalBoundaryMillis);
-
- return mThrottlingIntervalBoundaryMillis;
- }
- }
-
- boolean sendPendingIntents = false;
-
- synchronized (mEvents) {
- for (ConnectivityMetricsEvent e : events) {
- if (e.componentTag != componentTag) {
- Log.wtf(TAG, "Unexpected tag: " + e.componentTag);
- return -1;
- }
-
- addEvent(e);
- }
-
- mLastEventReference += events.length;
-
- mEventCounter += events.length;
- if (mEventCounter >= EVENTS_NOTIFICATION_THRESHOLD) {
- mEventCounter = 0;
- sendPendingIntents = true;
- }
- }
-
- if (sendPendingIntents) {
- synchronized (mPendingIntents) {
- for (PendingIntent pi : mPendingIntents) {
- if (VDBG) Log.v(TAG, "Send pending intent");
- try {
- pi.send(getContext(), 0, null, null, null);
- } catch (PendingIntent.CanceledException e) {
- Log.e(TAG, "Pending intent canceled: " + pi);
- mPendingIntents.remove(pi);
- }
- }
- }
- }
-
- return 0;
- }
-
- /**
- * Retrieve events
- *
- * @param reference of the last event previously returned. The function will return
- * events following it.
- * If 0 then all events will be returned.
- * After the function call it will contain reference of the
- * last returned event.
- * @return events
- */
- public ConnectivityMetricsEvent[] getEvents(ConnectivityMetricsEvent.Reference reference) {
- enforceDumpPermission();
- long ref = reference.getValue();
- if (VDBG) Log.v(TAG, "getEvents(" + ref + ")");
-
- ConnectivityMetricsEvent[] result;
- synchronized (mEvents) {
- if (ref > mLastEventReference) {
- Log.e(TAG, "Invalid reference");
- reference.setValue(mLastEventReference);
- return null;
- }
- if (ref < mLastEventReference - mEvents.size()) {
- ref = mLastEventReference - mEvents.size();
- }
-
- int numEventsToSkip =
- mEvents.size() // Total number of events
- - (int)(mLastEventReference - ref); // Number of events to return
-
- result = new ConnectivityMetricsEvent[mEvents.size() - numEventsToSkip];
- int i = 0;
- for (ConnectivityMetricsEvent e : mEvents) {
- if (numEventsToSkip > 0) {
- numEventsToSkip--;
- } else {
- result[i++] = e;
- }
- }
-
- reference.setValue(mLastEventReference);
- }
-
- return result;
- }
-
- public boolean register(PendingIntent newEventsIntent) {
- enforceDumpPermission();
- if (VDBG) Log.v(TAG, "register(" + newEventsIntent + ")");
-
- synchronized (mPendingIntents) {
- if (mPendingIntents.remove(newEventsIntent)) {
- Log.w(TAG, "Replacing registered pending intent");
- }
- mPendingIntents.add(newEventsIntent);
- }
-
- return true;
- }
-
- public void unregister(PendingIntent newEventsIntent) {
- enforceDumpPermission();
- if (VDBG) Log.v(TAG, "unregister(" + newEventsIntent + ")");
-
- synchronized (mPendingIntents) {
- if (!mPendingIntents.remove(newEventsIntent)) {
- Log.e(TAG, "Pending intent is not registered");
- }
- }
- }
- };
-}
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index 9ffe2b7..97669d2 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -789,6 +789,8 @@
if (userAgent != null) {
urlConnection.setRequestProperty("User-Agent", userAgent);
}
+ // cannot read request header after connection
+ String requestHeader = urlConnection.getRequestProperties().toString();
// Time how long it takes to get a response to our request
long requestTimestamp = SystemClock.elapsedRealtime();
@@ -802,6 +804,7 @@
validationLog(ValidationProbeEvent.getProbeName(probeType) + " " + url +
" time=" + (responseTimestamp - requestTimestamp) + "ms" +
" ret=" + httpResponseCode +
+ " request=" + requestHeader +
" headers=" + urlConnection.getHeaderFields());
// NOTE: We may want to consider an "HTTP/1.0 204" response to be a captive
// portal. The only example of this seen so far was a captive portal. For
@@ -809,19 +812,26 @@
// portal. If it is considered a captive portal, a different sign-in URL
// is needed (i.e. can't browse a 204). This could be the result of an HTTP
// proxy server.
-
- // Consider 200 response with "Content-length=0" to not be a captive portal.
- // There's no point in considering this a captive portal as the user cannot
- // sign-in to an empty page. Probably the result of a broken transparent proxy.
- // See http://b/9972012.
- if (httpResponseCode == 200 && urlConnection.getContentLength() == 0) {
- validationLog("Empty 200 response interpreted as 204 response.");
- httpResponseCode = 204;
- }
-
- if (httpResponseCode == 200 && probeType == ValidationProbeEvent.PROBE_PAC) {
- validationLog("PAC fetch 200 response interpreted as 204 response.");
- httpResponseCode = 204;
+ if (httpResponseCode == 200) {
+ if (probeType == ValidationProbeEvent.PROBE_PAC) {
+ validationLog("PAC fetch 200 response interpreted as 204 response.");
+ httpResponseCode = 204;
+ } else if (urlConnection.getContentLengthLong() == 0) {
+ // Consider 200 response with "Content-length=0" to not be a captive portal.
+ // There's no point in considering this a captive portal as the user cannot
+ // sign-in to an empty page. Probably the result of a broken transparent proxy.
+ // See http://b/9972012.
+ validationLog(
+ "200 response with Content-length=0 interpreted as 204 response.");
+ httpResponseCode = 204;
+ } else if (urlConnection.getContentLengthLong() == -1) {
+ // When no Content-length (default value == -1), attempt to read a byte from the
+ // response. Do not use available() as it is unreliable. See http://b/33498325.
+ if (urlConnection.getInputStream().read() == -1) {
+ validationLog("Empty 200 response interpreted as 204 response.");
+ httpResponseCode = 204;
+ }
+ }
}
} catch (IOException e) {
validationLog("Probably not a portal: exception " + e);
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 63e66a2..a53d19c 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -76,8 +76,9 @@
import com.android.server.connectivity.tethering.IControlsTethering;
import com.android.server.connectivity.tethering.IPv6TetheringCoordinator;
import com.android.server.connectivity.tethering.IPv6TetheringInterfaceServices;
-import com.android.server.connectivity.tethering.TetheringConfiguration;
+import com.android.server.connectivity.tethering.OffloadController;
import com.android.server.connectivity.tethering.TetherInterfaceStateMachine;
+import com.android.server.connectivity.tethering.TetheringConfiguration;
import com.android.server.connectivity.tethering.UpstreamNetworkMonitor;
import com.android.server.net.BaseNetworkObserver;
@@ -101,8 +102,7 @@
*/
public class Tethering extends BaseNetworkObserver implements IControlsTethering {
- private final Context mContext;
- private final static String TAG = "Tethering";
+ private final static String TAG = Tethering.class.getSimpleName();
private final static boolean DBG = false;
private final static boolean VDBG = false;
@@ -114,47 +114,43 @@
private static final SparseArray<String> sMagicDecoderRing =
MessageUtils.findMessageNames(messageClasses);
- private volatile TetheringConfiguration mConfig;
+ // {@link ComponentName} of the Service used to run tether provisioning.
+ private static final ComponentName TETHER_SERVICE = ComponentName.unflattenFromString(Resources
+ .getSystem().getString(com.android.internal.R.string.config_wifi_tether_enable));
+
+ private static class TetherState {
+ public final TetherInterfaceStateMachine stateMachine;
+ public int lastState;
+ public int lastError;
+ public TetherState(TetherInterfaceStateMachine sm) {
+ stateMachine = sm;
+ // Assume all state machines start out available and with no errors.
+ lastState = IControlsTethering.STATE_AVAILABLE;
+ lastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
+ }
+ }
// used to synchronize public access to members
private final Object mPublicSync;
-
+ private final Context mContext;
+ private final ArrayMap<String, TetherState> mTetherStates;
+ private final BroadcastReceiver mStateReceiver;
private final INetworkManagementService mNMService;
private final INetworkStatsService mStatsService;
private final INetworkPolicyManager mPolicyManager;
private final Looper mLooper;
private final MockableSystemProperties mSystemProperties;
-
- private static class TetherState {
- public final TetherInterfaceStateMachine mStateMachine;
- public int mLastState;
- public int mLastError;
- public TetherState(TetherInterfaceStateMachine sm) {
- mStateMachine = sm;
- // Assume all state machines start out available and with no errors.
- mLastState = IControlsTethering.STATE_AVAILABLE;
- mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
- }
- }
- private final ArrayMap<String, TetherState> mTetherStates;
-
- private final BroadcastReceiver mStateReceiver;
-
- // {@link ComponentName} of the Service used to run tether provisioning.
- private static final ComponentName TETHER_SERVICE = ComponentName.unflattenFromString(Resources
- .getSystem().getString(com.android.internal.R.string.config_wifi_tether_enable));
-
private final StateMachine mTetherMasterSM;
+ private final OffloadController mOffloadController;
private final UpstreamNetworkMonitor mUpstreamNetworkMonitor;
- private String mCurrentUpstreamIface;
+ private volatile TetheringConfiguration mConfig;
+ private String mCurrentUpstreamIface;
private Notification.Builder mTetheredNotificationBuilder;
private int mLastNotificationId;
-
private boolean mRndisEnabled; // track the RNDIS function enabled state
private boolean mUsbTetherRequested; // true if USB tethering should be started
// when RNDIS is enabled
-
// True iff WiFi tethering should be started when soft AP is ready.
private boolean mWifiTetherRequested;
@@ -175,6 +171,7 @@
mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper);
mTetherMasterSM.start();
+ mOffloadController = new OffloadController(mTetherMasterSM.getHandler());
mUpstreamNetworkMonitor = new UpstreamNetworkMonitor(
mContext, mTetherMasterSM, TetherMasterSM.EVENT_UPSTREAM_CALLBACK);
@@ -224,7 +221,7 @@
}
} else {
if (interfaceType == ConnectivityManager.TETHERING_BLUETOOTH) {
- tetherState.mStateMachine.sendMessage(
+ tetherState.stateMachine.sendMessage(
TetherInterfaceStateMachine.CMD_INTERFACE_DOWN);
mTetherStates.remove(iface);
} else {
@@ -286,13 +283,12 @@
}
return;
}
- tetherState.mStateMachine.sendMessage(TetherInterfaceStateMachine.CMD_INTERFACE_DOWN);
+ tetherState.stateMachine.sendMessage(TetherInterfaceStateMachine.CMD_INTERFACE_DOWN);
mTetherStates.remove(iface);
}
}
- public void startTethering(int type, ResultReceiver receiver,
- boolean showProvisioningUi) {
+ public void startTethering(int type, ResultReceiver receiver, boolean showProvisioningUi) {
if (!isTetherProvisioningRequired()) {
enableTetheringInternal(type, true, receiver);
return;
@@ -524,11 +520,11 @@
}
// Ignore the error status of the interface. If the interface is available,
// the errors are referring to past tethering attempts anyway.
- if (tetherState.mLastState != IControlsTethering.STATE_AVAILABLE) {
+ if (tetherState.lastState != IControlsTethering.STATE_AVAILABLE) {
Log.e(TAG, "Tried to Tether an unavailable iface: " + iface + ", ignoring");
return ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE;
}
- tetherState.mStateMachine.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED);
+ tetherState.stateMachine.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_REQUESTED);
return ConnectivityManager.TETHER_ERROR_NO_ERROR;
}
}
@@ -541,11 +537,11 @@
Log.e(TAG, "Tried to Untether an unknown iface :" + iface + ", ignoring");
return ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
}
- if (tetherState.mLastState != IControlsTethering.STATE_TETHERED) {
+ if (tetherState.lastState != IControlsTethering.STATE_TETHERED) {
Log.e(TAG, "Tried to untether an untethered iface :" + iface + ", ignoring");
return ConnectivityManager.TETHER_ERROR_UNAVAIL_IFACE;
}
- tetherState.mStateMachine.sendMessage(
+ tetherState.stateMachine.sendMessage(
TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED);
return ConnectivityManager.TETHER_ERROR_NO_ERROR;
}
@@ -565,7 +561,7 @@
", ignoring");
return ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE;
}
- return tetherState.mLastError;
+ return tetherState.lastError;
}
}
@@ -586,11 +582,11 @@
for (int i = 0; i < mTetherStates.size(); i++) {
TetherState tetherState = mTetherStates.valueAt(i);
String iface = mTetherStates.keyAt(i);
- if (tetherState.mLastError != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+ if (tetherState.lastError != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
erroredList.add(iface);
- } else if (tetherState.mLastState == IControlsTethering.STATE_AVAILABLE) {
+ } else if (tetherState.lastState == IControlsTethering.STATE_AVAILABLE) {
availableList.add(iface);
- } else if (tetherState.mLastState == IControlsTethering.STATE_TETHERED) {
+ } else if (tetherState.lastState == IControlsTethering.STATE_TETHERED) {
if (cfg.isUsb(iface)) {
usbTethered = true;
} else if (cfg.isWifi(iface)) {
@@ -763,7 +759,7 @@
// themselves down.
for (int i = 0; i < mTetherStates.size(); i++) {
TetherInterfaceStateMachine tism =
- mTetherStates.valueAt(i).mStateMachine;
+ mTetherStates.valueAt(i).stateMachine;
if (tism.interfaceType() == ConnectivityManager.TETHERING_WIFI) {
tism.sendMessage(
TetherInterfaceStateMachine.CMD_TETHER_UNREQUESTED);
@@ -878,7 +874,7 @@
synchronized (mPublicSync) {
for (int i = 0; i < mTetherStates.size(); i++) {
TetherState tetherState = mTetherStates.valueAt(i);
- if (tetherState.mLastState == IControlsTethering.STATE_TETHERED) {
+ if (tetherState.lastState == IControlsTethering.STATE_TETHERED) {
list.add(mTetherStates.keyAt(i));
}
}
@@ -891,7 +887,7 @@
synchronized (mPublicSync) {
for (int i = 0; i < mTetherStates.size(); i++) {
TetherState tetherState = mTetherStates.valueAt(i);
- if (tetherState.mLastState == IControlsTethering.STATE_AVAILABLE) {
+ if (tetherState.lastState == IControlsTethering.STATE_AVAILABLE) {
list.add(mTetherStates.keyAt(i));
}
}
@@ -908,7 +904,7 @@
synchronized (mPublicSync) {
for (int i = 0; i < mTetherStates.size(); i++) {
TetherState tetherState = mTetherStates.valueAt(i);
- if (tetherState.mLastError != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
+ if (tetherState.lastError != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
list.add(mTetherStates.keyAt(i));
}
}
@@ -950,6 +946,8 @@
// Events from NetworkCallbacks that we process on the master state
// machine thread on behalf of the UpstreamNetworkMonitor.
static final int EVENT_UPSTREAM_CALLBACK = BASE_MASTER + 5;
+ // we treated the error and want now to clear it
+ static final int CMD_CLEAR_ERROR = BASE_MASTER + 6;
private State mInitialState;
private State mTetherModeAliveState;
@@ -1201,6 +1199,8 @@
protected void handleNewUpstreamNetworkState(NetworkState ns) {
mIPv6TetheringCoordinator.updateUpstreamNetworkState(ns);
+ mOffloadController.setUpstreamLinkProperties(
+ (ns != null) ? ns.linkProperties : null);
}
}
@@ -1311,7 +1311,7 @@
synchronized (mPublicSync) {
for (int i = 0; i < mTetherStates.size(); i++) {
TetherState tetherState = mTetherStates.valueAt(i);
- if (tetherState.mLastState != IControlsTethering.STATE_TETHERED) {
+ if (tetherState.lastState != IControlsTethering.STATE_TETHERED) {
continue; // Skip interfaces that aren't tethered.
}
String iface = mTetherStates.keyAt(i);
@@ -1362,12 +1362,14 @@
class TetherModeAliveState extends TetherMasterUtilState {
final SimChangeListener simChange = new SimChangeListener(mContext);
boolean mTryCell = true;
+
@Override
public void enter() {
// TODO: examine if we should check the return value.
turnOnMasterTetherSettings(); // may transition us out
simChange.startListening();
mUpstreamNetworkMonitor.start();
+ mOffloadController.start();
// Better try something first pass or crazy tests cases will fail.
chooseUpstreamType(true);
@@ -1376,6 +1378,7 @@
@Override
public void exit() {
+ mOffloadController.stop();
unrequestUpstreamMobileConnection();
mUpstreamNetworkMonitor.stop();
simChange.stopListening();
@@ -1496,6 +1499,10 @@
TetherInterfaceStateMachine who = (TetherInterfaceStateMachine)message.obj;
who.sendMessage(mErrorNotification);
break;
+ case CMD_CLEAR_ERROR:
+ mErrorNotification = ConnectivityManager.TETHER_ERROR_NO_ERROR;
+ transitionTo(mInitialState);
+ break;
default:
retValue = false;
}
@@ -1592,7 +1599,7 @@
final TetherState tetherState = mTetherStates.valueAt(i);
pw.print(iface + " - ");
- switch (tetherState.mLastState) {
+ switch (tetherState.lastState) {
case IControlsTethering.STATE_UNAVAILABLE:
pw.print("UnavailableState");
break;
@@ -1606,7 +1613,7 @@
pw.print("UnknownState");
break;
}
- pw.println(" - lastError = " + tetherState.mLastError);
+ pw.println(" - lastError = " + tetherState.lastError);
}
pw.decreaseIndent();
}
@@ -1618,9 +1625,9 @@
int state, int error) {
synchronized (mPublicSync) {
TetherState tetherState = mTetherStates.get(iface);
- if (tetherState != null && tetherState.mStateMachine.equals(who)) {
- tetherState.mLastState = state;
- tetherState.mLastError = error;
+ if (tetherState != null && tetherState.stateMachine.equals(who)) {
+ tetherState.lastState = state;
+ tetherState.lastError = error;
} else {
if (DBG) Log.d(TAG, "got notification from stale iface " + iface);
}
@@ -1640,6 +1647,12 @@
// Not really very much we can do here.
}
+ // If TetherMasterSM is in ErrorState, TetherMasterSM stays there.
+ // Thus we give a chance for TetherMasterSM to recover to InitialState
+ // by sending CMD_CLEAR_ERROR
+ if (error == ConnectivityManager.TETHER_ERROR_MASTER_ERROR) {
+ mTetherMasterSM.sendMessage(TetherMasterSM.CMD_CLEAR_ERROR, who);
+ }
switch (state) {
case IControlsTethering.STATE_UNAVAILABLE:
case IControlsTethering.STATE_AVAILABLE:
@@ -1658,7 +1671,7 @@
interfaceType, mNMService, mStatsService, this,
new IPv6TetheringInterfaceServices(iface, mNMService)));
mTetherStates.put(iface, tetherState);
- tetherState.mStateMachine.start();
+ tetherState.stateMachine.start();
}
private static String[] copy(String[] strarray) {
diff --git a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
new file mode 100644
index 0000000..220e751
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity.tethering;
+
+import android.net.LinkProperties;
+import android.os.Handler;
+import android.util.Log;
+
+/**
+ * A wrapper around hardware offload interface.
+ *
+ * @hide
+ */
+public class OffloadController {
+ private static final String TAG = OffloadController.class.getSimpleName();
+
+ private final Handler mHandler;
+ private LinkProperties mUpstreamLinkProperties;
+
+ public OffloadController(Handler h) {
+ mHandler = h;
+ }
+
+ public void start() {
+ // TODO: initOffload() and configure callbacks to be handled on our
+ // preferred Handler.
+ Log.d(TAG, "tethering offload not supported");
+ }
+
+ public void stop() {
+ // TODO: stopOffload().
+ mUpstreamLinkProperties = null;
+ }
+
+ public void setUpstreamLinkProperties(LinkProperties lp) {
+ // TODO: setUpstreamParameters().
+ mUpstreamLinkProperties = lp;
+ }
+}
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
index 5e51579..710ab33 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
@@ -167,7 +167,8 @@
private void maybeLogMessage(State state, int what) {
if (DBG) {
Log.d(TAG, state.getName() + " got " +
- sMagicDecoderRing.get(what, Integer.toString(what)));
+ sMagicDecoderRing.get(what, Integer.toString(what)) + ", Iface = " +
+ mIfaceName);
}
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
index 017c5fb..6209929 100644
--- a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
@@ -20,6 +20,8 @@
import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.LinkProperties;
@@ -72,6 +74,7 @@
private final Context mContext;
private final StateMachine mTarget;
+ private final Handler mHandler;
private final int mWhat;
private final HashMap<Network, NetworkState> mNetworkMap = new HashMap<>();
private ConnectivityManager mCM;
@@ -84,6 +87,7 @@
public UpstreamNetworkMonitor(Context ctx, StateMachine tgt, int what) {
mContext = ctx;
mTarget = tgt;
+ mHandler = mTarget.getHandler();
mWhat = what;
}
@@ -99,10 +103,10 @@
final NetworkRequest listenAllRequest = new NetworkRequest.Builder()
.clearCapabilities().build();
mListenAllCallback = new UpstreamNetworkCallback(CALLBACK_LISTEN_ALL);
- cm().registerNetworkCallback(listenAllRequest, mListenAllCallback);
+ cm().registerNetworkCallback(listenAllRequest, mListenAllCallback, mHandler);
mDefaultNetworkCallback = new UpstreamNetworkCallback(CALLBACK_TRACK_DEFAULT);
- cm().registerDefaultNetworkCallback(mDefaultNetworkCallback);
+ cm().registerDefaultNetworkCallback(mDefaultNetworkCallback, mHandler);
}
public void stop() {
@@ -154,7 +158,7 @@
// Additionally, we log a message to aid in any subsequent debugging.
Log.d(TAG, "requesting mobile upstream network: " + mobileUpstreamRequest);
- cm().requestNetwork(mobileUpstreamRequest, mMobileNetworkCallback, 0, legacyType);
+ cm().requestNetwork(mobileUpstreamRequest, mMobileNetworkCallback, 0, legacyType, mHandler);
}
public void releaseMobileNetworkRequest() {
@@ -182,14 +186,32 @@
switch (callbackType) {
case CALLBACK_LISTEN_ALL:
break;
+
case CALLBACK_TRACK_DEFAULT:
- cm().requestNetworkCapabilities(mDefaultNetworkCallback);
- cm().requestLinkProperties(mDefaultNetworkCallback);
+ if (mDefaultNetworkCallback == null) {
+ // The callback was unregistered in the interval between
+ // ConnectivityService enqueueing onAvailable() and our
+ // handling of it here on the mHandler thread.
+ //
+ // Clean-up of this network entry is deferred to the
+ // handling of onLost() by other callbacks.
+ //
+ // These request*() calls can be deleted post oag/339444.
+ return;
+ }
mCurrentDefault = network;
break;
+
case CALLBACK_MOBILE_REQUEST:
- cm().requestNetworkCapabilities(mMobileNetworkCallback);
- cm().requestLinkProperties(mMobileNetworkCallback);
+ if (mMobileNetworkCallback == null) {
+ // The callback was unregistered in the interval between
+ // ConnectivityService enqueueing onAvailable() and our
+ // handling of it here on the mHandler thread.
+ //
+ // Clean-up of this network entry is deferred to the
+ // handling of onLost() by other callbacks.
+ return;
+ }
break;
}
@@ -292,8 +314,9 @@
}
/**
- * A NetworkCallback class that relays information of interest to the
- * tethering master state machine thread for subsequent processing.
+ * A NetworkCallback class that handles information of interest directly
+ * in the thread on which it is invoked. To avoid locking, this MUST be
+ * run on the same thread as the target state machine's handler.
*/
private class UpstreamNetworkCallback extends NetworkCallback {
private final int mCallbackType;
@@ -304,22 +327,35 @@
@Override
public void onAvailable(Network network) {
- mTarget.getHandler().post(() -> handleAvailable(mCallbackType, network));
+ checkExpectedThread();
+ handleAvailable(mCallbackType, network);
}
@Override
public void onCapabilitiesChanged(Network network, NetworkCapabilities newNc) {
- mTarget.getHandler().post(() -> handleNetCap(network, newNc));
+ checkExpectedThread();
+ handleNetCap(network, newNc);
}
@Override
public void onLinkPropertiesChanged(Network network, LinkProperties newLp) {
- mTarget.getHandler().post(() -> handleLinkProp(network, newLp));
+ checkExpectedThread();
+ handleLinkProp(network, newLp);
}
+ // TODO: Handle onNetworkSuspended();
+ // TODO: Handle onNetworkResumed();
+
@Override
public void onLost(Network network) {
- mTarget.getHandler().post(() -> handleLost(mCallbackType, network));
+ checkExpectedThread();
+ handleLost(mCallbackType, network);
+ }
+
+ private void checkExpectedThread() {
+ if (Looper.myLooper() != mHandler.getLooper()) {
+ Log.wtf(TAG, "Handling callback in unexpected thread.");
+ }
}
}
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index ae98077..e2e834d 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -477,12 +477,6 @@
public void onLost(Network network) {
releaseSuplConnection(GPS_RELEASE_AGPS_DATA_CONN);
}
-
- @Override
- public void onUnavailable() {
- // timeout, it was not possible to establish the required connection
- releaseSuplConnection(GPS_AGPS_DATA_CONN_FAILED);
- }
};
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@@ -902,8 +896,7 @@
NetworkRequest request = requestBuilder.build();
mConnMgr.requestNetwork(
request,
- mSuplConnectivityCallback,
- ConnectivityManager.MAX_NETWORK_REQUEST_TIMEOUT_MS);
+ mSuplConnectivityCallback);
}
private void handleReleaseSuplConnection(int agpsDataConnStatus) {
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 601a219..7aa96cf 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -30,10 +30,13 @@
import android.os.BatteryManager;
import android.os.Environment;
import android.os.ServiceManager;
+import android.os.SystemProperties;
import android.os.storage.StorageManager;
import android.util.ArraySet;
import android.util.Log;
+import com.android.server.pm.dex.DexManager;
+
import java.io.File;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.TimeUnit;
@@ -59,21 +62,33 @@
"android",
BackgroundDexOptService.class.getName());
+ // Possible return codes of individual optimization steps.
+
+ // Optimizations finished. All packages were processed.
+ private static final int OPTIMIZE_PROCESSED = 0;
+ // Optimizations should continue. Issued after checking the scheduler, disk space or battery.
+ private static final int OPTIMIZE_CONTINUE = 1;
+ // Optimizations should be aborted. Job scheduler requested it.
+ private static final int OPTIMIZE_ABORT_BY_JOB_SCHEDULER = 2;
+ // Optimizations should be aborted. No space left on device.
+ private static final int OPTIMIZE_ABORT_NO_SPACE_LEFT = 3;
+
/**
* Set of failed packages remembered across job runs.
*/
- static final ArraySet<String> sFailedPackageNames = new ArraySet<String>();
+ static final ArraySet<String> sFailedPackageNamesPrimary = new ArraySet<String>();
+ static final ArraySet<String> sFailedPackageNamesSecondary = new ArraySet<String>();
/**
* Atomics set to true if the JobScheduler requests an abort.
*/
- final AtomicBoolean mAbortPostBootUpdate = new AtomicBoolean(false);
- final AtomicBoolean mAbortIdleOptimization = new AtomicBoolean(false);
+ private final AtomicBoolean mAbortPostBootUpdate = new AtomicBoolean(false);
+ private final AtomicBoolean mAbortIdleOptimization = new AtomicBoolean(false);
/**
* Atomic set to true if one job should exit early because another job was started.
*/
- final AtomicBoolean mExitPostBootUpdate = new AtomicBoolean(false);
+ private final AtomicBoolean mExitPostBootUpdate = new AtomicBoolean(false);
private final File mDataDir = Environment.getDataDirectory();
@@ -104,8 +119,11 @@
// The idle maintanance job skips packages which previously failed to
// compile. The given package has changed and may successfully compile
// now. Remove it from the list of known failing packages.
- synchronized (sFailedPackageNames) {
- sFailedPackageNames.remove(packageName);
+ synchronized (sFailedPackageNamesPrimary) {
+ sFailedPackageNamesPrimary.remove(packageName);
+ }
+ synchronized (sFailedPackageNamesSecondary) {
+ sFailedPackageNamesSecondary.remove(packageName);
}
}
@@ -124,9 +142,9 @@
return (100 * level / scale);
}
- private long getLowStorageThreshold() {
+ private long getLowStorageThreshold(Context context) {
@SuppressWarnings("deprecation")
- final long lowThreshold = StorageManager.from(this).getStorageLowBytes(mDataDir);
+ final long lowThreshold = StorageManager.from(context).getStorageLowBytes(mDataDir);
if (lowThreshold == 0) {
Log.e(TAG, "Invalid low storage threshold");
}
@@ -155,7 +173,7 @@
// Load low battery threshold from the system config. This is a 0-100 integer.
final int lowBatteryThreshold = getResources().getInteger(
com.android.internal.R.integer.config_lowBatteryWarningLevel);
- final long lowThreshold = getLowStorageThreshold();
+ final long lowThreshold = getLowStorageThreshold(this);
mAbortPostBootUpdate.set(false);
@@ -206,61 +224,123 @@
new Thread("BackgroundDexOptService_IdleOptimization") {
@Override
public void run() {
- idleOptimization(jobParams, pm, pkgs);
+ int result = idleOptimization(pm, pkgs, BackgroundDexOptService.this);
+ if (result != OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
+ Log.w(TAG, "Idle optimizations aborted because of space constraints.");
+ // If we didn't abort we ran to completion (or stopped because of space).
+ // Abandon our timeslice and do not reschedule.
+ jobFinished(jobParams, /* reschedule */ false);
+ }
}
}.start();
return true;
}
- private void idleOptimization(JobParameters jobParams, PackageManagerService pm,
- ArraySet<String> pkgs) {
+ // Optimize the given packages and return the optimization result (one of the OPTIMIZE_* codes).
+ private int idleOptimization(PackageManagerService pm, ArraySet<String> pkgs, Context context) {
Log.i(TAG, "Performing idle optimizations");
// If post-boot update is still running, request that it exits early.
mExitPostBootUpdate.set(true);
-
mAbortIdleOptimization.set(false);
- final long lowThreshold = getLowStorageThreshold();
- for (String pkg : pkgs) {
- if (mAbortIdleOptimization.get()) {
- // JobScheduler requested an early abort.
- return;
+ long lowStorageThreshold = getLowStorageThreshold(context);
+ // Optimize primary apks.
+ int result = optimizePackages(pm, pkgs, lowStorageThreshold, /*is_for_primary_dex*/ true,
+ sFailedPackageNamesPrimary);
+
+ if (result == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
+ return result;
+ }
+
+ if (SystemProperties.getBoolean("dalvik.vm.dexopt.secondary", false)) {
+ result = reconcileSecondaryDexFiles(pm.getDexManager());
+ if (result == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
+ return result;
}
- synchronized (sFailedPackageNames) {
- if (sFailedPackageNames.contains(pkg)) {
+ result = optimizePackages(pm, pkgs, lowStorageThreshold, /*is_for_primary_dex*/ false,
+ sFailedPackageNamesSecondary);
+ }
+ return result;
+ }
+
+ private int optimizePackages(PackageManagerService pm, ArraySet<String> pkgs,
+ long lowStorageThreshold, boolean is_for_primary_dex,
+ ArraySet<String> failedPackageNames) {
+ for (String pkg : pkgs) {
+ int abort_code = abortIdleOptimizations(lowStorageThreshold);
+ if (abort_code != OPTIMIZE_CONTINUE) {
+ return abort_code;
+ }
+
+ synchronized (failedPackageNames) {
+ if (failedPackageNames.contains(pkg)) {
// Skip previously failing package
continue;
+ } else {
+ // Conservatively add package to the list of failing ones in case performDexOpt
+ // never returns.
+ failedPackageNames.add(pkg);
}
}
- long usableSpace = mDataDir.getUsableSpace();
- if (usableSpace < lowThreshold) {
- // Rather bail than completely fill up the disk.
- Log.w(TAG, "Aborting background dex opt job due to low storage: " +
- usableSpace);
- break;
- }
-
- // Conservatively add package to the list of failing ones in case performDexOpt
- // never returns.
- synchronized (sFailedPackageNames) {
- sFailedPackageNames.add(pkg);
- }
// Optimize package if needed. Note that there can be no race between
// concurrent jobs because PackageDexOptimizer.performDexOpt is synchronized.
- if (pm.performDexOpt(pkg,
- /* checkProfiles */ true,
- PackageManagerService.REASON_BACKGROUND_DEXOPT,
- /* force */ false)) {
+ boolean success = is_for_primary_dex
+ ? pm.performDexOpt(pkg,
+ /* checkProfiles */ true,
+ PackageManagerService.REASON_BACKGROUND_DEXOPT,
+ /* force */ false)
+ : pm.performDexOptSecondary(pkg,
+ PackageManagerServiceCompilerMapping.getFullCompilerFilter(),
+ /* force */ true);
+ if (success) {
// Dexopt succeeded, remove package from the list of failing ones.
- synchronized (sFailedPackageNames) {
- sFailedPackageNames.remove(pkg);
+ synchronized (failedPackageNames) {
+ failedPackageNames.remove(pkg);
}
}
}
- // Ran to completion, so we abandon our timeslice and do not reschedule.
- jobFinished(jobParams, /* reschedule */ false);
+ return OPTIMIZE_PROCESSED;
+ }
+
+ private int reconcileSecondaryDexFiles(DexManager dm) {
+ // TODO(calin): should we blacklist packages for which we fail to reconcile?
+ for (String p : dm.getAllPackagesWithSecondaryDexFiles()) {
+ if (mAbortIdleOptimization.get()) {
+ return OPTIMIZE_ABORT_BY_JOB_SCHEDULER;
+ }
+ dm.reconcileSecondaryDexFiles(p);
+ }
+ return OPTIMIZE_PROCESSED;
+ }
+
+ // Evaluate whether or not idle optimizations should continue.
+ private int abortIdleOptimizations(long lowStorageThreshold) {
+ if (mAbortIdleOptimization.get()) {
+ // JobScheduler requested an early abort.
+ return OPTIMIZE_ABORT_BY_JOB_SCHEDULER;
+ }
+ long usableSpace = mDataDir.getUsableSpace();
+ if (usableSpace < lowStorageThreshold) {
+ // Rather bail than completely fill up the disk.
+ Log.w(TAG, "Aborting background dex opt job due to low storage: " + usableSpace);
+ return OPTIMIZE_ABORT_NO_SPACE_LEFT;
+ }
+
+ return OPTIMIZE_CONTINUE;
+ }
+
+ /**
+ * Execute the idle optimizations immediately.
+ */
+ public static boolean runIdleOptimizationsNow(PackageManagerService pm, Context context) {
+ // Create a new object to make sure we don't interfere with the scheduled jobs.
+ // Note that this may still run at the same time with the job scheduled by the
+ // JobScheduler but the scheduler will not be able to cancel it.
+ BackgroundDexOptService bdos = new BackgroundDexOptService();
+ int result = bdos.idleOptimization(pm, pm.getOptimizablePackages(), context);
+ return result == OPTIMIZE_PROCESSED;
}
@Override
@@ -281,7 +361,7 @@
}
final ArraySet<String> pkgs = pm.getOptimizablePackages();
- if (pkgs == null || pkgs.isEmpty()) {
+ if (pkgs.isEmpty()) {
if (DEBUG_DEXOPT) {
Log.i(TAG, "No packages to optimize");
}
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 04569c2..42a0bad 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -50,6 +50,14 @@
public static final int DEXOPT_BOOTCOMPLETE = 1 << 4;
/** Hint that the dexopt type is profile-guided. */
public static final int DEXOPT_PROFILE_GUIDED = 1 << 5;
+ /** The compilation is for a secondary dex file. */
+ public static final int DEXOPT_SECONDARY_DEX = 1 << 6;
+ /** Ignore the result of dexoptNeeded and force compilation. */
+ public static final int DEXOPT_FORCE = 1 << 7;
+ /** Indicates that the dex file passed to dexopt in on CE storage. */
+ public static final int DEXOPT_STORAGE_CE = 1 << 8;
+ /** Indicates that the dex file passed to dexopt in on DE storage. */
+ public static final int DEXOPT_STORAGE_DE = 1 << 9;
// NOTE: keep in sync with installd
public static final int FLAG_CLEAR_CACHE_ONLY = 1 << 8;
@@ -425,6 +433,20 @@
}
}
+ public boolean reconcileSecondaryDexFile(String apkPath, String packageName, int uid,
+ String[] isas, @Nullable String volumeUuid, int flags) throws InstallerException {
+ for (int i = 0; i < isas.length; i++) {
+ assertValidInstructionSet(isas[i]);
+ }
+ if (!checkBeforeRemote()) return false;
+ try {
+ return mInstalld.reconcileSecondaryDexFile(apkPath, packageName, uid, isas,
+ volumeUuid, flags);
+ } catch (Exception e) {
+ throw InstallerException.from(e);
+ }
+ }
+
private static void assertValidInstructionSet(String instructionSet)
throws InstallerException {
for (String abi : Build.SUPPORTED_ABIS) {
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index 60c83b4..9418e74 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -313,7 +313,8 @@
optimizer.performDexOpt(pkg, libraryDependencies,
null /* ISAs */, false /* checkProfiles */,
getCompilerFilterForReason(compilationReason),
- null /* CompilerStats.PackageStats */);
+ null /* CompilerStats.PackageStats */,
+ mPackageManagerService.getDexManager().isUsedByOtherApps(pkg.packageName));
return commands;
}
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 8e201ac..d9ea728 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -19,6 +19,7 @@
import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageParser;
import android.os.Environment;
import android.os.PowerManager;
@@ -35,6 +36,7 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
import dalvik.system.DexFile;
@@ -43,6 +45,10 @@
import static com.android.server.pm.Installer.DEXOPT_PROFILE_GUIDED;
import static com.android.server.pm.Installer.DEXOPT_PUBLIC;
import static com.android.server.pm.Installer.DEXOPT_SAFEMODE;
+import static com.android.server.pm.Installer.DEXOPT_SECONDARY_DEX;
+import static com.android.server.pm.Installer.DEXOPT_FORCE;
+import static com.android.server.pm.Installer.DEXOPT_STORAGE_CE;
+import static com.android.server.pm.Installer.DEXOPT_STORAGE_DE;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
@@ -52,13 +58,16 @@
/**
* Helper class for running dexopt command on packages.
*/
-class PackageDexOptimizer {
+public class PackageDexOptimizer {
private static final String TAG = "PackageManager.DexOptimizer";
static final String OAT_DIR_NAME = "oat";
// TODO b/19550105 Remove error codes and use exceptions
- static final int DEX_OPT_SKIPPED = 0;
- static final int DEX_OPT_PERFORMED = 1;
- static final int DEX_OPT_FAILED = -1;
+ public static final int DEX_OPT_SKIPPED = 0;
+ public static final int DEX_OPT_PERFORMED = 1;
+ public static final int DEX_OPT_FAILED = -1;
+
+ /** Special library name that skips shared libraries check during compilation. */
+ public static final String SKIP_SHARED_LIBRARY_CHECK = "&";
private final Installer mInstaller;
private final Object mInstallLock;
@@ -95,11 +104,14 @@
*/
int performDexOpt(PackageParser.Package pkg, String[] sharedLibraries,
String[] instructionSets, boolean checkProfiles, String targetCompilationFilter,
- CompilerStats.PackageStats packageStats) {
+ CompilerStats.PackageStats packageStats, boolean isUsedByOtherApps) {
if (!canOptimizePackage(pkg)) {
return DEX_OPT_SKIPPED;
}
synchronized (mInstallLock) {
+ // During boot the system doesn't need to instantiate and obtain a wake lock.
+ // PowerManager might not be ready, but that doesn't mean that we can't proceed with
+ // dexopt.
final boolean useLock = mSystemReady;
if (useLock) {
mDexoptWakeLock.setWorkSource(new WorkSource(pkg.applicationInfo.uid));
@@ -107,7 +119,7 @@
}
try {
return performDexOptLI(pkg, sharedLibraries, instructionSets, checkProfiles,
- targetCompilationFilter, packageStats);
+ targetCompilationFilter, packageStats, isUsedByOtherApps);
} finally {
if (useLock) {
mDexoptWakeLock.release();
@@ -123,16 +135,19 @@
@GuardedBy("mInstallLock")
private int performDexOptLI(PackageParser.Package pkg, String[] sharedLibraries,
String[] targetInstructionSets, boolean checkForProfileUpdates,
- String targetCompilerFilter, CompilerStats.PackageStats packageStats) {
+ String targetCompilerFilter, CompilerStats.PackageStats packageStats,
+ boolean isUsedByOtherApps) {
final String[] instructionSets = targetInstructionSets != null ?
targetInstructionSets : getAppDexInstructionSets(pkg.applicationInfo);
final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
final List<String> paths = pkg.getAllCodePathsExcludingResourceOnly();
final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
- final String compilerFilter = getRealCompilerFilter(pkg, targetCompilerFilter);
+ final String compilerFilter = getRealCompilerFilter(pkg.applicationInfo,
+ targetCompilerFilter, isUsedByOtherApps);
final boolean profileUpdated = checkForProfileUpdates &&
isProfileUpdated(pkg, sharedGid, compilerFilter);
+
// TODO(calin,jeffhao): shared library paths should be adjusted to include previous code
// paths (b/34169257).
final String sharedLibrariesPath = getSharedLibrariesPath(sharedLibraries);
@@ -201,6 +216,79 @@
}
/**
+ * Performs dexopt on the secondary dex {@code path} belonging to the app {@code info}.
+ *
+ * @return
+ * DEX_OPT_FAILED if there was any exception during dexopt
+ * DEX_OPT_PERFORMED if dexopt was performed successfully on the given path.
+ * NOTE that DEX_OPT_PERFORMED for secondary dex files includes the case when the dex file
+ * didn't need an update. That's because at the moment we don't get more than success/failure
+ * from installd.
+ *
+ * TODO(calin): Consider adding return codes to installd dexopt invocation (rather than
+ * throwing exceptions). Or maybe make a separate call to installd to get DexOptNeeded, though
+ * that seems wasteful.
+ */
+ public int dexOptSecondaryDexPath(ApplicationInfo info, String path, Set<String> isas,
+ String compilerFilter, boolean isUsedByOtherApps) {
+ synchronized (mInstallLock) {
+ // During boot the system doesn't need to instantiate and obtain a wake lock.
+ // PowerManager might not be ready, but that doesn't mean that we can't proceed with
+ // dexopt.
+ final boolean useLock = mSystemReady;
+ if (useLock) {
+ mDexoptWakeLock.setWorkSource(new WorkSource(info.uid));
+ mDexoptWakeLock.acquire();
+ }
+ try {
+ return dexOptSecondaryDexPathLI(info, path, isas, compilerFilter,
+ isUsedByOtherApps);
+ } finally {
+ if (useLock) {
+ mDexoptWakeLock.release();
+ }
+ }
+ }
+ }
+
+ @GuardedBy("mInstallLock")
+ private int dexOptSecondaryDexPathLI(ApplicationInfo info, String path, Set<String> isas,
+ String compilerFilter, boolean isUsedByOtherApps) {
+ int dexoptFlags = getDexFlags(info, compilerFilter) | DEXOPT_SECONDARY_DEX;
+ // Check the app storage and add the appropriate flags.
+ if (info.dataDir.equals(info.deviceProtectedDataDir)) {
+ dexoptFlags |= DEXOPT_STORAGE_DE;
+ } else if (info.dataDir.equals(info.credentialProtectedDataDir)) {
+ dexoptFlags |= DEXOPT_STORAGE_CE;
+ } else {
+ Slog.e(TAG, "Could not infer CE/DE storage for package " + info.packageName);
+ return DEX_OPT_FAILED;
+ }
+ compilerFilter = getRealCompilerFilter(info, compilerFilter, isUsedByOtherApps);
+ Log.d(TAG, "Running dexopt on: " + path
+ + " pkg=" + info.packageName + " isa=" + isas
+ + " dexoptFlags=" + printDexoptFlags(dexoptFlags)
+ + " target-filter=" + compilerFilter);
+
+ try {
+ for (String isa : isas) {
+ // Reuse the same dexopt path as for the primary apks. We don't need all the
+ // arguments as some (dexopNeeded and oatDir) will be computed by installd because
+ // system server cannot read untrusted app content.
+ // TODO(calin): maybe add a separate call.
+ mInstaller.dexopt(path, info.uid, info.packageName, isa, /*dexoptNeeded*/ 0,
+ /*oatDir*/ null, dexoptFlags,
+ compilerFilter, info.volumeUuid, SKIP_SHARED_LIBRARY_CHECK);
+ }
+
+ return DEX_OPT_PERFORMED;
+ } catch (InstallerException e) {
+ Slog.w(TAG, "Failed to dexopt", e);
+ return DEX_OPT_FAILED;
+ }
+ }
+
+ /**
* Adjust the given dexopt-needed value. Can be overridden to influence the decision to
* optimize or not (and in what way).
*/
@@ -246,8 +334,9 @@
* The target filter will be updated if the package code is used by other apps
* or if it has the safe mode flag set.
*/
- private String getRealCompilerFilter(PackageParser.Package pkg, String targetCompilerFilter) {
- int flags = pkg.applicationInfo.flags;
+ private String getRealCompilerFilter(ApplicationInfo info, String targetCompilerFilter,
+ boolean isUsedByOtherApps) {
+ int flags = info.flags;
boolean vmSafeMode = (flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0;
if (vmSafeMode) {
// For the compilation, it doesn't really matter what we return here because installd
@@ -259,7 +348,7 @@
return getNonProfileGuidedCompilerFilter(targetCompilerFilter);
}
- if (isProfileGuidedCompilerFilter(targetCompilerFilter) && isUsedByOtherApps(pkg)) {
+ if (isProfileGuidedCompilerFilter(targetCompilerFilter) && isUsedByOtherApps) {
// If the dex files is used by other apps, we cannot use profile-guided compilation.
return getNonProfileGuidedCompilerFilter(targetCompilerFilter);
}
@@ -272,12 +361,16 @@
* filter.
*/
private int getDexFlags(PackageParser.Package pkg, String compilerFilter) {
- int flags = pkg.applicationInfo.flags;
+ return getDexFlags(pkg.applicationInfo, compilerFilter);
+ }
+
+ private int getDexFlags(ApplicationInfo info, String compilerFilter) {
+ int flags = info.flags;
boolean vmSafeMode = (flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0;
boolean debuggable = (flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
// Profile guide compiled oat files should not be public.
boolean isProfileGuidedFilter = isProfileGuidedCompilerFilter(compilerFilter);
- boolean isPublic = !pkg.isForwardLocked() && !isProfileGuidedFilter;
+ boolean isPublic = !info.isForwardLocked() && !isProfileGuidedFilter;
int profileFlag = isProfileGuidedFilter ? DEXOPT_PROFILE_GUIDED : 0;
int dexFlags =
(isPublic ? DEXOPT_PUBLIC : 0)
@@ -385,40 +478,6 @@
mSystemReady = true;
}
- /**
- * Returns true if the profiling data collected for the given app indicate
- * that the apps's APK has been loaded by another app.
- * Note that this returns false for all forward-locked apps and apps without
- * any collected profiling data.
- */
- public static boolean isUsedByOtherApps(PackageParser.Package pkg) {
- if (pkg.isForwardLocked()) {
- // Skip the check for forward locked packages since they don't share their code.
- return false;
- }
-
- for (String apkPath : pkg.getAllCodePathsExcludingResourceOnly()) {
- try {
- apkPath = PackageManagerServiceUtils.realpath(new File(apkPath));
- } catch (IOException e) {
- // Log an error but continue without it.
- Slog.w(TAG, "Failed to get canonical path", e);
- continue;
- }
- String useMarker = apkPath.replace('/', '@');
- final int[] currentUserIds = UserManagerService.getInstance().getUserIds();
- for (int i = 0; i < currentUserIds.length; i++) {
- File profileDir =
- Environment.getDataProfilesDeForeignDexDirectory(currentUserIds[i]);
- File foreignUseMark = new File(profileDir, useMarker);
- if (foreignUseMark.exists()) {
- return true;
- }
- }
- }
- return false;
- }
-
private String printDexoptFlags(int flags) {
ArrayList<String> flagsList = new ArrayList<>();
@@ -437,6 +496,19 @@
if ((flags & DEXOPT_SAFEMODE) == DEXOPT_SAFEMODE) {
flagsList.add("safemode");
}
+ if ((flags & DEXOPT_SECONDARY_DEX) == DEXOPT_SECONDARY_DEX) {
+ flagsList.add("secondary");
+ }
+ if ((flags & DEXOPT_FORCE) == DEXOPT_FORCE) {
+ flagsList.add("force");
+ }
+ if ((flags & DEXOPT_STORAGE_CE) == DEXOPT_STORAGE_CE) {
+ flagsList.add("storage_ce");
+ }
+ if ((flags & DEXOPT_STORAGE_DE) == DEXOPT_STORAGE_DE) {
+ flagsList.add("storage_de");
+ }
+
return String.join(",", flagsList);
}
@@ -461,5 +533,12 @@
// TODO: The return value is wrong when patchoat is needed.
return DexFile.DEX2OAT_FROM_SCRATCH;
}
+
+ @Override
+ protected int adjustDexoptFlags(int flags) {
+ // Add DEXOPT_FORCE flag to signal installd that it should force compilation
+ // and discard dexoptanalyzer result.
+ return flags | DEXOPT_FORCE;
+ }
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ca0f85d..9452219 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -524,9 +524,6 @@
public static final int REASON_LAST = REASON_CORE_APP;
- /** Special library name that skips shared libraries check during compilation. */
- private static final String SKIP_SHARED_LIBRARY_CHECK = "&";
-
final ServiceThread mHandlerThread;
final PackageHandler mHandler;
@@ -1787,6 +1784,18 @@
res.removedInfo.args.doPostDeleteLI(true);
}
}
+
+ if (!isEphemeral(res.pkg)) {
+ // Notify DexManager that the package was installed for new users.
+ // The updated users should already be indexed and the package code paths
+ // should not change.
+ // Don't notify the manager for ephemeral apps as they are not expected to
+ // survive long enough to benefit of background optimizations.
+ for (int userId : firstUsers) {
+ PackageInfo info = getPackageInfo(packageName, /*flags*/ 0, userId);
+ mDexManager.notifyPackageInstalled(info, userId);
+ }
+ }
}
// If someone is watching installs - notify them
@@ -2121,7 +2130,7 @@
mInstaller = installer;
mPackageDexOptimizer = new PackageDexOptimizer(installer, mInstallLock, context,
"*dexopt*");
- mDexManager = new DexManager();
+ mDexManager = new DexManager(this, mPackageDexOptimizer, installer, mInstallLock);
mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());
mOnPermissionChangeListeners = new OnPermissionChangeListeners(
@@ -2248,7 +2257,7 @@
DEXOPT_PUBLIC,
getCompilerFilterForReason(REASON_SHARED_APK),
StorageManager.UUID_PRIVATE_INTERNAL,
- SKIP_SHARED_LIBRARY_CHECK);
+ PackageDexOptimizer.SKIP_SHARED_LIBRARY_CHECK);
}
} catch (FileNotFoundException e) {
Slog.w(TAG, "Library not found: " + lib);
@@ -7489,11 +7498,46 @@
pdo.performDexOpt(depPackage, null /* sharedLibraries */, instructionSets,
false /* checkProfiles */,
getCompilerFilterForReason(REASON_NON_SYSTEM_LIBRARY),
- getOrCreateCompilerPackageStats(depPackage));
+ getOrCreateCompilerPackageStats(depPackage),
+ mDexManager.isUsedByOtherApps(p.packageName));
}
}
return pdo.performDexOpt(p, p.usesLibraryFiles, instructionSets, checkProfiles,
- targetCompilerFilter, getOrCreateCompilerPackageStats(p));
+ targetCompilerFilter, getOrCreateCompilerPackageStats(p),
+ mDexManager.isUsedByOtherApps(p.packageName));
+ }
+
+ // Performs dexopt on the used secondary dex files belonging to the given package.
+ // Returns true if all dex files were process successfully (which could mean either dexopt or
+ // skip). Returns false if any of the files caused errors.
+ @Override
+ public boolean performDexOptSecondary(String packageName, String compilerFilter,
+ boolean force) {
+ return mDexManager.dexoptSecondaryDex(packageName, compilerFilter, force);
+ }
+
+ /**
+ * Reconcile the information we have about the secondary dex files belonging to
+ * {@code packagName} and the actual dex files. For all dex files that were
+ * deleted, update the internal records and delete the generated oat files.
+ */
+ @Override
+ public void reconcileSecondaryDexFiles(String packageName) {
+ mDexManager.reconcileSecondaryDexFiles(packageName);
+ }
+
+ // TODO(calin): this is only needed for BackgroundDexOptService. Find a cleaner way to inject
+ // a reference there.
+ /*package*/ DexManager getDexManager() {
+ return mDexManager;
+ }
+
+ /**
+ * Execute the background dexopt job immediately.
+ */
+ @Override
+ public boolean runBackgroundDexoptJob() {
+ return BackgroundDexOptService.runIdleOptimizationsNow(this, mContext);
}
Collection<PackageParser.Package> findSharedNonSystemLibraries(PackageParser.Package p) {
@@ -7703,60 +7747,9 @@
return;
}
destroyAppProfilesLeafLIF(pkg);
- destroyAppReferenceProfileLeafLIF(pkg, userId, true /* removeBaseMarker */);
final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
destroyAppProfilesLeafLIF(pkg.childPackages.get(i));
- destroyAppReferenceProfileLeafLIF(pkg.childPackages.get(i), userId,
- true /* removeBaseMarker */);
- }
- }
-
- private void destroyAppReferenceProfileLeafLIF(PackageParser.Package pkg, int userId,
- boolean removeBaseMarker) {
- if (pkg.isForwardLocked()) {
- return;
- }
-
- for (String path : pkg.getAllCodePathsExcludingResourceOnly()) {
- try {
- path = PackageManagerServiceUtils.realpath(new File(path));
- } catch (IOException e) {
- // TODO: Should we return early here ?
- Slog.w(TAG, "Failed to get canonical path", e);
- continue;
- }
-
- final String useMarker = path.replace('/', '@');
- for (int realUserId : resolveUserIds(userId)) {
- File profileDir = Environment.getDataProfilesDeForeignDexDirectory(realUserId);
- if (removeBaseMarker) {
- File foreignUseMark = new File(profileDir, useMarker);
- if (foreignUseMark.exists()) {
- if (!foreignUseMark.delete()) {
- Slog.w(TAG, "Unable to delete foreign user mark for package: "
- + pkg.packageName);
- }
- }
- }
-
- File[] markers = profileDir.listFiles();
- if (markers != null) {
- final String searchString = "@" + pkg.packageName + "@";
- // We also delete all markers that contain the package name we're
- // uninstalling. These are associated with secondary dex-files belonging
- // to the package. Reconstructing the path of these dex files is messy
- // in general.
- for (File marker : markers) {
- if (marker.getName().indexOf(searchString) > 0) {
- if (!marker.delete()) {
- Slog.w(TAG, "Unable to delete foreign user mark for package: "
- + pkg.packageName);
- }
- }
- }
- }
- }
}
}
@@ -7774,10 +7767,6 @@
return;
}
clearAppProfilesLeafLIF(pkg);
- // We don't remove the base foreign use marker when clearing profiles because
- // we will rename it when the app is updated. Unlike the actual profile contents,
- // the foreign use marker is good across installs.
- destroyAppReferenceProfileLeafLIF(pkg, userId, false /* removeBaseMarker */);
final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
clearAppProfilesLeafLIF(pkg.childPackages.get(i));
@@ -8657,14 +8646,6 @@
synchronized (mPackages) {
// We don't expect installation to fail beyond this point
- if (pkgSetting.pkg != null) {
- // Note that |user| might be null during the initial boot scan. If a codePath
- // for an app has changed during a boot scan, it's due to an app update that's
- // part of the system partition and marker changes must be applied to all users.
- maybeRenameForeignDexMarkers(pkgSetting.pkg, pkg,
- (user != null) ? user : UserHandle.ALL);
- }
-
// Add the new setting to mSettings
mSettings.insertPackageSettingLPw(pkgSetting, pkg);
// Add the new setting to mPackages
@@ -9029,74 +9010,6 @@
return pkg;
}
- private void maybeRenameForeignDexMarkers(PackageParser.Package existing,
- PackageParser.Package update, UserHandle user) {
- if (existing.applicationInfo == null || update.applicationInfo == null) {
- // This isn't due to an app installation.
- return;
- }
-
- final File oldCodePath = new File(existing.applicationInfo.getCodePath());
- final File newCodePath = new File(update.applicationInfo.getCodePath());
-
- // The codePath hasn't changed, so there's nothing for us to do.
- if (Objects.equals(oldCodePath, newCodePath)) {
- return;
- }
-
- File canonicalNewCodePath;
- try {
- canonicalNewCodePath = new File(PackageManagerServiceUtils.realpath(newCodePath));
- } catch (IOException e) {
- Slog.w(TAG, "Failed to get canonical path.", e);
- return;
- }
-
- // This is a bit of a hack. The oldCodePath doesn't exist at this point (because
- // we've already renamed / deleted it) so we cannot call realpath on it. Here we assume
- // that the last component of the path (i.e, the name) doesn't need canonicalization
- // (i.e, that it isn't ".", ".." or a symbolic link). This is a valid assumption for now
- // but may change in the future. Hopefully this function won't exist at that point.
- final File canonicalOldCodePath = new File(canonicalNewCodePath.getParentFile(),
- oldCodePath.getName());
-
- // Calculate the prefixes of the markers. These are just the paths with "/" replaced
- // with "@".
- String oldMarkerPrefix = canonicalOldCodePath.getAbsolutePath().replace('/', '@');
- if (!oldMarkerPrefix.endsWith("@")) {
- oldMarkerPrefix += "@";
- }
- String newMarkerPrefix = canonicalNewCodePath.getAbsolutePath().replace('/', '@');
- if (!newMarkerPrefix.endsWith("@")) {
- newMarkerPrefix += "@";
- }
-
- List<String> updatedPaths = update.getAllCodePathsExcludingResourceOnly();
- List<String> markerSuffixes = new ArrayList<String>(updatedPaths.size());
- for (String updatedPath : updatedPaths) {
- String updatedPathName = new File(updatedPath).getName();
- markerSuffixes.add(updatedPathName.replace('/', '@'));
- }
-
- for (int userId : resolveUserIds(user.getIdentifier())) {
- File profileDir = Environment.getDataProfilesDeForeignDexDirectory(userId);
-
- for (String markerSuffix : markerSuffixes) {
- File oldForeignUseMark = new File(profileDir, oldMarkerPrefix + markerSuffix);
- File newForeignUseMark = new File(profileDir, newMarkerPrefix + markerSuffix);
- if (oldForeignUseMark.exists()) {
- try {
- Os.rename(oldForeignUseMark.getAbsolutePath(),
- newForeignUseMark.getAbsolutePath());
- } catch (ErrnoException e) {
- Slog.w(TAG, "Failed to rename foreign use marker", e);
- oldForeignUseMark.delete();
- }
- }
- }
- }
- }
-
/**
* Derive the ABI of a non-system package located at {@code scanFile}. This information
* is derived purely on the basis of the contents of {@code scanFile} and
@@ -15279,7 +15192,8 @@
mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles,
null /* instructionSets */, false /* checkProfiles */,
getCompilerFilterForReason(REASON_INSTALL),
- getOrCreateCompilerPackageStats(pkg));
+ getOrCreateCompilerPackageStats(pkg),
+ mDexManager.isUsedByOtherApps(pkg.packageName));
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
// Notify BackgroundDexOptService that the package has been changed.
@@ -16578,8 +16492,6 @@
try (PackageFreezer freezer = freezePackage(packageName, "clearApplicationProfileData")) {
synchronized (mInstallLock) {
clearAppProfilesLIF(pkg, UserHandle.USER_ALL);
- destroyAppReferenceProfileLeafLIF(pkg, UserHandle.USER_ALL,
- true /* removeBaseMarker */);
}
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
index 8a3f48e..0634dac 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
@@ -23,7 +23,7 @@
/**
* Manage (retrieve) mappings from compilation reason to compilation filter.
*/
-class PackageManagerServiceCompilerMapping {
+public class PackageManagerServiceCompilerMapping {
// Names for compilation reasons.
static final String REASON_STRINGS[] = {
"first-boot", "boot", "install", "bg-dexopt", "ab-ota", "nsys-library", "shared-apk",
@@ -80,9 +80,7 @@
try {
// Check that the system property name is legal.
String sysPropName = getSystemPropertyName(reason);
- if (sysPropName == null ||
- sysPropName.isEmpty() ||
- sysPropName.length() > SystemProperties.PROP_NAME_MAX) {
+ if (sysPropName == null || sysPropName.isEmpty()) {
throw new IllegalStateException("Reason system property name \"" +
sysPropName +"\" for reason " + REASON_STRINGS[reason]);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 45887e1..9feee8c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -133,7 +133,8 @@
sortTemp, packageManagerService);
// Give priority to apps used by other apps.
- applyPackageFilter((pkg) -> PackageDexOptimizer.isUsedByOtherApps(pkg), result,
+ applyPackageFilter((pkg) ->
+ packageManagerService.getDexManager().isUsedByOtherApps(pkg.packageName), result,
remainingPkgs, sortTemp, packageManagerService);
// Filter out packages that aren't recently used, add all remaining apps.
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index e18d4e0..44b372b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -110,6 +110,10 @@
return runInstallWrite();
case "compile":
return runCompile();
+ case "reconcile-secondary-dex-files":
+ return runreconcileSecondaryDexFiles();
+ case "bg-dexopt-job":
+ return runDexoptJob();
case "dump-profiles":
return runDumpProfiles();
case "list":
@@ -281,6 +285,7 @@
String compilerFilter = null;
String compilationReason = null;
String checkProfilesRaw = null;
+ boolean secondaryDex = false;
String opt;
while ((opt = getNextOption()) != null) {
@@ -308,6 +313,9 @@
clearProfileData = true;
compilationReason = "install";
break;
+ case "--secondary-dex":
+ secondaryDex = true;
+ break;
default:
pw.println("Error: Unknown option: " + opt);
return 1;
@@ -380,8 +388,11 @@
mInterface.clearApplicationProfileData(packageName);
}
- boolean result = mInterface.performDexOptMode(packageName,
- checkProfiles, targetCompilerFilter, forceCompilation);
+ boolean result = secondaryDex
+ ? mInterface.performDexOptSecondary(packageName,
+ targetCompilerFilter, forceCompilation)
+ : mInterface.performDexOptMode(packageName,
+ checkProfiles, targetCompilerFilter, forceCompilation);
if (!result) {
failedPackages.add(packageName);
}
@@ -409,6 +420,17 @@
}
}
+ private int runreconcileSecondaryDexFiles() throws RemoteException {
+ String packageName = getNextArg();
+ mInterface.reconcileSecondaryDexFiles(packageName);
+ return 0;
+ }
+
+ private int runDexoptJob() throws RemoteException {
+ boolean result = mInterface.runBackgroundDexoptJob();
+ return result ? 0 : -1;
+ }
+
private int runDumpProfiles() throws RemoteException {
String packageName = getNextArg();
mInterface.dumpProfiles(packageName);
@@ -1441,6 +1463,13 @@
}
pw.println(" --reset: restore package to its post-install state");
pw.println(" --check-prof (true | false): look at profiles when doing dexopt?");
+ pw.println(" --secondary-dex: compile app secondary dex files");
+ pw.println(" bg-dexopt-job");
+ pw.println(" Execute the background optimizations immediately.");
+ pw.println(" Note that the command only runs the background optimizer logic. It may");
+ pw.println(" overlap with the actual job but the job scheduler will not be able to");
+ pw.println(" cancel it. It will also run even if the device is not in the idle");
+ pw.println(" maintenance mode.");
pw.println(" list features");
pw.println(" Prints all features of the system.");
pw.println(" list instrumentation [-f] [TARGET-PACKAGE]");
@@ -1460,6 +1489,8 @@
pw.println(" -3: filter to only show third party packages");
pw.println(" -i: see the installer for the packages");
pw.println(" -u: also include uninstalled packages");
+ pw.println(" reconcile-secondary-dex-files TARGET-PACKAGE");
+ pw.println(" Reconciles the package secondary dex files with the generated oat files.");
pw.println(" list permission-groups");
pw.println(" Prints all known permission groups.");
pw.println(" list permissions [-g] [-f] [-d] [-u] [GROUP]");
diff --git a/services/core/java/com/android/server/pm/SELinuxMMAC.java b/services/core/java/com/android/server/pm/SELinuxMMAC.java
index 522c2e8..9a559ee 100644
--- a/services/core/java/com/android/server/pm/SELinuxMMAC.java
+++ b/services/core/java/com/android/server/pm/SELinuxMMAC.java
@@ -60,8 +60,8 @@
/** Path to MAC permissions on system image */
private static final File[] MAC_PERMISSIONS =
- { new File(Environment.getRootDirectory(), "/etc/security/plat_mac_permissions.xml"),
- new File(Environment.getRootDirectory(), "/etc/security/nonplat_mac_permissions.xml") };
+ { new File(Environment.getRootDirectory(), "/etc/selinux/plat_mac_permissions.xml"),
+ new File(Environment.getVendorDirectory(), "/etc/selinux/nonplat_mac_permissions.xml") };
// Append privapp to existing seinfo label
private static final String PRIVILEGED_APP_STR = ":privapp";
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 6d06838..01124e2 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -16,13 +16,21 @@
package com.android.server.pm.dex;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageParser;
-import android.content.pm.ApplicationInfo;
+import android.os.RemoteException;
+import android.os.storage.StorageManager;
import android.util.Slog;
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.pm.Installer;
+import com.android.server.pm.Installer.InstallerException;
+import com.android.server.pm.PackageDexOptimizer;
import com.android.server.pm.PackageManagerServiceUtils;
+import com.android.server.pm.PackageManagerServiceCompilerMapping;
import java.io.File;
import java.io.IOException;
@@ -32,6 +40,9 @@
import java.util.Map;
import java.util.Set;
+import static com.android.server.pm.dex.PackageDexUsage.PackageUseInfo;
+import static com.android.server.pm.dex.PackageDexUsage.DexUseInfo;
+
/**
* This class keeps track of how dex files are used.
* Every time it gets a notification about a dex file being loaded it tracks
@@ -54,15 +65,26 @@
// encode and save the dex usage data.
private final PackageDexUsage mPackageDexUsage;
+ private final IPackageManager mPackageManager;
+ private final PackageDexOptimizer mPackageDexOptimizer;
+ private final Object mInstallLock;
+ @GuardedBy("mInstallLock")
+ private final Installer mInstaller;
+
// Possible outcomes of a dex search.
private static int DEX_SEARCH_NOT_FOUND = 0; // dex file not found
private static int DEX_SEARCH_FOUND_PRIMARY = 1; // dex file is the primary/base apk
private static int DEX_SEARCH_FOUND_SPLIT = 2; // dex file is a split apk
private static int DEX_SEARCH_FOUND_SECONDARY = 3; // dex file is a secondary dex
- public DexManager() {
+ public DexManager(IPackageManager pms, PackageDexOptimizer pdo,
+ Installer installer, Object installLock) {
mPackageCodeLocationsCache = new HashMap<>();
mPackageDexUsage = new PackageDexUsage();
+ mPackageManager = pms;
+ mPackageDexOptimizer = pdo;
+ mInstaller = installer;
+ mInstallLock = installLock;
}
/**
@@ -132,8 +154,8 @@
// - new installed splits
// If we can't find the owner of the dex we simply do not track it. The impact is
// that the dex file will not be considered for offline optimizations.
- // TODO(calin): add hooks for install/uninstall notifications to
- // capture new or obsolete packages.
+ // TODO(calin): add hooks for move/uninstall notifications to
+ // capture package moves or obsolete packages.
if (DEBUG) {
Slog.i(TAG, "Could not find owning package for dex file: " + dexPath);
}
@@ -157,6 +179,20 @@
}
}
+ public void notifyPackageInstalled(PackageInfo info, int userId) {
+ cachePackageCodeLocation(info, userId);
+ }
+
+ private void cachePackageCodeLocation(PackageInfo info, int userId) {
+ PackageCodeLocations pcl = mPackageCodeLocationsCache.get(info.packageName);
+ if (pcl != null) {
+ pcl.mergeAppDataDirs(info.applicationInfo, userId);
+ } else {
+ mPackageCodeLocationsCache.put(info.packageName,
+ new PackageCodeLocations(info.applicationInfo, userId));
+ }
+ }
+
private void loadInternal(Map<Integer, List<PackageInfo>> existingPackages) {
Map<String, Set<Integer>> packageToUsersMap = new HashMap<>();
// Cache the code locations for the installed packages. This allows for
@@ -166,13 +202,8 @@
int userId = entry.getKey();
for (PackageInfo pi : packageInfoList) {
// Cache the code locations.
- PackageCodeLocations pcl = mPackageCodeLocationsCache.get(pi.packageName);
- if (pcl != null) {
- pcl.mergeAppDataDirs(pi.applicationInfo, userId);
- } else {
- mPackageCodeLocationsCache.put(pi.packageName,
- new PackageCodeLocations(pi.applicationInfo, userId));
- }
+ cachePackageCodeLocation(pi, userId);
+
// Cache a map from package name to the set of user ids who installed the package.
// We will use it to sync the data and remove obsolete entries from
// mPackageDexUsage.
@@ -190,11 +221,161 @@
* Get the package dex usage for the given package name.
* @return the package data or null if there is no data available for this package.
*/
- public PackageDexUsage.PackageUseInfo getPackageUseInfo(String packageName) {
+ public PackageUseInfo getPackageUseInfo(String packageName) {
return mPackageDexUsage.getPackageUseInfo(packageName);
}
/**
+ * Perform dexopt on the package {@code packageName} secondary dex files.
+ * @return true if all secondary dex files were processed successfully (compiled or skipped
+ * because they don't need to be compiled)..
+ */
+ public boolean dexoptSecondaryDex(String packageName, String compilerFilter, boolean force) {
+ // Select the dex optimizer based on the force parameter.
+ // Forced compilation is done through ForcedUpdatePackageDexOptimizer which will adjust
+ // the necessary dexopt flags to make sure that compilation is not skipped. This avoid
+ // passing the force flag through the multitude of layers.
+ // Note: The force option is rarely used (cmdline input for testing, mostly), so it's OK to
+ // allocate an object here.
+ PackageDexOptimizer pdo = force
+ ? new PackageDexOptimizer.ForcedUpdatePackageDexOptimizer(mPackageDexOptimizer)
+ : mPackageDexOptimizer;
+ PackageUseInfo useInfo = getPackageUseInfo(packageName);
+ if (useInfo == null || useInfo.getDexUseInfoMap().isEmpty()) {
+ if (DEBUG) {
+ Slog.d(TAG, "No secondary dex use for package:" + packageName);
+ }
+ // Nothing to compile, return true.
+ return true;
+ }
+ boolean success = true;
+ for (Map.Entry<String, DexUseInfo> entry : useInfo.getDexUseInfoMap().entrySet()) {
+ String dexPath = entry.getKey();
+ DexUseInfo dexUseInfo = entry.getValue();
+ PackageInfo pkg = null;
+ try {
+ pkg = mPackageManager.getPackageInfo(packageName, /*flags*/0,
+ dexUseInfo.getOwnerUserId());
+ } catch (RemoteException e) {
+ throw new AssertionError(e);
+ }
+ // It may be that the package gets uninstalled while we try to compile its
+ // secondary dex files. If that's the case, just ignore.
+ // Note that we don't break the entire loop because the package might still be
+ // installed for other users.
+ if (pkg == null) {
+ Slog.d(TAG, "Could not find package when compiling secondary dex " + packageName
+ + " for user " + dexUseInfo.getOwnerUserId());
+ mPackageDexUsage.removeUserPackage(packageName, dexUseInfo.getOwnerUserId());
+ continue;
+ }
+ int result = pdo.dexOptSecondaryDexPath(pkg.applicationInfo, dexPath,
+ dexUseInfo.getLoaderIsas(), compilerFilter, dexUseInfo.isUsedByOtherApps());
+ success = success && (result != PackageDexOptimizer.DEX_OPT_FAILED);
+ }
+ return success;
+ }
+
+ /**
+ * Reconcile the information we have about the secondary dex files belonging to
+ * {@code packagName} and the actual dex files. For all dex files that were
+ * deleted, update the internal records and delete any generated oat files.
+ */
+ public void reconcileSecondaryDexFiles(String packageName) {
+ PackageUseInfo useInfo = getPackageUseInfo(packageName);
+ if (useInfo == null || useInfo.getDexUseInfoMap().isEmpty()) {
+ if (DEBUG) {
+ Slog.d(TAG, "No secondary dex use for package:" + packageName);
+ }
+ // Nothing to reconcile.
+ return;
+ }
+ Set<String> dexFilesToRemove = new HashSet<>();
+ boolean updated = false;
+ for (Map.Entry<String, DexUseInfo> entry : useInfo.getDexUseInfoMap().entrySet()) {
+ String dexPath = entry.getKey();
+ DexUseInfo dexUseInfo = entry.getValue();
+ PackageInfo pkg = null;
+ try {
+ // Note that we look for the package in the PackageManager just to be able
+ // to get back the real app uid and its storage kind. These are only used
+ // to perform extra validation in installd.
+ // TODO(calin): maybe a bit overkill.
+ pkg = mPackageManager.getPackageInfo(packageName, /*flags*/0,
+ dexUseInfo.getOwnerUserId());
+ } catch (RemoteException ignore) {
+ // Can't happen, DexManager is local.
+ }
+ if (pkg == null) {
+ // It may be that the package was uninstalled while we process the secondary
+ // dex files.
+ Slog.d(TAG, "Could not find package when compiling secondary dex " + packageName
+ + " for user " + dexUseInfo.getOwnerUserId());
+ // Update the usage and continue, another user might still have the package.
+ updated = mPackageDexUsage.removeUserPackage(
+ packageName, dexUseInfo.getOwnerUserId()) || updated;
+ continue;
+ }
+ ApplicationInfo info = pkg.applicationInfo;
+ int flags = 0;
+ if (info.dataDir.equals(info.deviceProtectedDataDir)) {
+ flags |= StorageManager.FLAG_STORAGE_DE;
+ } else if (info.dataDir.equals(info.credentialProtectedDataDir)) {
+ flags |= StorageManager.FLAG_STORAGE_CE;
+ } else {
+ Slog.e(TAG, "Could not infer CE/DE storage for package " + info.packageName);
+ updated = mPackageDexUsage.removeUserPackage(
+ packageName, dexUseInfo.getOwnerUserId()) || updated;
+ continue;
+ }
+
+ boolean dexStillExists = true;
+ synchronized(mInstallLock) {
+ try {
+ String[] isas = dexUseInfo.getLoaderIsas().toArray(new String[0]);
+ dexStillExists = mInstaller.reconcileSecondaryDexFile(dexPath, packageName,
+ pkg.applicationInfo.uid, isas, pkg.applicationInfo.volumeUuid, flags);
+ } catch (InstallerException e) {
+ Slog.e(TAG, "Got InstallerException when reconciling dex " + dexPath +
+ " : " + e.getMessage());
+ }
+ }
+ if (!dexStillExists) {
+ updated = mPackageDexUsage.removeDexFile(
+ packageName, dexPath, dexUseInfo.getOwnerUserId()) || updated;
+ }
+
+ }
+ if (updated) {
+ mPackageDexUsage.maybeWriteAsync();
+ }
+ }
+
+ /**
+ * Return all packages that contain records of secondary dex files.
+ */
+ public Set<String> getAllPackagesWithSecondaryDexFiles() {
+ return mPackageDexUsage.getAllPackagesWithSecondaryDexFiles();
+ }
+
+ /**
+ * Return true if the profiling data collected for the given app indicate
+ * that the apps's APK has been loaded by another app.
+ * Note that this returns false for all apps without any collected profiling data.
+ */
+ public boolean isUsedByOtherApps(String packageName) {
+ PackageUseInfo useInfo = getPackageUseInfo(packageName);
+ if (useInfo == null) {
+ // No use info, means the package was not used or it was used but not by other apps.
+ // Note that right now we might prune packages which are not used by other apps.
+ // TODO(calin): maybe we should not (prune) so we can have an accurate view when we try
+ // to access the package use.
+ return false;
+ }
+ return useInfo.isUsedByOtherApps();
+ }
+
+ /**
* Retrieves the package which owns the given dexPath.
*/
private DexSearchResult getDexPackage(
diff --git a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
index 10384a2..3693bce0 100644
--- a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
+++ b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
@@ -376,12 +376,86 @@
}
}
+ /**
+ * Remove all the records about package {@code packageName} belonging to user {@code userId}.
+ * @return true if the record was found and actually deleted,
+ * false if the record doesn't exist
+ */
+ public boolean removeUserPackage(String packageName, int userId) {
+ synchronized (mPackageUseInfoMap) {
+ PackageUseInfo packageUseInfo = mPackageUseInfoMap.get(packageName);
+ if (packageUseInfo == null) {
+ return false;
+ }
+ boolean updated = false;
+ Iterator<Map.Entry<String, DexUseInfo>> dIt =
+ packageUseInfo.mDexUseInfoMap.entrySet().iterator();
+ while (dIt.hasNext()) {
+ DexUseInfo dexUseInfo = dIt.next().getValue();
+ if (dexUseInfo.mOwnerUserId == userId) {
+ dIt.remove();
+ updated = true;
+ }
+ }
+ return updated;
+ }
+ }
+
+ /**
+ * Remove the secondary dex file record belonging to the package {@code packageName}
+ * and user {@code userId}.
+ * @return true if the record was found and actually deleted,
+ * false if the record doesn't exist
+ */
+ public boolean removeDexFile(String packageName, String dexFile, int userId) {
+ synchronized (mPackageUseInfoMap) {
+ PackageUseInfo packageUseInfo = mPackageUseInfoMap.get(packageName);
+ if (packageUseInfo == null) {
+ return false;
+ }
+ return removeDexFile(packageUseInfo, dexFile, userId);
+ }
+ }
+
+ private boolean removeDexFile(PackageUseInfo packageUseInfo, String dexFile, int userId) {
+ DexUseInfo dexUseInfo = packageUseInfo.mDexUseInfoMap.get(dexFile);
+ if (dexUseInfo == null) {
+ return false;
+ }
+ if (dexUseInfo.mOwnerUserId == userId) {
+ packageUseInfo.mDexUseInfoMap.remove(dexFile);
+ return true;
+ }
+ return false;
+ }
+
public PackageUseInfo getPackageUseInfo(String packageName) {
synchronized (mPackageUseInfoMap) {
- return mPackageUseInfoMap.get(packageName);
+ PackageUseInfo useInfo = mPackageUseInfoMap.get(packageName);
+ // The useInfo contains a map for secondary dex files which could be modified
+ // concurrently after this method returns and thus outside the locking we do here.
+ // (i.e. the map is updated when new class loaders are created, which can happen anytime
+ // after this method returns)
+ // Make a defensive copy to be sure we don't get concurrent modifications.
+ return useInfo == null ? null : new PackageUseInfo(useInfo);
}
}
+ /**
+ * Return all packages that contain records of secondary dex files.
+ */
+ public Set<String> getAllPackagesWithSecondaryDexFiles() {
+ Set<String> packages = new HashSet<>();
+ synchronized (mPackageUseInfoMap) {
+ for (Map.Entry<String, PackageUseInfo> entry : mPackageUseInfoMap.entrySet()) {
+ if (!entry.getValue().mDexUseInfoMap.isEmpty()) {
+ packages.add(entry.getKey());
+ }
+ }
+ }
+ return packages;
+ }
+
public void clear() {
synchronized (mPackageUseInfoMap) {
mPackageUseInfoMap.clear();
diff --git a/services/core/java/com/android/server/updates/TzDataInstallReceiver.java b/services/core/java/com/android/server/updates/TzDataInstallReceiver.java
index b704eb1..3c73c88 100644
--- a/services/core/java/com/android/server/updates/TzDataInstallReceiver.java
+++ b/services/core/java/com/android/server/updates/TzDataInstallReceiver.java
@@ -20,7 +20,7 @@
import java.io.File;
import java.io.IOException;
-import libcore.tzdata.update2.TimeZoneBundleInstaller;
+import libcore.tzdata.update2.TimeZoneDistroInstaller;
/**
* An install receiver responsible for installing timezone data updates.
@@ -34,14 +34,14 @@
private static final String UPDATE_DIR_NAME = TZ_DATA_DIR.getPath() + "/updates/";
private static final String UPDATE_METADATA_DIR_NAME = "metadata/";
private static final String UPDATE_VERSION_FILE_NAME = "version";
- private static final String UPDATE_CONTENT_FILE_NAME = "tzdata_bundle.zip";
+ private static final String UPDATE_CONTENT_FILE_NAME = "tzdata_distro.zip";
- private final TimeZoneBundleInstaller installer;
+ private final TimeZoneDistroInstaller installer;
public TzDataInstallReceiver() {
super(UPDATE_DIR_NAME, UPDATE_CONTENT_FILE_NAME, UPDATE_METADATA_DIR_NAME,
UPDATE_VERSION_FILE_NAME);
- installer = new TimeZoneBundleInstaller(TAG, SYSTEM_TZ_DATA_FILE, TZ_DATA_DIR);
+ installer = new TimeZoneDistroInstaller(TAG, SYSTEM_TZ_DATA_FILE, TZ_DATA_DIR);
}
@Override
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 27d95a4..de4a55b 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -61,7 +61,6 @@
import com.android.server.camera.CameraService;
import com.android.server.clipboard.ClipboardService;
import com.android.server.connectivity.IpConnectivityMetrics;
-import com.android.server.connectivity.MetricsLoggerService;
import com.android.server.devicepolicy.DevicePolicyManagerService;
import com.android.server.display.DisplayManagerService;
import com.android.server.display.NightDisplayService;
@@ -662,10 +661,6 @@
mSystemServiceManager.startService(BluetoothService.class);
}
- traceBeginAndSlog("ConnectivityMetricsLoggerService");
- mSystemServiceManager.startService(MetricsLoggerService.class);
- Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
-
traceBeginAndSlog("IpConnectivityMetrics");
mSystemServiceManager.startService(IpConnectivityMetrics.class);
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
diff --git a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
index 43c8957..5553fd6 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkScoreServiceTest.java
@@ -271,8 +271,8 @@
final Bundle bundle = new Bundle();
bundle.putParcelable(EXTRA_RECOMMENDATION_RESULT, providerResult);
doAnswer(invocation -> {
- bundle.putInt(EXTRA_SEQUENCE, invocation.getArgumentAt(2, int.class));
- invocation.getArgumentAt(1, IRemoteCallback.class).sendResult(bundle);
+ bundle.putInt(EXTRA_SEQUENCE, invocation.getArgument(2));
+ invocation.<IRemoteCallback>getArgument(1).sendResult(bundle);
return null;
}).when(mRecommendationProvider)
.requestRecommendation(eq(mRecommendationRequest), isA(IRemoteCallback.class),
@@ -336,7 +336,7 @@
injectProvider();
final Bundle bundle = new Bundle();
doAnswer(invocation -> {
- invocation.getArgumentAt(1, IRemoteCallback.class).sendResult(bundle);
+ invocation.<IRemoteCallback>getArgument(1).sendResult(bundle);
return null;
}).when(mRecommendationProvider)
.requestRecommendation(eq(mRecommendationRequest), isA(IRemoteCallback.class),
@@ -634,7 +634,7 @@
IBinder mockBinder = mock(IBinder.class);
when(mockBinder.queryLocalInterface(anyString()))
.thenReturn(mRecommendationProvider);
- invocation.getArgumentAt(1, ServiceConnection.class)
+ invocation.<ServiceConnection>getArgument(1)
.onServiceConnected(componentName, mockBinder);
return true;
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java
index 3806da6..e43786c 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockUtils.java
@@ -29,6 +29,7 @@
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.mockito.Mockito;
+import org.mockito.hamcrest.MockitoHamcrest;
public class MockUtils {
private MockUtils() {
@@ -47,7 +48,7 @@
description.appendText("UserHandle: user-id= \"" + userId + "\"");
}
};
- return Mockito.argThat(m);
+ return MockitoHamcrest.argThat(m);
}
public static Intent checkIntentComponent(final ComponentName component) {
@@ -63,7 +64,7 @@
description.appendText("Intent: component=\"" + component + "\"");
}
};
- return Mockito.argThat(m);
+ return MockitoHamcrest.argThat(m);
}
public static Intent checkIntentAction(final String action) {
@@ -79,7 +80,7 @@
description.appendText("Intent: action=\"" + action + "\"");
}
};
- return Mockito.argThat(m);
+ return MockitoHamcrest.argThat(m);
}
public static Intent checkIntent(final Intent intent) {
@@ -94,7 +95,7 @@
description.appendText(intent.toString());
}
};
- return Mockito.argThat(m);
+ return MockitoHamcrest.argThat(m);
}
public static Bundle checkUserRestrictions(String... keys) {
@@ -111,7 +112,7 @@
description.appendText("User restrictions=" + getRestrictionsAsString(expected));
}
};
- return Mockito.argThat(m);
+ return MockitoHamcrest.argThat(m);
}
private static String getRestrictionsAsString(Bundle b) {
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
index b655f3a..90a2ec0 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
@@ -73,8 +73,7 @@
mInvalidIsa = new TestData("INVALID", "INVALID_ISA", mUser0);
mDoesNotExist = new TestData("DOES.NOT.EXIST", isa, mUser1);
-
- mDexManager = new DexManager();
+ mDexManager = new DexManager(null, null, null, null);
// Foo and Bar are available to user0.
// Only Bar is available to user1;
@@ -204,6 +203,46 @@
assertNull(getPackageUseInfo(mBarUser1));
}
+ @Test
+ public void testNotifyPackageInstallUsedByOther() {
+ TestData newPackage = new TestData("newPackage",
+ VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]), mUser0);
+
+ List<String> newSecondaries = newPackage.getSecondaryDexPaths();
+ // Before we notify about the installation of the newPackage if mFoo
+ // is trying to load something from it we should not find it.
+ notifyDexLoad(mFooUser0, newSecondaries, mUser0);
+ assertNull(getPackageUseInfo(newPackage));
+
+ // Notify about newPackage install and let mFoo load its dexes.
+ mDexManager.notifyPackageInstalled(newPackage.mPackageInfo, mUser0);
+ notifyDexLoad(mFooUser0, newSecondaries, mUser0);
+
+ // We should get back the right info.
+ PackageUseInfo pui = getPackageUseInfo(newPackage);
+ assertNotNull(pui);
+ assertFalse(pui.isUsedByOtherApps());
+ assertEquals(newSecondaries.size(), pui.getDexUseInfoMap().size());
+ assertSecondaryUse(newPackage, pui, newSecondaries, /*isUsedByOtherApps*/true, mUser0);
+ }
+
+ @Test
+ public void testNotifyPackageInstallSelfUse() {
+ TestData newPackage = new TestData("newPackage",
+ VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]), mUser0);
+
+ List<String> newSecondaries = newPackage.getSecondaryDexPaths();
+ // Packages should be able to find their own dex files even if the notification about
+ // their installation is delayed.
+ notifyDexLoad(newPackage, newSecondaries, mUser0);
+
+ PackageUseInfo pui = getPackageUseInfo(newPackage);
+ assertNotNull(pui);
+ assertFalse(pui.isUsedByOtherApps());
+ assertEquals(newSecondaries.size(), pui.getDexUseInfoMap().size());
+ assertSecondaryUse(newPackage, pui, newSecondaries, /*isUsedByOtherApps*/false, mUser0);
+ }
+
private void assertSecondaryUse(TestData testData, PackageUseInfo pui,
List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId) {
for (String dex : secondaries) {
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
index 5a42841..19e0bcf 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
@@ -256,6 +256,32 @@
assertNull(mPackageDexUsage.getPackageUseInfo(mFooBaseUser0.mPackageName));
}
+ @Test
+ public void testRemoveUserPackage() {
+ // Record Bar secondaries for two different users.
+ assertTrue(record(mBarSecondary1User0));
+ assertTrue(record(mBarSecondary2User1));
+
+ // Remove user 0 files.
+ assertTrue(mPackageDexUsage.removeUserPackage(mBarSecondary1User0.mPackageName,
+ mBarSecondary1User0.mOwnerUserId));
+ // Assert that only user 1 files are there.
+ assertPackageDexUsage(null, mBarSecondary2User1);
+ }
+
+ @Test
+ public void testRemoveDexFile() {
+ // Record Bar secondaries for two different users.
+ assertTrue(record(mBarSecondary1User0));
+ assertTrue(record(mBarSecondary2User1));
+
+ // Remove mBarSecondary1User0 file.
+ assertTrue(mPackageDexUsage.removeDexFile(mBarSecondary1User0.mPackageName,
+ mBarSecondary1User0.mDexFile, mBarSecondary1User0.mOwnerUserId));
+ // Assert that only user 1 files are there.
+ assertPackageDexUsage(null, mBarSecondary2User1);
+ }
+
private void assertPackageDexUsage(TestData primary, TestData... secondaries) {
String packageName = primary == null ? secondaries[0].mPackageName : primary.mPackageName;
boolean primaryUsedByOtherApps = primary == null ? false : primary.mUsedByOtherApps;
diff --git a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
index 82c7bdb..1192901 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
@@ -32,14 +32,12 @@
import android.webkit.WebViewProviderInfo;
import android.webkit.WebViewProviderResponse;
-import org.hamcrest.Description;
-
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.Matchers;
-import org.mockito.ArgumentMatcher;
+import org.mockito.compat.ArgumentMatcher;
import java.util.concurrent.CountDownLatch;
@@ -131,14 +129,13 @@
}
@Override
- public boolean matches(Object p) {
+ public boolean matchesObject(Object p) {
return ((PackageInfo) p).packageName.equals(mPackageName);
}
- // Provide a more useful description in case of mismatch
@Override
- public void describeTo (Description description) {
- description.appendText(String.format("PackageInfo with name '%s'", mPackageName));
+ public String toString() {
+ return String.format("PackageInfo with name '%s'", mPackageName);
}
}
diff --git a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
index 1fe5cb7..29d58ce 100644
--- a/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
+++ b/services/tests/shortcutmanagerutils/src/com/android/server/pm/shortcutmanagertest/ShortcutManagerTestUtils.java
@@ -59,6 +59,7 @@
import org.json.JSONObject;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
+import org.mockito.hamcrest.MockitoHamcrest;
import java.io.BufferedReader;
import java.io.File;
@@ -665,7 +666,7 @@
d.appendText(description);
}
};
- return Mockito.argThat(m);
+ return MockitoHamcrest.argThat(m);
}
public static List<ShortcutInfo> checkShortcutIds(String... ids) {
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 211173c..39da224 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -32,6 +32,7 @@
import android.hardware.usb.UsbManager;
import android.hardware.usb.UsbPort;
import android.hardware.usb.UsbPortStatus;
+import android.os.BatteryManager;
import android.os.FileUtils;
import android.os.Handler;
import android.os.Looper;
@@ -111,6 +112,7 @@
private static final int MSG_USER_SWITCHED = 5;
private static final int MSG_UPDATE_USER_RESTRICTIONS = 6;
private static final int MSG_UPDATE_HOST_STATE = 7;
+ private static final int MSG_UPDATE_CHARGING_STATE = 9;
private static final int AUDIO_MODE_SOURCE = 1;
@@ -192,6 +194,15 @@
}
};
+ private final BroadcastReceiver mChargingReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ int chargePlug = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
+ boolean usbCharging = chargePlug == BatteryManager.BATTERY_PLUGGED_USB;
+ mHandler.sendMessage(MSG_UPDATE_CHARGING_STATE, usbCharging);
+ }
+ };
+
public UsbDeviceManager(Context context, UsbAlsaManager alsaManager) {
mContext = context;
mUsbAlsaManager = alsaManager;
@@ -216,6 +227,8 @@
}
mContext.registerReceiver(mHostReceiver,
new IntentFilter(UsbManager.ACTION_USB_PORT_CHANGED));
+ mContext.registerReceiver(mChargingReceiver,
+ new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
}
private UsbSettingsManager getCurrentSettings() {
@@ -330,6 +343,7 @@
private int mUsbNotificationId;
private boolean mAdbNotificationShown;
private int mCurrentUser = UserHandle.USER_NULL;
+ private boolean mUsbCharging;
public UsbHandler(Looper looper) {
super(looper);
@@ -428,7 +442,10 @@
args.argi2 = sourcePower ? 1 :0;
args.argi3 = sinkPower ? 1 :0;
- obtainMessage(MSG_UPDATE_HOST_STATE, args).sendToTarget();
+ removeMessages(MSG_UPDATE_HOST_STATE);
+ Message msg = obtainMessage(MSG_UPDATE_HOST_STATE, args);
+ // debounce rapid transitions of connect/disconnect on type-c ports
+ sendMessageDelayed(msg, UPDATE_DELAY);
}
private boolean waitForState(String state) {
@@ -767,6 +784,10 @@
mPendingBootBroadcast = true;
}
break;
+ case MSG_UPDATE_CHARGING_STATE:
+ mUsbCharging = (msg.arg1 == 1);
+ updateUsbNotification();
+ break;
case MSG_ENABLE_ADB:
setAdbEnabled(msg.arg1 == 1);
break;
@@ -851,7 +872,7 @@
}
} else if (mSourcePower) {
id = com.android.internal.R.string.usb_supplying_notification_title;
- } else if (mHostConnected && mSinkPower) {
+ } else if (mHostConnected && mSinkPower && mUsbCharging) {
id = com.android.internal.R.string.usb_charging_notification_title;
}
if (id != mUsbNotificationId) {
@@ -955,6 +976,7 @@
pw.println(" mHostConnected: " + mHostConnected);
pw.println(" mSourcePower: " + mSourcePower);
pw.println(" mSinkPower: " + mSinkPower);
+ pw.println(" mUsbCharging: " + mUsbCharging);
try {
pw.println(" Kernel state: "
+ FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim());
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index c69b7c2..27f7172 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -16,12 +16,21 @@
package android.telecom;
+import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
+import android.os.ParcelFileDescriptor;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
import java.lang.String;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -312,8 +321,15 @@
*/
public static final int PROPERTY_HAS_CDMA_VOICE_PRIVACY = 0x00000080;
+ /**
+ * Indicates that the call is from a self-managed {@link ConnectionService}.
+ * <p>
+ * See also {@link Connection#PROPERTY_SELF_MANAGED}
+ */
+ public static final int PROPERTY_SELF_MANAGED = 0x00000100;
+
//******************************************************************************************
- // Next PROPERTY value: 0x00000100
+ // Next PROPERTY value: 0x00000200
//******************************************************************************************
private final String mTelecomCallId;
@@ -828,6 +844,155 @@
* @param extras Extras associated with the connection event.
*/
public void onConnectionEvent(Call call, String event, Bundle extras) {}
+
+ /**
+ * Invoked when the RTT mode changes for this call.
+ * @param call The call whose RTT mode has changed.
+ * @param mode the new RTT mode, one of
+ * {@link RttCall#RTT_MODE_FULL}, {@link RttCall#RTT_MODE_HCO},
+ * or {@link RttCall#RTT_MODE_VCO}
+ */
+ public void onRttModeChanged(Call call, int mode) {}
+
+ /**
+ * Invoked when the call's RTT status changes, either from off to on or from on to off.
+ * @param call The call whose RTT status has changed.
+ * @param enabled whether RTT is now enabled or disabled
+ * @param rttCall the {@link RttCall} object to use for reading and writing if RTT is now
+ * on, null otherwise.
+ */
+ public void onRttStatusChanged(Call call, boolean enabled, RttCall rttCall) {}
+
+ /**
+ * Invoked when the remote end of the connection has requested that an RTT communication
+ * channel be opened. A response to this should be sent via {@link #respondToRttRequest}
+ * with the same ID that this method is invoked with.
+ * @param call The call which the RTT request was placed on
+ * @param id The ID of the request.
+ */
+ public void onRttRequest(Call call, int id) {}
+
+ /**
+ * Invoked when the RTT session failed to initiate for some reason, including rejection
+ * by the remote party.
+ * @param call The call which the RTT initiation failure occurred on.
+ * @param reason One of the status codes defined in
+ * {@link android.telecom.Connection.RttModifyStatus}, with the exception of
+ * {@link android.telecom.Connection.RttModifyStatus#SESSION_MODIFY_REQUEST_SUCCESS}.
+ */
+ public void onRttInitiationFailure(Call call, int reason) {}
+ }
+
+ /**
+ * A class that holds the state that describes the state of the RTT channel to the remote
+ * party, if it is active.
+ */
+ public static final class RttCall {
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({RTT_MODE_INVALID, RTT_MODE_FULL, RTT_MODE_HCO, RTT_MODE_VCO})
+ public @interface RttAudioMode {}
+
+ /**
+ * For metrics use. Default value in the proto.
+ * @hide
+ */
+ public static final int RTT_MODE_INVALID = 0;
+
+ /**
+ * Indicates that there should be a bidirectional audio stream between the two parties
+ * on the call.
+ */
+ public static final int RTT_MODE_FULL = 1;
+
+ /**
+ * Indicates that the local user should be able to hear the audio stream from the remote
+ * user, but not vice versa. Equivalent to muting the microphone.
+ */
+ public static final int RTT_MODE_HCO = 2;
+
+ /**
+ * Indicates that the remote user should be able to hear the audio stream from the local
+ * user, but not vice versa. Equivalent to setting the volume to zero.
+ */
+ public static final int RTT_MODE_VCO = 3;
+
+ private static final int READ_BUFFER_SIZE = 1000;
+
+ private InputStreamReader mReceiveStream;
+ private OutputStreamWriter mTransmitStream;
+ private int mRttMode;
+ private final InCallAdapter mInCallAdapter;
+ private final String mTelecomCallId;
+ private char[] mReadBuffer = new char[READ_BUFFER_SIZE];
+
+ /**
+ * @hide
+ */
+ public RttCall(String telecomCallId, InputStreamReader receiveStream,
+ OutputStreamWriter transmitStream, int mode, InCallAdapter inCallAdapter) {
+ mTelecomCallId = telecomCallId;
+ mReceiveStream = receiveStream;
+ mTransmitStream = transmitStream;
+ mRttMode = mode;
+ mInCallAdapter = inCallAdapter;
+ }
+
+ /**
+ * Returns the current RTT audio mode.
+ * @return Current RTT audio mode. One of {@link #RTT_MODE_FULL}, {@link #RTT_MODE_VCO}, or
+ * {@link #RTT_MODE_HCO}.
+ */
+ public int getRttAudioMode() {
+ return mRttMode;
+ }
+
+ /**
+ * Sets the RTT audio mode. The requested mode change will be communicated through
+ * {@link Callback#onRttModeChanged(Call, int)}.
+ * @param mode The desired RTT audio mode, one of {@link #RTT_MODE_FULL},
+ * {@link #RTT_MODE_VCO}, or {@link #RTT_MODE_HCO}.
+ */
+ public void setRttMode(@RttAudioMode int mode) {
+ mInCallAdapter.setRttMode(mTelecomCallId, mode);
+ }
+
+ /**
+ * Writes the string {@param input} into the outgoing text stream for this RTT call. Since
+ * RTT transmits text in real-time, this method should be called once for each character
+ * the user enters into the device.
+ *
+ * This method is not thread-safe -- calling it from multiple threads simultaneously may
+ * lead to interleaved text.
+ * @param input The message to send to the remote user.
+ */
+ public void write(String input) throws IOException {
+ mTransmitStream.write(input);
+ mTransmitStream.flush();
+ }
+
+ /**
+ * Reads a string from the remote user, blocking if there is no data available. Returns
+ * {@code null} if the RTT conversation has been terminated and there is no further data
+ * to read.
+ *
+ * This method is not thread-safe -- calling it from multiple threads simultaneously may
+ * lead to interleaved text.
+ * @return A string containing text sent by the remote user, or {@code null} if the
+ * conversation has been terminated or if there was an error while reading.
+ */
+ public String read() {
+ try {
+ int numRead = mReceiveStream.read(mReadBuffer, 0, READ_BUFFER_SIZE);
+ if (numRead < 0) {
+ return null;
+ }
+ return new String(mReadBuffer, 0, numRead);
+ } catch (IOException e) {
+ Log.w(this, "Exception encountered when reading from InputStreamReader: %s", e);
+ return null;
+ }
+ }
}
/**
@@ -854,8 +1019,10 @@
private int mState;
private List<String> mCannedTextResponses = null;
private String mCallingPackage;
+ private int mTargetSdkVersion;
private String mRemainingPostDialSequence;
private VideoCallImpl mVideoCallImpl;
+ private RttCall mRttCall;
private Details mDetails;
private Bundle mExtras;
@@ -1054,6 +1221,34 @@
}
/**
+ * Sends an RTT upgrade request to the remote end of the connection. Success is not
+ * guaranteed, and notification of success will be via the
+ * {@link Callback#onRttStatusChanged(Call, boolean, RttCall)} callback.
+ */
+ public void sendRttRequest() {
+ mInCallAdapter.sendRttRequest(mTelecomCallId);
+ }
+
+ /**
+ * Responds to an RTT request received via the {@link Callback#onRttRequest(Call, int)} )}
+ * callback.
+ * The ID used here should be the same as the ID that was received via the callback.
+ * @param id The request ID received via {@link Callback#onRttRequest(Call, int)}
+ * @param accept {@code true} if the RTT request should be accepted, {@code false} otherwise.
+ */
+ public void respondToRttRequest(int id, boolean accept) {
+ mInCallAdapter.respondToRttRequest(mTelecomCallId, id, accept);
+ }
+
+ /**
+ * Terminate the RTT session on this call. The resulting state change will be notified via
+ * the {@link Callback#onRttStatusChanged(Call, boolean, RttCall)} callback.
+ */
+ public void stopRtt() {
+ mInCallAdapter.stopRtt(mTelecomCallId);
+ }
+
+ /**
* Adds some extras to this {@link Call}. Existing keys are replaced and new ones are
* added.
* <p>
@@ -1233,6 +1428,23 @@
}
/**
+ * Returns this call's RttCall object. The {@link RttCall} instance is used to send and
+ * receive RTT text data, as well as to change the RTT mode.
+ * @return A {@link Call.RttCall}. {@code null} if there is no active RTT connection.
+ */
+ public @Nullable RttCall getRttCall() {
+ return mRttCall;
+ }
+
+ /**
+ * Returns whether this call has an active RTT connection.
+ * @return true if there is a connection, false otherwise.
+ */
+ public boolean isRttActive() {
+ return mRttCall != null;
+ }
+
+ /**
* Registers a callback to this {@code Call}.
*
* @param callback A {@code Callback}.
@@ -1341,22 +1553,25 @@
}
/** {@hide} */
- Call(Phone phone, String telecomCallId, InCallAdapter inCallAdapter, String callingPackage) {
+ Call(Phone phone, String telecomCallId, InCallAdapter inCallAdapter, String callingPackage,
+ int targetSdkVersion) {
mPhone = phone;
mTelecomCallId = telecomCallId;
mInCallAdapter = inCallAdapter;
mState = STATE_NEW;
mCallingPackage = callingPackage;
+ mTargetSdkVersion = targetSdkVersion;
}
/** {@hide} */
Call(Phone phone, String telecomCallId, InCallAdapter inCallAdapter, int state,
- String callingPackage) {
+ String callingPackage, int targetSdkVersion) {
mPhone = phone;
mTelecomCallId = telecomCallId;
mInCallAdapter = inCallAdapter;
mState = state;
mCallingPackage = callingPackage;
+ mTargetSdkVersion = targetSdkVersion;
}
/** {@hide} */
@@ -1382,7 +1597,8 @@
cannedTextResponsesChanged = true;
}
- VideoCallImpl newVideoCallImpl = parcelableCall.getVideoCallImpl(mCallingPackage);
+ VideoCallImpl newVideoCallImpl = parcelableCall.getVideoCallImpl(mCallingPackage,
+ mTargetSdkVersion);
boolean videoCallChanged = parcelableCall.isVideoCallProviderChanged() &&
!Objects.equals(mVideoCallImpl, newVideoCallImpl);
if (videoCallChanged) {
@@ -1426,6 +1642,32 @@
fireConferenceableCallsChanged();
}
+ boolean isRttChanged = false;
+ boolean rttModeChanged = false;
+ if (parcelableCall.getParcelableRttCall() != null && parcelableCall.getIsRttCallChanged()) {
+ ParcelableRttCall parcelableRttCall = parcelableCall.getParcelableRttCall();
+ InputStreamReader receiveStream = new InputStreamReader(
+ new ParcelFileDescriptor.AutoCloseInputStream(
+ parcelableRttCall.getReceiveStream()),
+ StandardCharsets.UTF_8);
+ OutputStreamWriter transmitStream = new OutputStreamWriter(
+ new ParcelFileDescriptor.AutoCloseOutputStream(
+ parcelableRttCall.getTransmitStream()),
+ StandardCharsets.UTF_8);
+ RttCall newRttCall = new Call.RttCall(mTelecomCallId,
+ receiveStream, transmitStream, parcelableRttCall.getRttMode(), mInCallAdapter);
+ if (mRttCall == null) {
+ isRttChanged = true;
+ } else if (mRttCall.getRttAudioMode() != newRttCall.getRttAudioMode()) {
+ rttModeChanged = true;
+ }
+ mRttCall = newRttCall;
+ } else if (mRttCall != null && parcelableCall.getParcelableRttCall() == null
+ && parcelableCall.getIsRttCallChanged()) {
+ isRttChanged = true;
+ mRttCall = null;
+ }
+
// Now we fire updates, ensuring that any client who listens to any of these notifications
// gets the most up-to-date state.
@@ -1447,6 +1689,12 @@
if (childrenChanged) {
fireChildrenChanged(getChildren());
}
+ if (isRttChanged) {
+ fireOnIsRttChanged(mRttCall != null, mRttCall);
+ }
+ if (rttModeChanged) {
+ fireOnRttModeChanged(mRttCall.getRttAudioMode());
+ }
// If we have transitioned to DISCONNECTED, that means we need to notify clients and
// remove ourselves from the Phone. Note that we do this after completing all state updates
@@ -1477,6 +1725,24 @@
fireOnConnectionEvent(event, extras);
}
+ /** {@hide} */
+ final void internalOnRttUpgradeRequest(final int requestId) {
+ for (CallbackRecord<Callback> record : mCallbackRecords) {
+ final Call call = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(() -> callback.onRttRequest(call, requestId));
+ }
+ }
+
+ /** @hide */
+ final void internalOnRttInitiationFailure(int reason) {
+ for (CallbackRecord<Callback> record : mCallbackRecords) {
+ final Call call = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(() -> callback.onRttInitiationFailure(call, reason));
+ }
+ }
+
private void fireStateChanged(final int newState) {
for (CallbackRecord<Callback> record : mCallbackRecords) {
final Call call = this;
@@ -1645,6 +1911,32 @@
}
/**
+ * Notifies listeners of an RTT on/off change
+ *
+ * @param enabled True if RTT is now enabled, false otherwise
+ */
+ private void fireOnIsRttChanged(final boolean enabled, final RttCall rttCall) {
+ for (CallbackRecord<Callback> record : mCallbackRecords) {
+ final Call call = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(() -> callback.onRttStatusChanged(call, enabled, rttCall));
+ }
+ }
+
+ /**
+ * Notifies listeners of a RTT mode change
+ *
+ * @param mode The new RTT mode
+ */
+ private void fireOnRttModeChanged(final int mode) {
+ for (CallbackRecord<Callback> record : mCallbackRecords) {
+ final Call call = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(() -> callback.onRttModeChanged(call, mode));
+ }
+ }
+
+ /**
* Determines if two bundles are equal.
*
* @param bundle The original bundle.
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index b7391b4..833affa 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -20,9 +20,12 @@
import com.android.internal.telecom.IVideoCallback;
import com.android.internal.telecom.IVideoProvider;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.app.Notification;
+import android.content.Intent;
import android.hardware.camera2.CameraManager;
import android.net.Uri;
import android.os.Binder;
@@ -31,10 +34,16 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.util.ArraySet;
import android.view.Surface;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -385,8 +394,14 @@
*/
public static final int PROPERTY_SELF_MANAGED = 1<<7;
+ /**
+ * When set, indicates that a connection has an active RTT session associated with it.
+ * @hide
+ */
+ public static final int PROPERTY_IS_RTT = 1 << 8;
+
//**********************************************************************************************
- // Next PROPERTY value: 1<<8
+ // Next PROPERTY value: 1<<9
//**********************************************************************************************
/**
@@ -754,6 +769,114 @@
/** @hide */
public void onConferenceSupportedChanged(Connection c, boolean isConferenceSupported) {}
public void onAudioRouteChanged(Connection c, int audioRoute) {}
+ public void onRttInitiationSuccess(Connection c) {}
+ public void onRttInitiationFailure(Connection c, int reason) {}
+ public void onRttSessionRemotelyTerminated(Connection c) {}
+ public void onRemoteRttRequest(Connection c) {}
+ }
+
+ /**
+ * Provides methods to read and write RTT data to/from the in-call app.
+ * @hide
+ */
+ public static final class RttTextStream {
+ private static final int READ_BUFFER_SIZE = 1000;
+ private final InputStreamReader mPipeFromInCall;
+ private final OutputStreamWriter mPipeToInCall;
+ private final ParcelFileDescriptor mFdFromInCall;
+ private final ParcelFileDescriptor mFdToInCall;
+ private char[] mReadBuffer = new char[READ_BUFFER_SIZE];
+
+ /**
+ * @hide
+ */
+ public RttTextStream(ParcelFileDescriptor toInCall, ParcelFileDescriptor fromInCall) {
+ mFdFromInCall = fromInCall;
+ mFdToInCall = toInCall;
+ mPipeFromInCall = new InputStreamReader(
+ new ParcelFileDescriptor.AutoCloseInputStream(fromInCall));
+ mPipeToInCall = new OutputStreamWriter(
+ new ParcelFileDescriptor.AutoCloseOutputStream(toInCall));
+ }
+
+ /**
+ * Writes the string {@param input} into the text stream to the UI for this RTT call. Since
+ * RTT transmits text in real-time, this method should be called as often as text snippets
+ * are received from the remote user, even if it is only one character.
+ *
+ * This method is not thread-safe -- calling it from multiple threads simultaneously may
+ * lead to interleaved text.
+ * @param input The message to send to the in-call app.
+ */
+ public void write(String input) throws IOException {
+ mPipeToInCall.write(input);
+ mPipeToInCall.flush();
+ }
+
+
+ /**
+ * Reads a string from the in-call app, blocking if there is no data available. Returns
+ * {@code null} if the RTT conversation has been terminated and there is no further data
+ * to read.
+ *
+ * This method is not thread-safe -- calling it from multiple threads simultaneously may
+ * lead to interleaved text.
+ * @return A string containing text entered by the user, or {@code null} if the
+ * conversation has been terminated or if there was an error while reading.
+ */
+ public String read() {
+ try {
+ int numRead = mPipeFromInCall.read(mReadBuffer, 0, READ_BUFFER_SIZE);
+ if (numRead < 0) {
+ return null;
+ }
+ return new String(mReadBuffer, 0, numRead);
+ } catch (IOException e) {
+ Log.w(this, "Exception encountered when reading from InputStreamReader: %s", e);
+ return null;
+ }
+ }
+
+ /** @hide */
+ public ParcelFileDescriptor getFdFromInCall() {
+ return mFdFromInCall;
+ }
+
+ /** @hide */
+ public ParcelFileDescriptor getFdToInCall() {
+ return mFdToInCall;
+ }
+ }
+
+ /**
+ * Provides constants to represent the results of responses to session modify requests sent via
+ * {@link Call#sendRttRequest()}
+ */
+ public static final class RttModifyStatus {
+ /**
+ * Session modify request was successful.
+ */
+ public static final int SESSION_MODIFY_REQUEST_SUCCESS = 1;
+
+ /**
+ * Session modify request failed.
+ */
+ public static final int SESSION_MODIFY_REQUEST_FAIL = 2;
+
+ /**
+ * Session modify request ignored due to invalid parameters.
+ */
+ public static final int SESSION_MODIFY_REQUEST_INVALID = 3;
+
+ /**
+ * Session modify request timed out.
+ */
+ public static final int SESSION_MODIFY_REQUEST_TIMED_OUT = 4;
+
+ /**
+ * Session modify request rejected by remote user.
+ */
+ public static final int SESSION_MODIFY_REQUEST_REJECTED_BY_REMOTE = 5;
}
/**
@@ -936,7 +1059,7 @@
try {
onSetCamera((String) args.arg1);
onSetCamera((String) args.arg1, (String) args.arg2, args.argi1,
- args.argi2);
+ args.argi2, args.argi3);
} finally {
args.recycle();
}
@@ -996,7 +1119,9 @@
MSG_REMOVE_VIDEO_CALLBACK, videoCallbackBinder).sendToTarget();
}
- public void setCamera(String cameraId, String callingPackageName) {
+ public void setCamera(String cameraId, String callingPackageName,
+ int targetSdkVersion) {
+
SomeArgs args = SomeArgs.obtain();
args.arg1 = cameraId;
// Propagate the calling package; originally determined in
@@ -1008,6 +1133,9 @@
// check to see if the calling app is able to use the camera.
args.argi1 = Binder.getCallingUid();
args.argi2 = Binder.getCallingPid();
+ // Pass along the target SDK version of the calling InCallService. This is used to
+ // maintain backwards compatibility of the API for older callers.
+ args.argi3 = targetSdkVersion;
mMessageHandler.obtainMessage(MSG_SET_CAMERA, args).sendToTarget();
}
@@ -1110,10 +1238,11 @@
* @param callingPackageName The AppOpps package name of the caller.
* @param callingUid The UID of the caller.
* @param callingPid The PID of the caller.
+ * @param targetSdkVersion The target SDK version of the caller.
* @hide
*/
public void onSetCamera(String cameraId, String callingPackageName, int callingUid,
- int callingPid) {}
+ int callingPid, int targetSdkVersion) {}
/**
* Sets the surface to be used for displaying a preview of what the user's camera is
@@ -2357,6 +2486,47 @@
}
/**
+ * Informs listeners that a previously requested RTT session via
+ * {@link ConnectionRequest#isRequestingRtt()} or
+ * {@link #onStartRtt(ParcelFileDescriptor, ParcelFileDescriptor)} has succeeded.
+ * @hide
+ */
+ public final void sendRttInitiationSuccess() {
+ mListeners.forEach((l) -> l.onRttInitiationSuccess(Connection.this));
+ }
+
+ /**
+ * Informs listeners that a previously requested RTT session via
+ * {@link ConnectionRequest#isRequestingRtt()} or
+ * {@link #onStartRtt(ParcelFileDescriptor, ParcelFileDescriptor)}
+ * has failed.
+ * @param reason One of the reason codes defined in {@link RttModifyStatus}, with the
+ * exception of {@link RttModifyStatus#SESSION_MODIFY_REQUEST_SUCCESS}.
+ * @hide
+ */
+ public final void sendRttInitiationFailure(int reason) {
+ mListeners.forEach((l) -> l.onRttInitiationFailure(Connection.this, reason));
+ }
+
+ /**
+ * Informs listeners that a currently active RTT session has been terminated by the remote
+ * side of the coll.
+ * @hide
+ */
+ public final void sendRttSessionRemotelyTerminated() {
+ mListeners.forEach((l) -> l.onRttSessionRemotelyTerminated(Connection.this));
+ }
+
+ /**
+ * Informs listeners that the remote side of the call has requested an upgrade to include an
+ * RTT session in the call.
+ * @hide
+ */
+ public final void sendRemoteRttRequest() {
+ mListeners.forEach((l) -> l.onRemoteRttRequest(Connection.this));
+ }
+
+ /**
* Notifies this Connection that the {@link #getAudioState()} property has a new value.
*
* @param state The new connection audio state.
@@ -2523,9 +2693,73 @@
* regular {@link ConnectionService}, the Telecom framework will display its own incoming call
* user interface to allow the user to choose whether to answer the new incoming call and
* disconnect other ongoing calls, or to reject the new incoming call.
+ * <p>
+ * You should trigger the display of the incoming call user interface for your application by
+ * showing a {@link Notification} with a full-screen {@link Intent} specified.
+ * For example:
+ * <pre><code>
+ * // Create an intent which triggers your fullscreen incoming call user interface.
+ * Intent intent = new Intent(Intent.ACTION_MAIN, null);
+ * intent.setFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION | Intent.FLAG_ACTIVITY_NEW_TASK);
+ * intent.setClass(context, YourIncomingCallActivity.class);
+ * PendingIntent pendingIntent = PendingIntent.getActivity(context, 1, intent, 0);
+ *
+ * // Build the notification as an ongoing high priority item; this ensures it will show as
+ * // a heads up notification which slides down over top of the current content.
+ * final Notification.Builder builder = new Notification.Builder(context);
+ * builder.setOngoing(true);
+ * builder.setPriority(Notification.PRIORITY_HIGH);
+ *
+ * // Set notification content intent to take user to fullscreen UI if user taps on the
+ * // notification body.
+ * builder.setContentIntent(pendingIntent);
+ * // Set full screen intent to trigger display of the fullscreen UI when the notification
+ * // manager deems it appropriate.
+ * builder.setFullScreenIntent(pendingIntent, true);
+ *
+ * // Setup notification content.
+ * builder.setSmallIcon( yourIconResourceId );
+ * builder.setContentTitle("Your notification title");
+ * builder.setContentText("Your notification content.");
+ *
+ * // Use builder.addAction(..) to add buttons to answer or reject the call.
+ *
+ * NotificationManager notificationManager = mContext.getSystemService(
+ * NotificationManager.class);
+ * notificationManager.notify(YOUR_TAG, YOUR_ID, builder.build());
+ * </code></pre>
*/
public void onShowIncomingCallUi() {}
+ /**
+ * Notifies this {@link Connection} that the user has requested an RTT session.
+ * The connection service should call {@link #sendRttInitiationSuccess} or
+ * {@link #sendRttInitiationFailure} to inform Telecom of the success or failure of the
+ * request, respectively.
+ * @param rttTextStream The object that should be used to send text to or receive text from
+ * the in-call app.
+ * @hide
+ */
+ public void onStartRtt(@NonNull RttTextStream rttTextStream) {}
+
+ /**
+ * Notifies this {@link Connection} that it should terminate any existing RTT communication
+ * channel. No response to Telecom is needed for this method.
+ * @hide
+ */
+ public void onStopRtt() {}
+
+ /**
+ * Notifies this connection of a response to a previous remotely-initiated RTT upgrade
+ * request sent via {@link #sendRemoteRttRequest}. Acceptance of the request is
+ * indicated by the supplied {@link RttTextStream} being non-null, and rejection is
+ * indicated by {@code rttTextStream} being {@code null}
+ * @hide
+ * @param rttTextStream The object that should be used to send text to or receive text from
+ * the in-call app.
+ */
+ public void handleRttUpgradeResponse(@Nullable RttTextStream rttTextStream) {}
+
static String toLogSafePhoneNumber(String number) {
// For unknown number, log empty string.
if (number == null) {
diff --git a/telecomm/java/android/telecom/ConnectionRequest.java b/telecomm/java/android/telecom/ConnectionRequest.java
index 2343462..054de4c 100644
--- a/telecomm/java/android/telecom/ConnectionRequest.java
+++ b/telecomm/java/android/telecom/ConnectionRequest.java
@@ -19,6 +19,7 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
/**
@@ -27,13 +28,121 @@
*/
public final class ConnectionRequest implements Parcelable {
- // TODO: Token to limit recursive invocations
+ /**
+ * Builder class for {@link ConnectionRequest}
+ * @hide
+ */
+ public static final class Builder {
+ private PhoneAccountHandle mAccountHandle;
+ private Uri mAddress;
+ private Bundle mExtras;
+ private int mVideoState = VideoProfile.STATE_AUDIO_ONLY;
+ private String mTelecomCallId;
+ private boolean mShouldShowIncomingCallUi = false;
+ private ParcelFileDescriptor mRttPipeToInCall;
+ private ParcelFileDescriptor mRttPipeFromInCall;
+
+ public Builder() { }
+
+ /**
+ * Sets the phone account handle for the resulting {@link ConnectionRequest}
+ * @param accountHandle The accountHandle which should be used to place the call.
+ */
+ public Builder setAccountHandle(PhoneAccountHandle accountHandle) {
+ this.mAccountHandle = accountHandle;
+ return this;
+ }
+
+ /**
+ * Sets the address for the resulting {@link ConnectionRequest}
+ * @param address The address(e.g., phone number) to which the {@link Connection} is to
+ * connect.
+ */
+ public Builder setAddress(Uri address) {
+ this.mAddress = address;
+ return this;
+ }
+
+ /**
+ * Sets the extras bundle for the resulting {@link ConnectionRequest}
+ * @param extras Application-specific extra data.
+ */
+ public Builder setExtras(Bundle extras) {
+ this.mExtras = extras;
+ return this;
+ }
+
+ /**
+ * Sets the video state for the resulting {@link ConnectionRequest}
+ * @param videoState Determines the video state for the connection.
+ */
+ public Builder setVideoState(int videoState) {
+ this.mVideoState = videoState;
+ return this;
+ }
+
+ /**
+ * Sets the Telecom call ID for the resulting {@link ConnectionRequest}
+ * @param telecomCallId The telecom call ID.
+ */
+ public Builder setTelecomCallId(String telecomCallId) {
+ this.mTelecomCallId = telecomCallId;
+ return this;
+ }
+
+ /**
+ * Sets shouldShowIncomingUi for the resulting {@link ConnectionRequest}
+ * @param shouldShowIncomingCallUi For a self-managed {@link ConnectionService}, will be
+ * {@code true} if the {@link ConnectionService} should show
+ * its own incoming call UI for an incoming call. When
+ * {@code false}, Telecom shows the incoming call UI.
+ */
+ public Builder setShouldShowIncomingCallUi(boolean shouldShowIncomingCallUi) {
+ this.mShouldShowIncomingCallUi = shouldShowIncomingCallUi;
+ return this;
+ }
+
+ /**
+ * Sets the RTT pipe for transferring text into the {@link ConnectionService} for the
+ * resulting {@link ConnectionRequest}
+ * @param rttPipeFromInCall The data pipe to read from.
+ */
+ public Builder setRttPipeFromInCall(ParcelFileDescriptor rttPipeFromInCall) {
+ this.mRttPipeFromInCall = rttPipeFromInCall;
+ return this;
+ }
+
+ /**
+ * Sets the RTT pipe for transferring text out of {@link ConnectionService} for the
+ * resulting {@link ConnectionRequest}
+ * @param rttPipeToInCall The data pipe to write to.
+ */
+ public Builder setRttPipeToInCall(ParcelFileDescriptor rttPipeToInCall) {
+ this.mRttPipeToInCall = rttPipeToInCall;
+ return this;
+ }
+
+ public ConnectionRequest build() {
+ return new ConnectionRequest(
+ mAccountHandle,
+ mAddress,
+ mExtras,
+ mVideoState,
+ mTelecomCallId,
+ mShouldShowIncomingCallUi,
+ mRttPipeFromInCall,
+ mRttPipeToInCall);
+ }
+ }
+
private final PhoneAccountHandle mAccountHandle;
private final Uri mAddress;
private final Bundle mExtras;
private final int mVideoState;
private final String mTelecomCallId;
private final boolean mShouldShowIncomingCallUi;
+ private final ParcelFileDescriptor mRttPipeToInCall;
+ private final ParcelFileDescriptor mRttPipeFromInCall;
/**
* @param accountHandle The accountHandle which should be used to place the call.
@@ -44,7 +153,7 @@
PhoneAccountHandle accountHandle,
Uri handle,
Bundle extras) {
- this(accountHandle, handle, extras, VideoProfile.STATE_AUDIO_ONLY, null, false);
+ this(accountHandle, handle, extras, VideoProfile.STATE_AUDIO_ONLY, null, false, null, null);
}
/**
@@ -58,7 +167,7 @@
Uri handle,
Bundle extras,
int videoState) {
- this(accountHandle, handle, extras, videoState, null, false);
+ this(accountHandle, handle, extras, videoState, null, false, null, null);
}
/**
@@ -80,12 +189,27 @@
int videoState,
String telecomCallId,
boolean shouldShowIncomingCallUi) {
+ this(accountHandle, handle, extras, videoState, telecomCallId,
+ shouldShowIncomingCallUi, null, null);
+ }
+
+ private ConnectionRequest(
+ PhoneAccountHandle accountHandle,
+ Uri handle,
+ Bundle extras,
+ int videoState,
+ String telecomCallId,
+ boolean shouldShowIncomingCallUi,
+ ParcelFileDescriptor rttPipeFromInCall,
+ ParcelFileDescriptor rttPipeToInCall) {
mAccountHandle = accountHandle;
mAddress = handle;
mExtras = extras;
mVideoState = videoState;
mTelecomCallId = telecomCallId;
mShouldShowIncomingCallUi = shouldShowIncomingCallUi;
+ mRttPipeFromInCall = rttPipeFromInCall;
+ mRttPipeToInCall = rttPipeToInCall;
}
private ConnectionRequest(Parcel in) {
@@ -95,6 +219,8 @@
mVideoState = in.readInt();
mTelecomCallId = in.readString();
mShouldShowIncomingCallUi = in.readInt() == 1;
+ mRttPipeFromInCall = in.readParcelable(getClass().getClassLoader());
+ mRttPipeToInCall = in.readParcelable(getClass().getClassLoader());
}
/**
@@ -149,6 +275,59 @@
return mShouldShowIncomingCallUi;
}
+ /**
+ * Gets the {@link ParcelFileDescriptor} that is used to send RTT text from the connection
+ * service to the in-call UI. In order to obtain an
+ * {@link java.io.InputStream} from this {@link ParcelFileDescriptor}, use
+ * {@link android.os.ParcelFileDescriptor.AutoCloseInputStream}.
+ * Only text data encoded using UTF-8 should be written into this {@link ParcelFileDescriptor}.
+ * @return The {@link ParcelFileDescriptor} that should be used for communication.
+ * Do not un-hide -- only for use by Telephony
+ * @hide
+ */
+ public ParcelFileDescriptor getRttPipeToInCall() {
+ return mRttPipeToInCall;
+ }
+
+ /**
+ * Gets the {@link ParcelFileDescriptor} that is used to send RTT text from the in-call UI to
+ * the connection service. In order to obtain an
+ * {@link java.io.OutputStream} from this {@link ParcelFileDescriptor}, use
+ * {@link android.os.ParcelFileDescriptor.AutoCloseOutputStream}.
+ * The contents of this {@link ParcelFileDescriptor} will consist solely of text encoded in
+ * UTF-8.
+ * @return The {@link ParcelFileDescriptor} that should be used for communication
+ * Do not un-hide -- only for use by Telephony
+ * @hide
+ */
+ public ParcelFileDescriptor getRttPipeFromInCall() {
+ return mRttPipeFromInCall;
+ }
+
+ /**
+ * Gets the {@link android.telecom.Connection.RttTextStream} object that should be used to
+ * send and receive RTT text to/from the in-call app.
+ * @return An instance of {@link android.telecom.Connection.RttTextStream}, or {@code null}
+ * if this connection request is not requesting an RTT session upon connection establishment.
+ * @hide
+ */
+ public Connection.RttTextStream getRttTextStream() {
+ if (isRequestingRtt()) {
+ return new Connection.RttTextStream(mRttPipeToInCall, mRttPipeFromInCall);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Convenience method for determining whether the ConnectionRequest is requesting an RTT session
+ * @return {@code true} if RTT is requested, {@code false} otherwise.
+ * @hide
+ */
+ public boolean isRequestingRtt() {
+ return mRttPipeFromInCall != null && mRttPipeToInCall != null;
+ }
+
@Override
public String toString() {
return String.format("ConnectionRequest %s %s",
@@ -186,5 +365,7 @@
destination.writeInt(mVideoState);
destination.writeString(mTelecomCallId);
destination.writeInt(mShouldShowIncomingCallUi ? 1 : 0);
+ destination.writeParcelable(mRttPipeFromInCall, 0);
+ destination.writeParcelable(mRttPipeToInCall, 0);
}
}
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 6e10029..bf8f8e4 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -26,6 +26,8 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
import android.telecom.Logging.Session;
import com.android.internal.os.SomeArgs;
@@ -119,6 +121,9 @@
private static final String SESSION_PULL_EXTERNAL_CALL = "CS.pEC";
private static final String SESSION_SEND_CALL_EVENT = "CS.sCE";
private static final String SESSION_EXTRAS_CHANGED = "CS.oEC";
+ private static final String SESSION_START_RTT = "CS.+RTT";
+ private static final String SESSION_STOP_RTT = "CS.-RTT";
+ private static final String SESSION_RTT_UPGRADE_RESPONSE = "CS.rTRUR";
private static final int MSG_ADD_CONNECTION_SERVICE_ADAPTER = 1;
private static final int MSG_CREATE_CONNECTION = 2;
@@ -144,6 +149,9 @@
private static final int MSG_SEND_CALL_EVENT = 23;
private static final int MSG_ON_EXTRAS_CHANGED = 24;
private static final int MSG_CREATE_CONNECTION_FAILED = 25;
+ private static final int MSG_ON_START_RTT = 26;
+ private static final int MSG_ON_STOP_RTT = 27;
+ private static final int MSG_RTT_UPGRADE_RESPONSE = 28;
private static Connection sNullConnection;
@@ -214,6 +222,7 @@
@Override
public void createConnectionFailed(
+ PhoneAccountHandle connectionManagerPhoneAccount,
String callId,
ConnectionRequest request,
boolean isIncoming,
@@ -224,6 +233,7 @@
args.arg1 = callId;
args.arg2 = request;
args.arg3 = Log.createSubsession();
+ args.arg4 = connectionManagerPhoneAccount;
args.argi1 = isIncoming ? 1 : 0;
mHandler.obtainMessage(MSG_CREATE_CONNECTION_FAILED, args).sendToTarget();
} finally {
@@ -501,6 +511,53 @@
Log.endSession();
}
}
+
+ @Override
+ public void startRtt(String callId, ParcelFileDescriptor fromInCall,
+ ParcelFileDescriptor toInCall, Session.Info sessionInfo) throws RemoteException {
+ Log.startSession(sessionInfo, SESSION_START_RTT);
+ try {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = new Connection.RttTextStream(toInCall, fromInCall);
+ args.arg3 = Log.createSubsession();
+ mHandler.obtainMessage(MSG_ON_START_RTT, args).sendToTarget();
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
+ public void stopRtt(String callId, Session.Info sessionInfo) throws RemoteException {
+ Log.startSession(sessionInfo, SESSION_STOP_RTT);
+ try {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = Log.createSubsession();
+ mHandler.obtainMessage(MSG_ON_STOP_RTT, args).sendToTarget();
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
+ public void respondToRttUpgradeRequest(String callId, ParcelFileDescriptor fromInCall,
+ ParcelFileDescriptor toInCall, Session.Info sessionInfo) throws RemoteException {
+ Log.startSession(sessionInfo, SESSION_RTT_UPGRADE_RESPONSE);
+ try {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ if (toInCall == null || fromInCall == null) {
+ args.arg2 = null;
+ } else {
+ args.arg2 = new Connection.RttTextStream(toInCall, fromInCall);
+ }
+ args.arg3 = Log.createSubsession();
+ mHandler.obtainMessage(MSG_RTT_UPGRADE_RESPONSE, args).sendToTarget();
+ } finally {
+ Log.endSession();
+ }
+ }
};
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@@ -581,6 +638,8 @@
final String id = (String) args.arg1;
final ConnectionRequest request = (ConnectionRequest) args.arg2;
final boolean isIncoming = args.argi1 == 1;
+ final PhoneAccountHandle connectionMgrPhoneAccount =
+ (PhoneAccountHandle) args.arg4;
if (!mAreAccountsInitialized) {
Log.d(this, "Enqueueing pre-init request %s", id);
mPreInitializationConnectionRequests.add(
@@ -589,12 +648,14 @@
null /*lock*/) {
@Override
public void loggedRun() {
- createConnectionFailed(id, request, isIncoming);
+ createConnectionFailed(connectionMgrPhoneAccount, id,
+ request, isIncoming);
}
}.prepare());
} else {
Log.i(this, "createConnectionFailed %s", id);
- createConnectionFailed(id, request, isIncoming);
+ createConnectionFailed(connectionMgrPhoneAccount, id, request,
+ isIncoming);
}
} finally {
args.recycle();
@@ -848,6 +909,49 @@
}
break;
}
+ case MSG_ON_START_RTT: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ Log.continueSession((Session) args.arg3,
+ SESSION_HANDLER + SESSION_START_RTT);
+ String callId = (String) args.arg1;
+ Connection.RttTextStream rttTextStream =
+ (Connection.RttTextStream) args.arg2;
+ startRtt(callId, rttTextStream);
+ } finally {
+ args.recycle();
+ Log.endSession();
+ }
+ break;
+ }
+ case MSG_ON_STOP_RTT: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ Log.continueSession((Session) args.arg2,
+ SESSION_HANDLER + SESSION_STOP_RTT);
+ String callId = (String) args.arg1;
+ stopRtt(callId);
+ } finally {
+ args.recycle();
+ Log.endSession();
+ }
+ break;
+ }
+ case MSG_RTT_UPGRADE_RESPONSE: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ Log.continueSession((Session) args.arg3,
+ SESSION_HANDLER + SESSION_RTT_UPGRADE_RESPONSE);
+ String callId = (String) args.arg1;
+ Connection.RttTextStream rttTextStream =
+ (Connection.RttTextStream) args.arg2;
+ handleRttUpgradeResponse(callId, rttTextStream);
+ } finally {
+ args.recycle();
+ Log.endSession();
+ }
+ break;
+ }
default:
break;
}
@@ -1136,6 +1240,38 @@
mAdapter.setAudioRoute(id, audioRoute);
}
}
+
+ @Override
+ public void onRttInitiationSuccess(Connection c) {
+ String id = mIdByConnection.get(c);
+ if (id != null) {
+ mAdapter.onRttInitiationSuccess(id);
+ }
+ }
+
+ @Override
+ public void onRttInitiationFailure(Connection c, int reason) {
+ String id = mIdByConnection.get(c);
+ if (id != null) {
+ mAdapter.onRttInitiationFailure(id, reason);
+ }
+ }
+
+ @Override
+ public void onRttSessionRemotelyTerminated(Connection c) {
+ String id = mIdByConnection.get(c);
+ if (id != null) {
+ mAdapter.onRttSessionRemotelyTerminated(id);
+ }
+ }
+
+ @Override
+ public void onRemoteRttRequest(Connection c) {
+ String id = mIdByConnection.get(c);
+ if (id != null) {
+ mAdapter.onRemoteRttRequest(id);
+ }
+ }
};
/** {@inheritDoc} */
@@ -1225,14 +1361,15 @@
}
}
- private void createConnectionFailed(final String callId, final ConnectionRequest request,
- boolean isIncoming) {
+ private void createConnectionFailed(final PhoneAccountHandle callManagerAccount,
+ final String callId, final ConnectionRequest request,
+ boolean isIncoming) {
Log.i(this, "createConnectionFailed %s", callId);
if (isIncoming) {
- onCreateIncomingConnectionFailed(request);
+ onCreateIncomingConnectionFailed(callManagerAccount, request);
} else {
- onCreateOutgoingConnectionFailed(request);
+ onCreateOutgoingConnectionFailed(callManagerAccount, request);
}
}
@@ -1430,7 +1567,6 @@
if (connection != null) {
connection.onCallEvent(event, extras);
}
-
}
/**
@@ -1454,6 +1590,34 @@
}
}
+ private void startRtt(String callId, Connection.RttTextStream rttTextStream) {
+ Log.d(this, "startRtt(%s)", callId);
+ if (mConnectionById.containsKey(callId)) {
+ findConnectionForAction(callId, "startRtt").onStartRtt(rttTextStream);
+ } else if (mConferenceById.containsKey(callId)) {
+ Log.w(this, "startRtt called on a conference.");
+ }
+ }
+
+ private void stopRtt(String callId) {
+ Log.d(this, "stopRtt(%s)", callId);
+ if (mConnectionById.containsKey(callId)) {
+ findConnectionForAction(callId, "stopRtt").onStopRtt();
+ } else if (mConferenceById.containsKey(callId)) {
+ Log.w(this, "stopRtt called on a conference.");
+ }
+ }
+
+ private void handleRttUpgradeResponse(String callId, Connection.RttTextStream rttTextStream) {
+ Log.d(this, "handleRttUpgradeResponse(%s, %s)", callId, rttTextStream == null);
+ if (mConnectionById.containsKey(callId)) {
+ findConnectionForAction(callId, "handleRttUpgradeResponse")
+ .handleRttUpgradeResponse(rttTextStream);
+ } else if (mConferenceById.containsKey(callId)) {
+ Log.w(this, "handleRttUpgradeResponse called on a conference.");
+ }
+ }
+
private void onPostDialContinue(String callId, boolean proceed) {
Log.d(this, "onPostDialContinue(%s)", callId);
findConnectionForAction(callId, "stopDtmfTone").onPostDialContinue(proceed);
@@ -1682,9 +1846,12 @@
* <p>
* See {@link TelecomManager#isIncomingCallPermitted(PhoneAccountHandle)} for more information.
*
+ * @param connectionManagerPhoneAccount See description at
+ * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
* @param request The incoming connection request.
*/
- public void onCreateIncomingConnectionFailed(ConnectionRequest request) {
+ public void onCreateIncomingConnectionFailed(PhoneAccountHandle connectionManagerPhoneAccount,
+ ConnectionRequest request) {
}
/**
@@ -1698,9 +1865,12 @@
* <p>
* See {@link TelecomManager#isOutgoingCallPermitted(PhoneAccountHandle)} for more information.
*
+ * @param connectionManagerPhoneAccount See description at
+ * {@link #onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)}.
* @param request The outgoing connection request.
*/
- public void onCreateOutgoingConnectionFailed(ConnectionRequest request) {
+ public void onCreateOutgoingConnectionFailed(PhoneAccountHandle connectionManagerPhoneAccount,
+ ConnectionRequest request) {
}
/**
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapter.java b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
index 9542b73..63bdf74 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapter.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
@@ -547,4 +547,66 @@
}
}
}
+
+ /**
+ * Notifies Telecom that an RTT session was successfully established.
+ *
+ * @param callId The unique ID of the call.
+ */
+ void onRttInitiationSuccess(String callId) {
+ Log.v(this, "onRttInitiationSuccess: %s", callId);
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ adapter.onRttInitiationSuccess(callId, Log.getExternalSession());
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ /**
+ * Notifies Telecom that a requested RTT session failed to be established.
+ *
+ * @param callId The unique ID of the call.
+ */
+ void onRttInitiationFailure(String callId, int reason) {
+ Log.v(this, "onRttInitiationFailure: %s", callId);
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ adapter.onRttInitiationFailure(callId, reason, Log.getExternalSession());
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ /**
+ * Notifies Telecom that an established RTT session was terminated by the remote user on
+ * the call.
+ *
+ * @param callId The unique ID of the call.
+ */
+ void onRttSessionRemotelyTerminated(String callId) {
+ Log.v(this, "onRttSessionRemotelyTerminated: %s", callId);
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ adapter.onRttSessionRemotelyTerminated(callId, Log.getExternalSession());
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ /**
+ * Notifies Telecom that the remote user on the call has requested an upgrade to an RTT
+ * session for this call.
+ *
+ * @param callId The unique ID of the call.
+ */
+ void onRemoteRttRequest(String callId) {
+ Log.v(this, "onRemoteRttRequest: %s", callId);
+ for (IConnectionServiceAdapter adapter : mAdapters) {
+ try {
+ adapter.onRemoteRttRequest(callId, Log.getExternalSession());
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
}
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
index cc437f9..80e3c33 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
@@ -68,6 +68,10 @@
private static final int MSG_SET_CONNECTION_PROPERTIES = 27;
private static final int MSG_SET_PULLING = 28;
private static final int MSG_SET_AUDIO_ROUTE = 29;
+ private static final int MSG_ON_RTT_INITIATION_SUCCESS = 30;
+ private static final int MSG_ON_RTT_INITIATION_FAILURE = 31;
+ private static final int MSG_ON_RTT_REMOTELY_TERMINATED = 32;
+ private static final int MSG_ON_RTT_UPGRADE_REQUEST = 33;
private final IConnectionServiceAdapter mDelegate;
@@ -300,6 +304,20 @@
}
break;
}
+ case MSG_ON_RTT_INITIATION_SUCCESS:
+ mDelegate.onRttInitiationSuccess((String) msg.obj, null /*Session.Info*/);
+ break;
+ case MSG_ON_RTT_INITIATION_FAILURE:
+ mDelegate.onRttInitiationFailure((String) msg.obj, msg.arg1,
+ null /*Session.Info*/);
+ break;
+ case MSG_ON_RTT_REMOTELY_TERMINATED:
+ mDelegate.onRttSessionRemotelyTerminated((String) msg.obj,
+ null /*Session.Info*/);
+ break;
+ case MSG_ON_RTT_UPGRADE_REQUEST:
+ mDelegate.onRemoteRttRequest((String) msg.obj, null /*Session.Info*/);
+ break;
}
}
};
@@ -537,6 +555,32 @@
args.arg3 = extras;
mHandler.obtainMessage(MSG_ON_CONNECTION_EVENT, args).sendToTarget();
}
+
+ @Override
+ public void onRttInitiationSuccess(String connectionId, Session.Info sessionInfo)
+ throws RemoteException {
+ mHandler.obtainMessage(MSG_ON_RTT_INITIATION_SUCCESS, connectionId).sendToTarget();
+ }
+
+ @Override
+ public void onRttInitiationFailure(String connectionId, int reason,
+ Session.Info sessionInfo)
+ throws RemoteException {
+ mHandler.obtainMessage(MSG_ON_RTT_INITIATION_FAILURE, reason, 0, connectionId)
+ .sendToTarget();
+ }
+
+ @Override
+ public void onRttSessionRemotelyTerminated(String connectionId, Session.Info sessionInfo)
+ throws RemoteException {
+ mHandler.obtainMessage(MSG_ON_RTT_REMOTELY_TERMINATED, connectionId).sendToTarget();
+ }
+
+ @Override
+ public void onRemoteRttRequest(String connectionId, Session.Info sessionInfo)
+ throws RemoteException {
+ mHandler.obtainMessage(MSG_ON_RTT_UPGRADE_REQUEST, connectionId).sendToTarget();
+ }
};
public ConnectionServiceAdapterServant(IConnectionServiceAdapter delegate) {
diff --git a/telecomm/java/android/telecom/InCallAdapter.java b/telecomm/java/android/telecom/InCallAdapter.java
index 3f270d9..9559a28 100644
--- a/telecomm/java/android/telecom/InCallAdapter.java
+++ b/telecomm/java/android/telecom/InCallAdapter.java
@@ -34,7 +34,7 @@
* <p>
* The adapter will stop functioning when there are no more calls.
*
- * {@hide}
+ * @hide
*/
public final class InCallAdapter {
private final IInCallAdapter mAdapter;
@@ -375,4 +375,48 @@
} catch (RemoteException ignored) {
}
}
+
+ /**
+ * Sends an RTT upgrade request to the remote end of the connection.
+ */
+ public void sendRttRequest(String callId) {
+ try {
+ mAdapter.sendRttRequest(callId);
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
+ * Responds to an RTT upgrade request initiated from the remote end.
+ *
+ * @param id the ID of the request as specified by Telecom
+ * @param accept Whether the request should be accepted.
+ */
+ public void respondToRttRequest(String callId, int id, boolean accept) {
+ try {
+ mAdapter.respondToRttRequest(callId, id, accept);
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
+ * Instructs Telecom to shut down the RTT communication channel.
+ */
+ public void stopRtt(String callId) {
+ try {
+ mAdapter.stopRtt(callId);
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
+ * Sets the RTT audio mode.
+ * @param mode the desired RTT audio mode
+ */
+ public void setRttMode(String callId, int mode) {
+ try {
+ mAdapter.setRttMode(callId, mode);
+ } catch (RemoteException ignored) {
+ }
+ }
}
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index 5d68aae..e384d46 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -76,6 +76,8 @@
private static final int MSG_ON_CAN_ADD_CALL_CHANGED = 7;
private static final int MSG_SILENCE_RINGER = 8;
private static final int MSG_ON_CONNECTION_EVENT = 9;
+ private static final int MSG_ON_RTT_UPGRADE_REQUEST = 10;
+ private static final int MSG_ON_RTT_INITIATION_FAILURE = 11;
/** Default Handler used to consolidate binder method calls onto a single thread. */
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@@ -88,7 +90,8 @@
switch (msg.what) {
case MSG_SET_IN_CALL_ADAPTER:
String callingPackage = getApplicationContext().getOpPackageName();
- mPhone = new Phone(new InCallAdapter((IInCallAdapter) msg.obj), callingPackage);
+ mPhone = new Phone(new InCallAdapter((IInCallAdapter) msg.obj), callingPackage,
+ getApplicationContext().getApplicationInfo().targetSdkVersion);
mPhone.addListener(mPhoneListener);
onPhoneCreated(mPhone);
break;
@@ -133,6 +136,18 @@
}
break;
}
+ case MSG_ON_RTT_UPGRADE_REQUEST: {
+ String callId = (String) msg.obj;
+ int requestId = msg.arg1;
+ mPhone.internalOnRttUpgradeRequest(callId, requestId);
+ break;
+ }
+ case MSG_ON_RTT_INITIATION_FAILURE: {
+ String callId = (String) msg.obj;
+ int reason = msg.arg1;
+ mPhone.internalOnRttInitiationFailure(callId, reason);
+ break;
+ }
default:
break;
}
@@ -198,6 +213,16 @@
args.arg3 = extras;
mHandler.obtainMessage(MSG_ON_CONNECTION_EVENT, args).sendToTarget();
}
+
+ @Override
+ public void onRttUpgradeRequest(String callId, int id) {
+ mHandler.obtainMessage(MSG_ON_RTT_UPGRADE_REQUEST, id, 0, callId).sendToTarget();
+ }
+
+ @Override
+ public void onRttInitiationFailure(String callId, int reason) {
+ mHandler.obtainMessage(MSG_ON_RTT_INITIATION_FAILURE, reason, 0, callId).sendToTarget();
+ }
}
private Phone.Listener mPhoneListener = new Phone.Listener() {
diff --git a/telecomm/java/android/telecom/Log.java b/telecomm/java/android/telecom/Log.java
index ced6627..6107895 100644
--- a/telecomm/java/android/telecom/Log.java
+++ b/telecomm/java/android/telecom/Log.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.net.Uri;
import android.os.AsyncTask;
+import android.os.Build;
import android.telecom.Logging.EventManager;
import android.telecom.Logging.Session;
import android.telecom.Logging.SessionManager;
@@ -55,6 +56,7 @@
public static boolean ERROR = isLoggable(android.util.Log.ERROR);
private static final boolean FORCE_LOGGING = false; /* STOP SHIP if true */
+ private static final boolean USER_BUILD = Build.TYPE.equals("user");
// Used to synchronize singleton logging lazy initialization
private static final Object sSingletonSync = new Object();
@@ -404,7 +406,8 @@
/**
* Redact personally identifiable information for production users.
- * If we are running in verbose mode, return the original string, otherwise
+ * If we are running in verbose mode, return the original string,
+ * and return "****" if we are running on the user build, otherwise
* return a SHA-1 hash of the input string.
*/
public static String pii(Object pii) {
@@ -415,6 +418,11 @@
}
private static String secureHash(byte[] input) {
+ // Refrain from logging user personal information in user build.
+ if (USER_BUILD) {
+ return "****";
+ }
+
if (sMessageDigest != null) {
sMessageDigest.reset();
sMessageDigest.update(input);
diff --git a/telecomm/java/android/telecom/ParcelableCall.java b/telecomm/java/android/telecom/ParcelableCall.java
index a3fce9c..85a92d1 100644
--- a/telecomm/java/android/telecom/ParcelableCall.java
+++ b/telecomm/java/android/telecom/ParcelableCall.java
@@ -50,6 +50,8 @@
private final boolean mIsVideoCallProviderChanged;
private final IVideoProvider mVideoCallProvider;
private VideoCallImpl mVideoCall;
+ private final boolean mIsRttCallChanged;
+ private final ParcelableRttCall mRttCall;
private final String mParentCallId;
private final List<String> mChildCallIds;
private final StatusHints mStatusHints;
@@ -75,6 +77,8 @@
PhoneAccountHandle accountHandle,
boolean isVideoCallProviderChanged,
IVideoProvider videoCallProvider,
+ boolean isRttCallChanged,
+ ParcelableRttCall rttCall,
String parentCallId,
List<String> childCallIds,
StatusHints statusHints,
@@ -98,6 +102,8 @@
mAccountHandle = accountHandle;
mIsVideoCallProviderChanged = isVideoCallProviderChanged;
mVideoCallProvider = videoCallProvider;
+ mIsRttCallChanged = isRttCallChanged;
+ mRttCall = rttCall;
mParentCallId = parentCallId;
mChildCallIds = childCallIds;
mStatusHints = statusHints;
@@ -187,13 +193,16 @@
/**
* Returns an object for remotely communicating through the video call provider's binder.
-
+ *
+ * @param callingPackageName the package name of the calling InCallService.
+ * @param targetSdkVersion the target SDK version of the calling InCallService.
* @return The video call.
*/
- public VideoCallImpl getVideoCallImpl(String callingPackageName) {
+ public VideoCallImpl getVideoCallImpl(String callingPackageName, int targetSdkVersion) {
if (mVideoCall == null && mVideoCallProvider != null) {
try {
- mVideoCall = new VideoCallImpl(mVideoCallProvider, callingPackageName);
+ mVideoCall = new VideoCallImpl(mVideoCallProvider, callingPackageName,
+ targetSdkVersion);
} catch (RemoteException ignored) {
// Ignore RemoteException.
}
@@ -202,6 +211,18 @@
return mVideoCall;
}
+ public boolean getIsRttCallChanged() {
+ return mIsRttCallChanged;
+ }
+
+ /**
+ * RTT communication channel information
+ * @return The ParcelableRttCall
+ */
+ public ParcelableRttCall getParcelableRttCall() {
+ return mRttCall;
+ }
+
/**
* The conference call to which this call is conferenced. Null if not conferenced.
*/
@@ -301,6 +322,8 @@
Bundle intentExtras = source.readBundle(classLoader);
Bundle extras = source.readBundle(classLoader);
int supportedAudioRoutes = source.readInt();
+ boolean isRttCallChanged = source.readByte() == 1;
+ ParcelableRttCall rttCall = source.readParcelable(classLoader);
return new ParcelableCall(
id,
state,
@@ -318,6 +341,8 @@
accountHandle,
isVideoCallProviderChanged,
videoCallProvider,
+ isRttCallChanged,
+ rttCall,
parentCallId,
childCallIds,
statusHints,
@@ -366,6 +391,8 @@
destination.writeBundle(mIntentExtras);
destination.writeBundle(mExtras);
destination.writeInt(mSupportedAudioRoutes);
+ destination.writeByte((byte) (mIsRttCallChanged ? 1 : 0));
+ destination.writeParcelable(mRttCall, 0);
}
@Override
diff --git a/telecomm/java/android/telecom/ParcelableRttCall.aidl b/telecomm/java/android/telecom/ParcelableRttCall.aidl
new file mode 100644
index 0000000..4480710
--- /dev/null
+++ b/telecomm/java/android/telecom/ParcelableRttCall.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2017, 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;
+
+/**
+ * {@hide}
+ */
+parcelable ParcelableRttCall;
diff --git a/telecomm/java/android/telecom/ParcelableRttCall.java b/telecomm/java/android/telecom/ParcelableRttCall.java
new file mode 100644
index 0000000..763e48b
--- /dev/null
+++ b/telecomm/java/android/telecom/ParcelableRttCall.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2017 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.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * Data container for information associated with the RTT connection on a call.
+ * @hide
+ */
+public class ParcelableRttCall implements Parcelable {
+ private final int mRttMode;
+ private final ParcelFileDescriptor mTransmitStream;
+ private final ParcelFileDescriptor mReceiveStream;
+
+ public ParcelableRttCall(
+ int rttMode,
+ ParcelFileDescriptor transmitStream,
+ ParcelFileDescriptor receiveStream) {
+ mRttMode = rttMode;
+ mTransmitStream = transmitStream;
+ mReceiveStream = receiveStream;
+ }
+
+ protected ParcelableRttCall(Parcel in) {
+ mRttMode = in.readInt();
+ mTransmitStream = in.readParcelable(ParcelFileDescriptor.class.getClassLoader());
+ mReceiveStream = in.readParcelable(ParcelFileDescriptor.class.getClassLoader());
+ }
+
+ public static final Creator<ParcelableRttCall> CREATOR = new Creator<ParcelableRttCall>() {
+ @Override
+ public ParcelableRttCall createFromParcel(Parcel in) {
+ return new ParcelableRttCall(in);
+ }
+
+ @Override
+ public ParcelableRttCall[] newArray(int size) {
+ return new ParcelableRttCall[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mRttMode);
+ dest.writeParcelable(mTransmitStream, flags);
+ dest.writeParcelable(mReceiveStream, flags);
+ }
+
+ public int getRttMode() {
+ return mRttMode;
+ }
+
+ public ParcelFileDescriptor getReceiveStream() {
+ return mReceiveStream;
+ }
+
+ public ParcelFileDescriptor getTransmitStream() {
+ return mTransmitStream;
+ }
+}
diff --git a/telecomm/java/android/telecom/Phone.java b/telecomm/java/android/telecom/Phone.java
index 30ec5b3..066f6c2 100644
--- a/telecomm/java/android/telecom/Phone.java
+++ b/telecomm/java/android/telecom/Phone.java
@@ -127,14 +127,20 @@
private final String mCallingPackage;
- Phone(InCallAdapter adapter, String callingPackage) {
+ /**
+ * The Target SDK version of the InCallService implementation.
+ */
+ private final int mTargetSdkVersion;
+
+ Phone(InCallAdapter adapter, String callingPackage, int targetSdkVersion) {
mInCallAdapter = adapter;
mCallingPackage = callingPackage;
+ mTargetSdkVersion = targetSdkVersion;
}
final void internalAddCall(ParcelableCall parcelableCall) {
Call call = new Call(this, parcelableCall.getId(), mInCallAdapter,
- parcelableCall.getState(), mCallingPackage);
+ parcelableCall.getState(), mCallingPackage, mTargetSdkVersion);
mCallByTelecomCallId.put(parcelableCall.getId(), call);
mCalls.add(call);
checkCallTree(parcelableCall);
@@ -201,6 +207,20 @@
}
}
+ final void internalOnRttUpgradeRequest(String callId, int requestId) {
+ Call call = mCallByTelecomCallId.get(callId);
+ if (call != null) {
+ call.internalOnRttUpgradeRequest(requestId);
+ }
+ }
+
+ final void internalOnRttInitiationFailure(String callId, int reason) {
+ Call call = mCallByTelecomCallId.get(callId);
+ if (call != null) {
+ call.internalOnRttInitiationFailure(reason);
+ }
+ }
+
/**
* Called to destroy the phone and cleanup any lingering calls.
*/
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index 845a103..3926e20 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -204,6 +204,18 @@
public static final int CAPABILITY_SELF_MANAGED = 0x800;
/**
+ * Flag indicating that this {@link PhoneAccount} is capable of making a call with an
+ * RTT (Real-time text) session.
+ * When set, Telecom will attempt to open an RTT session on outgoing calls that specify
+ * that they should be placed with an RTT session , and the in-call app will be displayed
+ * with text entry fields for RTT. Likewise, the in-call app can request that an RTT
+ * session be opened during a call if this bit is set.
+ */
+ public static final int CAPABILITY_RTT = 0x1000;
+
+ /* NEXT CAPABILITY: 0x2000 */
+
+ /**
* URI scheme for telephone number URIs.
*/
public static final String SCHEME_TEL = "tel";
diff --git a/telecomm/java/android/telecom/RemoteConnection.java b/telecomm/java/android/telecom/RemoteConnection.java
index 77e0e54..57fc9ce 100644
--- a/telecomm/java/android/telecom/RemoteConnection.java
+++ b/telecomm/java/android/telecom/RemoteConnection.java
@@ -20,6 +20,7 @@
import com.android.internal.telecom.IVideoCallback;
import com.android.internal.telecom.IVideoProvider;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.hardware.camera2.CameraManager;
@@ -231,6 +232,41 @@
* @param extras Extras associated with the event.
*/
public void onConnectionEvent(RemoteConnection connection, String event, Bundle extras) {}
+
+ /**
+ * Indicates that a RTT session was successfully established on this
+ * {@link RemoteConnection}. See {@link Connection#sendRttInitiationSuccess()}.
+ * @hide
+ * @param connection The {@code RemoteConnection} invoking this method.
+ */
+ public void onRttInitiationSuccess(RemoteConnection connection) {}
+
+ /**
+ * Indicates that a RTT session failed to be established on this
+ * {@link RemoteConnection}. See {@link Connection#sendRttInitiationFailure()}.
+ * @hide
+ * @param connection The {@code RemoteConnection} invoking this method.
+ * @param reason One of the reason codes defined in {@link Connection.RttModifyStatus},
+ * with the exception of
+ * {@link Connection.RttModifyStatus#SESSION_MODIFY_REQUEST_SUCCESS}.
+ */
+ public void onRttInitiationFailure(RemoteConnection connection, int reason) {}
+
+ /**
+ * Indicates that an established RTT session was terminated remotely on this
+ * {@link RemoteConnection}. See {@link Connection#sendRttSessionRemotelyTerminated()}
+ * @hide
+ * @param connection The {@code RemoteConnection} invoking this method.
+ */
+ public void onRttSessionRemotelyTerminated(RemoteConnection connection) {}
+
+ /**
+ * Indicates that the remote user on this {@link RemoteConnection} has requested an upgrade
+ * to an RTT session. See {@link Connection#sendRemoteRttRequest()}
+ * @hide
+ * @param connection The {@code RemoteConnection} invoking this method.
+ */
+ public void onRemoteRttRequest(RemoteConnection connection) {}
}
/**
@@ -410,6 +446,8 @@
private final String mCallingPackage;
+ private final int mTargetSdkVersion;
+
/**
* ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is
* load factor before resizing, 1 means we only expect a single thread to
@@ -418,9 +456,12 @@
private final Set<Callback> mCallbacks = Collections.newSetFromMap(
new ConcurrentHashMap<Callback, Boolean>(8, 0.9f, 1));
- VideoProvider(IVideoProvider videoProviderBinder, String callingPackage) {
+ VideoProvider(IVideoProvider videoProviderBinder, String callingPackage,
+ int targetSdkVersion) {
+
mVideoProviderBinder = videoProviderBinder;
mCallingPackage = callingPackage;
+ mTargetSdkVersion = targetSdkVersion;
try {
mVideoProviderBinder.addVideoCallback(mVideoCallbackServant.getStub().asBinder());
} catch (RemoteException e) {
@@ -455,7 +496,7 @@
*/
public void setCamera(String cameraId) {
try {
- mVideoProviderBinder.setCamera(cameraId, mCallingPackage);
+ mVideoProviderBinder.setCamera(cameraId, mCallingPackage, mTargetSdkVersion);
} catch (RemoteException e) {
}
}
@@ -631,7 +672,7 @@
* @hide
*/
RemoteConnection(String callId, IConnectionService connectionService,
- ParcelableConnection connection, String callingPackage) {
+ ParcelableConnection connection, String callingPackage, int targetSdkVersion) {
mConnectionId = callId;
mConnectionService = connectionService;
mConnected = true;
@@ -643,7 +684,8 @@
mVideoState = connection.getVideoState();
IVideoProvider videoProvider = connection.getVideoProvider();
if (videoProvider != null) {
- mVideoProvider = new RemoteConnection.VideoProvider(videoProvider, callingPackage);
+ mVideoProvider = new RemoteConnection.VideoProvider(videoProvider, callingPackage,
+ targetSdkVersion);
} else {
mVideoProvider = null;
}
@@ -1046,6 +1088,61 @@
}
/**
+ * Notifies this {@link RemoteConnection} that the user has requested an RTT session.
+ * @param rttTextStream The object that should be used to send text to or receive text from
+ * the in-call app.
+ * @hide
+ */
+ public void startRtt(@NonNull Connection.RttTextStream rttTextStream) {
+ try {
+ if (mConnected) {
+ mConnectionService.startRtt(mConnectionId, rttTextStream.getFdFromInCall(),
+ rttTextStream.getFdToInCall(), null /*Session.Info*/);
+ }
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
+ * Notifies this {@link RemoteConnection} that it should terminate any existing RTT
+ * session. No response to Telecom is needed for this method.
+ * @hide
+ */
+ public void stopRtt() {
+ try {
+ if (mConnected) {
+ mConnectionService.stopRtt(mConnectionId, null /*Session.Info*/);
+ }
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
+ * Notifies this {@link RemoteConnection} of a response to a previous remotely-initiated RTT
+ * upgrade request sent via {@link Connection#sendRemoteRttRequest}.
+ * Acceptance of the request is indicated by the supplied {@link RttTextStream} being non-null,
+ * and rejection is indicated by {@code rttTextStream} being {@code null}
+ * @hide
+ * @param rttTextStream The object that should be used to send text to or receive text from
+ * the in-call app.
+ */
+ public void sendRttUpgradeResponse(@Nullable Connection.RttTextStream rttTextStream) {
+ try {
+ if (mConnected) {
+ if (rttTextStream == null) {
+ mConnectionService.respondToRttUpgradeRequest(mConnectionId,
+ null, null, null /*Session.Info*/);
+ } else {
+ mConnectionService.respondToRttUpgradeRequest(mConnectionId,
+ rttTextStream.getFdFromInCall(), rttTextStream.getFdToInCall(),
+ null /*Session.Info*/);
+ }
+ }
+ } catch (RemoteException ignored) {
+ }
+ }
+
+ /**
* Obtain the {@code RemoteConnection}s with which this {@code RemoteConnection} may be
* successfully asked to create a conference with.
*
@@ -1411,6 +1508,47 @@
}
}
+ /** @hide */
+ void onRttInitiationSuccess() {
+ for (CallbackRecord record : mCallbackRecords) {
+ final RemoteConnection connection = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(
+ () -> callback.onRttInitiationSuccess(connection));
+ }
+ }
+
+ /** @hide */
+ void onRttInitiationFailure(int reason) {
+ for (CallbackRecord record : mCallbackRecords) {
+ final RemoteConnection connection = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(
+ () -> callback.onRttInitiationFailure(connection, reason));
+ }
+ }
+
+ /** @hide */
+ void onRttSessionRemotelyTerminated() {
+ for (CallbackRecord record : mCallbackRecords) {
+ final RemoteConnection connection = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(
+ () -> callback.onRttSessionRemotelyTerminated(connection));
+ }
+ }
+
+ /** @hide */
+ void onRemoteRttRequest() {
+ for (CallbackRecord record : mCallbackRecords) {
+ final RemoteConnection connection = this;
+ final Callback callback = record.getCallback();
+ record.getHandler().post(
+ () -> callback.onRemoteRttRequest(connection));
+ }
+ }
+
+ /**
/**
* Create a RemoteConnection represents a failure, and which will be in
* {@link Connection#STATE_DISCONNECTED}. Attempting to use it for anything will almost
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
index 0c7404a..06cdd1a 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -286,10 +286,11 @@
String callingPackage = mOurConnectionServiceImpl.getApplicationContext()
.getOpPackageName();
+ int targetSdkVersion = mOurConnectionServiceImpl.getApplicationInfo().targetSdkVersion;
RemoteConnection.VideoProvider remoteVideoProvider = null;
if (videoProvider != null) {
remoteVideoProvider = new RemoteConnection.VideoProvider(videoProvider,
- callingPackage);
+ callingPackage, targetSdkVersion);
}
findConnectionForAction(callId, "setVideoProvider")
.setVideoProvider(remoteVideoProvider);
@@ -357,8 +358,11 @@
Session.Info sessionInfo) {
String callingPackage = mOurConnectionServiceImpl.getApplicationContext().
getOpPackageName();
+ int callingTargetSdkVersion = mOurConnectionServiceImpl.getApplicationInfo()
+ .targetSdkVersion;
RemoteConnection remoteConnection = new RemoteConnection(callId,
- mOutgoingConnectionServiceRpc, connection, callingPackage);
+ mOutgoingConnectionServiceRpc, connection, callingPackage,
+ callingTargetSdkVersion);
mConnectionById.put(callId, remoteConnection);
remoteConnection.registerCallback(new RemoteConnection.Callback() {
@Override
@@ -405,6 +409,50 @@
extras);
}
}
+
+ @Override
+ public void onRttInitiationSuccess(String callId, Session.Info sessionInfo)
+ throws RemoteException {
+ if (hasConnection(callId)) {
+ findConnectionForAction(callId, "onRttInitiationSuccess")
+ .onRttInitiationSuccess();
+ } else {
+ Log.w(this, "onRttInitiationSuccess called on a remote conference");
+ }
+ }
+
+ @Override
+ public void onRttInitiationFailure(String callId, int reason, Session.Info sessionInfo)
+ throws RemoteException {
+ if (hasConnection(callId)) {
+ findConnectionForAction(callId, "onRttInitiationFailure")
+ .onRttInitiationFailure(reason);
+ } else {
+ Log.w(this, "onRttInitiationFailure called on a remote conference");
+ }
+ }
+
+ @Override
+ public void onRttSessionRemotelyTerminated(String callId, Session.Info sessionInfo)
+ throws RemoteException {
+ if (hasConnection(callId)) {
+ findConnectionForAction(callId, "onRttSessionRemotelyTerminated")
+ .onRttSessionRemotelyTerminated();
+ } else {
+ Log.w(this, "onRttSessionRemotelyTerminated called on a remote conference");
+ }
+ }
+
+ @Override
+ public void onRemoteRttRequest(String callId, Session.Info sessionInfo)
+ throws RemoteException {
+ if (hasConnection(callId)) {
+ findConnectionForAction(callId, "onRemoteRttRequest")
+ .onRemoteRttRequest();
+ } else {
+ Log.w(this, "onRemoteRttRequest called on a remote conference");
+ }
+ }
};
private final ConnectionServiceAdapterServant mServant =
@@ -450,11 +498,14 @@
ConnectionRequest request,
boolean isIncoming) {
final String id = UUID.randomUUID().toString();
- final ConnectionRequest newRequest = new ConnectionRequest(
- request.getAccountHandle(),
- request.getAddress(),
- request.getExtras(),
- request.getVideoState());
+ final ConnectionRequest newRequest = new ConnectionRequest.Builder()
+ .setAccountHandle(request.getAccountHandle())
+ .setAddress(request.getAddress())
+ .setExtras(request.getExtras())
+ .setVideoState(request.getVideoState())
+ .setRttPipeFromInCall(request.getRttPipeFromInCall())
+ .setRttPipeToInCall(request.getRttPipeToInCall())
+ .build();
try {
if (mConnectionById.isEmpty()) {
mOutgoingConnectionServiceRpc.addConnectionServiceAdapter(mServant.getStub(),
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 96070b8..e21b4db 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -317,6 +317,23 @@
public static final String EXTRA_CALL_BACK_NUMBER = "android.telecom.extra.CALL_BACK_NUMBER";
/**
+ * The number of milliseconds that Telecom should wait after disconnecting a call via the
+ * ACTION_NEW_OUTGOING_CALL broadcast, in order to wait for the app which cancelled the call
+ * to make a new one.
+ * @hide
+ */
+ public static final String EXTRA_NEW_OUTGOING_CALL_CANCEL_TIMEOUT =
+ "android.telecom.extra.NEW_OUTGOING_CALL_CANCEL_TIMEOUT";
+
+ /**
+ * A boolean extra, which when set on the {@link Intent#ACTION_CALL} intent or on the bundle
+ * passed into {@link #placeCall(Uri, Bundle)}, indicates that the call should be initiated with
+ * an RTT session open. See {@link android.telecom.Call.RttCall} for more information on RTT.
+ */
+ public static final String EXTRA_START_CALL_WITH_RTT =
+ "android.telecom.extra.START_CALL_WITH_RTT";
+
+ /**
* A boolean meta-data value indicating whether an {@link InCallService} implements an
* in-call user interface. Dialer implementations (see {@link #getDefaultDialerPackage()}) which
* would also like to replace the in-call interface should set this meta-data to {@code true} in
@@ -356,6 +373,24 @@
"android.telecom.INCLUDE_EXTERNAL_CALLS";
/**
+ * A boolean meta-data value indicating whether an {@link InCallService} wants to be informed of
+ * calls which have the {@link Call.Details#PROPERTY_SELF_MANAGED} property. A self-managed
+ * call is one which originates from a self-managed {@link ConnectionService} which has chosen
+ * to implement its own call user interface. An {@link InCallService} implementation which
+ * would like to be informed of external calls should set this meta-data to {@code true} in the
+ * manifest registration of their {@link InCallService}. By default, the {@link InCallService}
+ * will NOT be informed about self-managed calls.
+ * <p>
+ * An {@link InCallService} which receives self-managed calls is free to view and control the
+ * state of calls in the self-managed {@link ConnectionService}. An example use-case is
+ * exposing these calls to a wearable or automotive device via its companion app.
+ * <p>
+ * See also {@link Connection#PROPERTY_SELF_MANAGED}.
+ */
+ public static final String METADATA_INCLUDE_SELF_MANAGED_CALLS =
+ "android.telecom.INCLUDE_SELF_MANAGED_CALLS";
+
+ /**
* The dual tone multi-frequency signaling character sent to indicate the dialing system should
* pause for a predefined period.
*/
@@ -1034,10 +1069,12 @@
/**
* Returns whether there is an ongoing phone call (can be in dialing, ringing, active or holding
- * states).
+ * states) originating from either a manager or self-managed {@link ConnectionService}.
* <p>
* Requires permission: {@link android.Manifest.permission#READ_PHONE_STATE}
- * </p>
+ *
+ * @return {@code true} if there is an ongoing call in either a managed or self-managed
+ * {@link ConnectionService}, {@code false} otherwise.
*/
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
public boolean isInCall() {
@@ -1052,6 +1089,31 @@
}
/**
+ * Returns whether there is an ongoing call originating from a managed
+ * {@link ConnectionService}. An ongoing call can be in dialing, ringing, active or holding
+ * states.
+ * <p>
+ * If you also need to know if there are ongoing self-managed calls, use {@link #isInCall()}
+ * instead.
+ * <p>
+ * Requires permission: {@link android.Manifest.permission#READ_PHONE_STATE}
+ *
+ * @return {@code true} if there is an ongoing call in a managed {@link ConnectionService},
+ * {@code false} otherwise.
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public boolean isInManagedCall() {
+ try {
+ if (isServiceConnected()) {
+ return getTelecomService().isInManagedCall(mContext.getOpPackageName());
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException calling isInManagedCall().", e);
+ }
+ return false;
+ }
+
+ /**
* Returns one of the following constants that represents the current state of Telecom:
*
* {@link TelephonyManager#CALL_STATE_RINGING}
@@ -1062,6 +1124,9 @@
* {@link android.Manifest.permission#READ_PHONE_STATE} permission. This is intentional, to
* preserve the behavior of {@link TelephonyManager#getCallState()}, which also did not require
* the permission.
+ *
+ * Takes into consideration both managed and self-managed calls.
+ *
* @hide
*/
@SystemApi
@@ -1079,6 +1144,7 @@
/**
* Returns whether there currently exists is a ringing incoming-call.
*
+ * @return {@code true} if there is a managed or self-managed ringing call.
* @hide
*/
@SystemApi
diff --git a/telecomm/java/android/telecom/VideoCallImpl.java b/telecomm/java/android/telecom/VideoCallImpl.java
index d8ede5c..429a434 100644
--- a/telecomm/java/android/telecom/VideoCallImpl.java
+++ b/telecomm/java/android/telecom/VideoCallImpl.java
@@ -44,6 +44,7 @@
private int mVideoQuality = VideoProfile.QUALITY_UNKNOWN;
private int mVideoState = VideoProfile.STATE_AUDIO_ONLY;
private final String mCallingPackageName;
+ private final int mTargetSdkVersion;
private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
@Override
@@ -198,13 +199,15 @@
private Handler mHandler;
- VideoCallImpl(IVideoProvider videoProvider, String callingPackageName) throws RemoteException {
+ VideoCallImpl(IVideoProvider videoProvider, String callingPackageName, int targetSdkVersion)
+ throws RemoteException {
mVideoProvider = videoProvider;
mVideoProvider.asBinder().linkToDeath(mDeathRecipient, 0);
mBinder = new VideoCallListenerBinder();
mVideoProvider.addVideoCallback(mBinder);
mCallingPackageName = callingPackageName;
+ mTargetSdkVersion = targetSdkVersion;
}
public void destroy() {
@@ -243,7 +246,7 @@
public void setCamera(String cameraId) {
try {
Log.w(this, "setCamera: cameraId=%s, calling=%s", cameraId, mCallingPackageName);
- mVideoProvider.setCamera(cameraId, mCallingPackageName);
+ mVideoProvider.setCamera(cameraId, mCallingPackageName, mTargetSdkVersion);
} catch (RemoteException e) {
}
}
diff --git a/telecomm/java/android/telecom/VideoProfile.java b/telecomm/java/android/telecom/VideoProfile.java
index 216603c..e0e3a08 100644
--- a/telecomm/java/android/telecom/VideoProfile.java
+++ b/telecomm/java/android/telecom/VideoProfile.java
@@ -235,7 +235,7 @@
StringBuilder sb = new StringBuilder();
sb.append("Audio");
- if (isAudioOnly(videoState)) {
+ if (videoState == STATE_AUDIO_ONLY) {
sb.append(" Only");
} else {
if (isTransmissionEnabled(videoState)) {
@@ -256,6 +256,9 @@
/**
* Indicates whether the video state is audio only.
+ * <p>
+ * Note: Considers only whether either both the {@link #STATE_RX_ENABLED} or
+ * {@link #STATE_TX_ENABLED} bits are off, but not {@link #STATE_PAUSED}.
*
* @param videoState The video state.
* @return {@code True} if the video state is audio only, {@code false} otherwise.
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
index 20feba7..c631d08 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
@@ -17,6 +17,7 @@
package com.android.internal.telecom;
import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
import android.telecom.CallAudioState;
import android.telecom.ConnectionRequest;
import android.telecom.Logging.Session;
@@ -46,8 +47,8 @@
boolean isUnknown,
in Session.Info sessionInfo);
- void createConnectionFailed(String callId, in ConnectionRequest request, boolean isIncoming,
- in Session.Info sessionInfo);
+ void createConnectionFailed(in PhoneAccountHandle connectionManagerPhoneAccount, String callId,
+ in ConnectionRequest request, boolean isIncoming, in Session.Info sessionInfo);
void abort(String callId, in Session.Info sessionInfo);
@@ -89,4 +90,12 @@
void sendCallEvent(String callId, String event, in Bundle extras, in Session.Info sessionInfo);
void onExtrasChanged(String callId, in Bundle extras, in Session.Info sessionInfo);
+
+ void startRtt(String callId, in ParcelFileDescriptor fromInCall,
+ in ParcelFileDescriptor toInCall, in Session.Info sessionInfo);
+
+ void stopRtt(String callId, in Session.Info sessionInfo);
+
+ void respondToRttUpgradeRequest(String callId, in ParcelFileDescriptor fromInCall,
+ in ParcelFileDescriptor toInCall, in Session.Info sessionInfo);
}
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
index b58f8bc..ac9da2e 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
@@ -106,4 +106,12 @@
void onConnectionEvent(String callId, String event, in Bundle extras,
in Session.Info sessionInfo);
+
+ void onRttInitiationSuccess(String callId, in Session.Info sessionInfo);
+
+ void onRttInitiationFailure(String callId, int reason, in Session.Info sessionInfo);
+
+ void onRttSessionRemotelyTerminated(String callId, in Session.Info sessionInfo);
+
+ void onRemoteRttRequest(String callId, in Session.Info sessionInfo);
}
diff --git a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
index 49f9b3b..73fa29a 100644
--- a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
@@ -69,4 +69,12 @@
void putExtras(String callId, in Bundle extras);
void removeExtras(String callId, in List<String> keys);
+
+ void sendRttRequest(String callId);
+
+ void respondToRttRequest(String callId, int id, boolean accept);
+
+ void stopRtt(String callId);
+
+ void setRttMode(String callId, int mode);
}
diff --git a/telecomm/java/com/android/internal/telecom/IInCallService.aidl b/telecomm/java/com/android/internal/telecom/IInCallService.aidl
index 3e43fe2..e8cf8e9 100644
--- a/telecomm/java/com/android/internal/telecom/IInCallService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IInCallService.aidl
@@ -50,4 +50,8 @@
void silenceRinger();
void onConnectionEvent(String callId, String event, in Bundle extras);
+
+ void onRttUpgradeRequest(String callId, int id);
+
+ void onRttInitiationFailure(String callId, int reason);
}
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index d9465dc..c044742 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -165,6 +165,11 @@
boolean isInCall(String callingPackage);
/**
+ * @see TelecomServiceImpl#isInManagedCall
+ */
+ boolean isInManagedCall(String callingPackage);
+
+ /**
* @see TelecomServiceImpl#isRinging
*/
boolean isRinging(String callingPackage);
diff --git a/telecomm/java/com/android/internal/telecom/IVideoProvider.aidl b/telecomm/java/com/android/internal/telecom/IVideoProvider.aidl
index a109e90..272b884 100644
--- a/telecomm/java/com/android/internal/telecom/IVideoProvider.aidl
+++ b/telecomm/java/com/android/internal/telecom/IVideoProvider.aidl
@@ -30,7 +30,7 @@
void removeVideoCallback(IBinder videoCallbackBinder);
- void setCamera(String cameraId, in String mCallingPackageName);
+ void setCamera(String cameraId, in String mCallingPackageName, int targetSdkVersion);
void setPreviewSurface(in Surface surface);
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 04c10d2..1076afc 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -817,7 +817,7 @@
/**
* Defines carrier-specific actions which act upon
- * android.intent.action.CARRIER_SIGNAL_REDIRECTED, used for customization of the
+ * com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED, used for customization of the
* default carrier app
* Format: "CARRIER_ACTION_IDX, ..."
* Where {@code CARRIER_ACTION_IDX} is an integer defined in
@@ -832,7 +832,7 @@
/**
* Defines carrier-specific actions which act upon
- * android.intent.action.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED
+ * com.android.internal.telephony.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED
* and configured signal args:
* {@link com.android.internal.telephony.TelephonyIntents#EXTRA_APN_TYPE_KEY apnType},
* {@link com.android.internal.telephony.TelephonyIntents#EXTRA_ERROR_CODE_KEY errorCode}
@@ -873,11 +873,11 @@
* @see com.android.internal.telephony.TelephonyIntents
* Example:
* <item>com.google.android.carrierAPK/.CarrierSignalReceiverA:
- * android.intent.action.CARRIER_SIGNAL_REDIRECTED,
- * android.intent.action.CARRIER_SIGNAL_PCO_VALUE
+ * com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED,
+ * com.android.internal.telephony.CARRIER_SIGNAL_PCO_VALUE
* </item>
* <item>com.google.android.carrierAPK/.CarrierSignalReceiverB:
- * android.intent.action.CARRIER_SIGNAL_PCO_VALUE
+ * com.android.internal.telephony.CARRIER_SIGNAL_PCO_VALUE
* </item>
* @hide
*/
@@ -892,11 +892,11 @@
* @see com.android.internal.telephony.TelephonyIntents
* Example:
* <item>com.google.android.carrierAPK/.CarrierSignalReceiverA:
- * android.intent.action.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED,
- * android.intent.action.CARRIER_SIGNAL_PCO_VALUE
+ * com.android.internal.telephony.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED,
+ * com.android.internal.telephony.CARRIER_SIGNAL_PCO_VALUE
* </item>
* <item>com.google.android.carrierAPK/.CarrierSignalReceiverB:
- * android.intent.action.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED
+ * com.android.internal.telephony.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED
* </item>
* @hide
*/
@@ -1119,6 +1119,22 @@
public static final String KEY_EDITABLE_WFC_ROAMING_MODE_BOOL =
"editable_wfc_roaming_mode_bool";
+ /**
+ * Indicates whether the carrier supports 3gpp call forwarding MMI codes while roaming. If
+ * false, the user will be notified that call forwarding is not available when the MMI code
+ * fails.
+ */
+ public static final String KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL =
+ "support_3gpp_call_forwarding_while_roaming_bool";
+
+ /**
+ * When {@code true}, the user will be notified when they attempt to place an international call
+ * when the call is placed using wifi calling.
+ * @hide
+ */
+ public static final String KEY_NOTIFY_INTERNATIONAL_CALL_ON_WFC_BOOL =
+ "notify_international_call_on_wfc_bool";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -1293,7 +1309,7 @@
sDefaults.putStringArray(KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY,
new String[]{
"com.android.carrierdefaultapp/.CarrierDefaultBroadcastReceiver:" +
- "android.intent.action.CARRIER_SIGNAL_REDIRECTED"
+ "com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED"
});
sDefaults.putStringArray(KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY, null);
@@ -1323,6 +1339,8 @@
sDefaults.putBoolean(KEY_NOTIFY_VT_HANDOVER_TO_WIFI_FAILURE_BOOL, false);
sDefaults.putStringArray(KEY_FILTERED_CNAP_NAMES_STRING_ARRAY, null);
sDefaults.putBoolean(KEY_EDITABLE_WFC_ROAMING_MODE_BOOL, false);
+ sDefaults.putBoolean(KEY_SUPPORT_3GPP_CALL_FORWARDING_WHILE_ROAMING_BOOL, true);
+ sDefaults.putBoolean(KEY_NOTIFY_INTERNATIONAL_CALL_ON_WFC_BOOL, false);
}
/**
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index 152b868..2eba402 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -1139,6 +1139,8 @@
private static final String KOREA_ISO_COUNTRY_CODE = "KR";
+ private static final String JAPAN_ISO_COUNTRY_CODE = "JP";
+
/**
* Breaks the given number down and formats it according to the rules
* for the country the number is from.
@@ -1437,6 +1439,30 @@
}
/**
+ * Determines if a {@param phoneNumber} is international if dialed from
+ * {@param defaultCountryIso}.
+ *
+ * @param phoneNumber The phone number.
+ * @param defaultCountryIso The current country ISO.
+ * @return {@code true} if the number is international, {@code false} otherwise.
+ * @hide
+ */
+ public static boolean isInternationalNumber(String phoneNumber, String defaultCountryIso) {
+ // If it starts with # or * its not international.
+ if (phoneNumber.startsWith("#") || phoneNumber.startsWith("*")) {
+ return false;
+ }
+
+ PhoneNumberUtil util = PhoneNumberUtil.getInstance();
+ try {
+ PhoneNumber pn = util.parseAndKeepRawInput(phoneNumber, defaultCountryIso);
+ return pn.getCountryCode() != util.getCountryCodeForRegion(defaultCountryIso);
+ } catch (NumberParseException e) {
+ return false;
+ }
+ }
+
+ /**
* Format a phone number.
* <p>
* If the given number doesn't have the country code, the phone will be
@@ -1459,15 +1485,25 @@
String result = null;
try {
PhoneNumber pn = util.parseAndKeepRawInput(phoneNumber, defaultCountryIso);
- /**
- * Need to reformat any local Korean phone numbers (when the user is in Korea) with
- * country code to corresponding national format which would replace the leading
- * +82 with 0.
- */
- if (KOREA_ISO_COUNTRY_CODE.equals(defaultCountryIso) &&
+
+ if (KOREA_ISO_COUNTRY_CODE.equalsIgnoreCase(defaultCountryIso) &&
(pn.getCountryCode() == util.getCountryCodeForRegion(KOREA_ISO_COUNTRY_CODE)) &&
(pn.getCountryCodeSource() ==
PhoneNumber.CountryCodeSource.FROM_NUMBER_WITH_PLUS_SIGN)) {
+ /**
+ * Need to reformat any local Korean phone numbers (when the user is in Korea) with
+ * country code to corresponding national format which would replace the leading
+ * +82 with 0.
+ */
+ result = util.format(pn, PhoneNumberUtil.PhoneNumberFormat.NATIONAL);
+ } else if (JAPAN_ISO_COUNTRY_CODE.equalsIgnoreCase(defaultCountryIso) &&
+ pn.getCountryCode() == util.getCountryCodeForRegion(JAPAN_ISO_COUNTRY_CODE) &&
+ (pn.getCountryCodeSource() ==
+ PhoneNumber.CountryCodeSource.FROM_NUMBER_WITH_PLUS_SIGN)) {
+ /**
+ * Need to reformat Japanese phone numbers (when user is in Japan) with the national
+ * dialing format.
+ */
result = util.format(pn, PhoneNumberUtil.PhoneNumberFormat.NATIONAL);
} else {
result = util.formatInOriginalFormat(pn, defaultCountryIso);
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index dd03305..afff6d5 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -208,7 +208,9 @@
*
* @see #onOemHookRawEvent
* @hide
+ * @deprecated OEM needs a vendor-extension hal and their apps should use that instead
*/
+ @Deprecated
public static final int LISTEN_OEM_HOOK_RAW_EVENT = 0x00008000;
/**
diff --git a/telephony/java/android/telephony/Rlog.java b/telephony/java/android/telephony/Rlog.java
index cd0a012..2a31e3a 100644
--- a/telephony/java/android/telephony/Rlog.java
+++ b/telephony/java/android/telephony/Rlog.java
@@ -16,6 +16,7 @@
package android.telephony;
+import android.os.Build;
import android.text.TextUtils;
import android.util.Log;
@@ -32,6 +33,8 @@
*/
public final class Rlog {
+ private static final boolean USER_BUILD = Build.TYPE.equals("user");
+
private Rlog() {
}
@@ -125,10 +128,15 @@
/**
* Returns a secure hash (using the SHA1 algorithm) of the provided input.
*
- * @return the hash
+ * @return "****" if the build type is user, otherwise the hash
* @param input the bytes for which the secure hash should be computed.
*/
private static String secureHash(byte[] input) {
+ // Refrain from logging user personal information in user build.
+ if (USER_BUILD) {
+ return "****";
+ }
+
MessageDigest messageDigest;
try {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index faeeded..c691879 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -780,6 +780,21 @@
"android.telephony.event.EVENT_DOWNGRADE_DATA_DISABLED";
/**
+ * {@link android.telecom.Connection} event used to indicate that the InCall UI should notify
+ * the user when an international call is placed while on WFC only.
+ * <p>
+ * Used when the carrier config value
+ * {@link CarrierConfigManager#KEY_NOTIFY_INTERNATIONAL_CALL_ON_WFC_BOOL} is true, the device
+ * is on WFC (VoLTE not available) and an international number is dialed.
+ * <p>
+ * Sent via {@link android.telecom.Connection#sendConnectionEvent(String, Bundle)}.
+ * The {@link Bundle} parameter is expected to be null when this connection event is used.
+ * @hide
+ */
+ public static final String EVENT_NOTIFY_INTERNATIONAL_CALL_ON_WFC =
+ "android.telephony.event.EVENT_NOTIFY_INTERNATIONAL_CALL_ON_WFC";
+
+ /**
* Response codes for sim activation. Activation completed successfully.
* @hide
*/
@@ -3915,9 +3930,8 @@
}
}
- if (property.length() > SystemProperties.PROP_NAME_MAX
- || propVal.length() > SystemProperties.PROP_VALUE_MAX) {
- Rlog.d(TAG, "setTelephonyProperty: property to long phoneId=" + phoneId +
+ if (propVal.length() > SystemProperties.PROP_VALUE_MAX) {
+ Rlog.d(TAG, "setTelephonyProperty: property too long phoneId=" + phoneId +
" property=" + property + " value: " + value + " propVal=" + propVal);
return;
}
@@ -4174,6 +4188,45 @@
}
/**
+ * Returns an array of Forbidden PLMNs from the USIM App
+ * Returns null if the query fails.
+ *
+ *
+ * <p>Requires that the caller has READ_PRIVILEGED_PHONE_STATE
+ *
+ * @return an array of forbidden PLMNs or null if not available
+ */
+ public String[] getForbiddenPlmns() {
+ return getForbiddenPlmns(getSubId(), APPTYPE_USIM);
+ }
+
+ /**
+ * Returns an array of Forbidden PLMNs from the specified SIM App
+ * Returns null if the query fails.
+ *
+ *
+ * <p>Requires that the calling app has READ_PRIVILEGED_PHONE_STATE
+ *
+ * @param subId subscription ID used for authentication
+ * @param appType the icc application type, like {@link #APPTYPE_USIM}
+ * @return fplmns an array of forbidden PLMNs
+ * @hide
+ */
+ public String[] getForbiddenPlmns(int subId, int appType) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony == null)
+ return null;
+ return telephony.getForbiddenPlmns(subId, appType);
+ } catch (RemoteException ex) {
+ return null;
+ } catch (NullPointerException ex) {
+ // This could happen before phone starts
+ return null;
+ }
+ }
+
+ /**
* Get P-CSCF address from PCO after data connection is established or modified.
* @param apnType the apnType, "ims" for IMS APN, "emergency" for EMERGENCY APN
* @return array of P-CSCF address
@@ -5040,7 +5093,9 @@
* 0 request was handled succesfully, but no response data
* positive value success, data length of response
* @hide
+ * @deprecated OEM needs a vendor-extension hal and their apps should use that instead
*/
+ @Deprecated
public int invokeOemRilRequestRaw(byte[] oemReq, byte[] oemResp) {
try {
ITelephony telephony = getITelephony();
@@ -5333,6 +5388,44 @@
}
/**
+ * Set SIM card power state. Request is equivalent to inserting or removing the card.
+ *
+ * @param powerUp True if powering up the SIM, otherwise powering down
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ *
+ * @hide
+ **/
+ public void setSimPowerState(boolean powerUp) {
+ setSimPowerStateForSlot(getDefaultSim(), powerUp);
+ }
+
+ /**
+ * Set SIM card power state. Request is equivalent to inserting or removing the card.
+ *
+ * @param slotId SIM slot id
+ * @param powerUp True if powering up the SIM, otherwise powering down
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
+ *
+ * @hide
+ **/
+ public void setSimPowerStateForSlot(int slotId, boolean powerUp) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ telephony.setSimPowerStateForSlot(slotId, powerUp);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelephony#setSimPowerStateForSlot", e);
+ } catch (SecurityException e) {
+ Log.e(TAG, "Permission error calling ITelephony#setSimPowerStateForSlot", e);
+ }
+ }
+
+ /**
* Set baseband version for the default phone.
*
* @param version baseband version
diff --git a/telephony/java/android/telephony/ims/ImsService.java b/telephony/java/android/telephony/ims/ImsService.java
new file mode 100644
index 0000000..f1f683c
--- /dev/null
+++ b/telephony/java/android/telephony/ims/ImsService.java
@@ -0,0 +1,467 @@
+/*
+ * Copyright (C) 2017 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.ims;
+
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.CarrierConfigManager;
+import android.telephony.ims.feature.ImsFeature;
+import android.telephony.ims.feature.MMTelFeature;
+import android.telephony.ims.feature.RcsFeature;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.ims.ImsCallProfile;
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsCallSessionListener;
+import com.android.ims.internal.IImsConfig;
+import com.android.ims.internal.IImsEcbm;
+import com.android.ims.internal.IImsFeatureStatusCallback;
+import com.android.ims.internal.IImsMultiEndpoint;
+import com.android.ims.internal.IImsRegistrationListener;
+import com.android.ims.internal.IImsServiceController;
+import com.android.ims.internal.IImsServiceFeatureListener;
+import com.android.ims.internal.IImsUt;
+import com.android.internal.annotations.VisibleForTesting;
+
+import static android.Manifest.permission.MODIFY_PHONE_STATE;
+import static android.Manifest.permission.READ_PHONE_STATE;
+import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
+
+/**
+ * Main ImsService implementation, which binds via the Telephony ImsResolver. Services that extend
+ * ImsService must register the service in their AndroidManifest to be detected by the framework.
+ * First, the application must declare that they use the "android.permission.BIND_IMS_SERVICE"
+ * permission. Then, the ImsService definition in the manifest must follow the following format:
+ *
+ * ...
+ * <service android:name=".EgImsService"
+ * android:permission="android.permission.BIND_IMS_SERVICE" >
+ * <!-- Apps must declare which features they support as metadata. The different categories are
+ * defined below. In this example, the RCS_FEATURE feature is supported. -->
+ * <meta-data android:name="android.telephony.ims.RCS_FEATURE" android:value="true" />
+ * <intent-filter>
+ * <action android:name="android.telephony.ims.ImsService" />
+ * </intent-filter>
+ * </service>
+ * ...
+ *
+ * The telephony framework will then bind to the ImsService you have defined in your manifest
+ * if you are either:
+ * 1) Defined as the default ImsService for the device in the device overlay using
+ * "config_ims_package".
+ * 2) Defined as a Carrier Provided ImsService in the Carrier Configuration using
+ * {@link CarrierConfigManager#KEY_CONFIG_IMS_PACKAGE_OVERRIDE_STRING}.
+ *
+ * The features that are currently supported in an ImsService are:
+ * - RCS_FEATURE: This ImsService implements the {@link RcsFeature} class.
+ * - MMTEL_FEATURE: This ImsService implements the {@link MMTelFeature} class.
+ * - EMERGENCY_MMTEL_FEATURE: This ImsService implements the {@link MMTelFeature} class and will be
+ * available to place emergency calls at all times. This MUST be implemented by the default
+ * ImsService provided in the device overlay.
+ *
+ * @hide
+ */
+public abstract class ImsService extends ImsServiceBase {
+
+ private static final String LOG_TAG = "ImsService";
+
+ /**
+ * The intent that must be defined as an intent-filter in the AndroidManifest of the ImsService.
+ */
+ public static final String SERVICE_INTERFACE = "android.telephony.ims.ImsService";
+
+ // A map of slot Id -> Set of features corresponding to that slot.
+ private final SparseArray<SparseArray<ImsFeature>> mFeatures = new SparseArray<>();
+
+ // Implements all supported features as a flat interface.
+ protected final IBinder mImsServiceController = new IImsServiceController.Stub() {
+
+ @Override
+ public void createImsFeature(int slotId, int feature, IImsFeatureStatusCallback c)
+ throws RemoteException {
+ synchronized (mFeatures) {
+ enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "createImsFeature");
+ onCreateImsFeatureInternal(slotId, feature, c);
+ }
+ }
+
+ @Override
+ public void removeImsFeature(int slotId, int feature) throws RemoteException {
+ synchronized (mFeatures) {
+ enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "removeImsFeature");
+ onRemoveImsFeatureInternal(slotId, feature);
+ }
+ }
+
+ @Override
+ public int startSession(int slotId, int featureType, PendingIntent incomingCallIntent,
+ IImsRegistrationListener listener) throws RemoteException {
+ enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "startSession");
+ synchronized (mFeatures) {
+ MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
+ if (feature != null) {
+ return feature.startSession(incomingCallIntent, listener);
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ public void endSession(int slotId, int featureType, int sessionId) throws RemoteException {
+ synchronized (mFeatures) {
+ enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "endSession");
+ MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
+ if (feature != null) {
+ feature.endSession(sessionId);
+ }
+ }
+ }
+
+ @Override
+ public boolean isConnected(int slotId, int featureType, int callSessionType, int callType)
+ throws RemoteException {
+ enforceReadPhoneStatePermission("isConnected");
+ synchronized (mFeatures) {
+ MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
+ if (feature != null) {
+ return feature.isConnected(callSessionType, callType);
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean isOpened(int slotId, int featureType) throws RemoteException {
+ enforceReadPhoneStatePermission("isOpened");
+ synchronized (mFeatures) {
+ MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
+ if (feature != null) {
+ return feature.isOpened();
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public int getFeatureStatus(int slotId, int featureType) throws RemoteException {
+ enforceReadPhoneStatePermission("getFeatureStatus");
+ int status = ImsFeature.STATE_NOT_AVAILABLE;
+ synchronized (mFeatures) {
+ SparseArray<ImsFeature> featureMap = mFeatures.get(slotId);
+ if (featureMap != null) {
+ ImsFeature feature = getImsFeatureFromType(featureMap, featureType);
+ if (feature != null) {
+ status = feature.getFeatureState();
+ }
+ }
+ }
+ return status;
+ }
+
+ @Override
+ public void addRegistrationListener(int slotId, int featureType,
+ IImsRegistrationListener listener) throws RemoteException {
+ enforceReadPhoneStatePermission("addRegistrationListener");
+ synchronized (mFeatures) {
+ MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
+ if (feature != null) {
+ feature.addRegistrationListener(listener);
+ }
+ }
+ }
+
+ @Override
+ public void removeRegistrationListener(int slotId, int featureType,
+ IImsRegistrationListener listener) throws RemoteException {
+ enforceReadPhoneStatePermission("removeRegistrationListener");
+ synchronized (mFeatures) {
+ MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
+ if (feature != null) {
+ feature.removeRegistrationListener(listener);
+ }
+ }
+ }
+
+ @Override
+ public ImsCallProfile createCallProfile(int slotId, int featureType, int sessionId,
+ int callSessionType, int callType) throws RemoteException {
+ enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "createCallProfile");
+ synchronized (mFeatures) {
+ MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
+ if (feature != null) {
+ return feature.createCallProfile(sessionId, callSessionType, callType);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public IImsCallSession createCallSession(int slotId, int featureType, int sessionId,
+ ImsCallProfile profile, IImsCallSessionListener listener) throws RemoteException {
+ enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "createCallSession");
+ synchronized (mFeatures) {
+ MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
+ if (feature != null) {
+ return feature.createCallSession(sessionId, profile, listener);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public IImsCallSession getPendingCallSession(int slotId, int featureType, int sessionId,
+ String callId) throws RemoteException {
+ enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "getPendingCallSession");
+ synchronized (mFeatures) {
+ MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
+ if (feature != null) {
+ return feature.getPendingCallSession(sessionId, callId);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public IImsUt getUtInterface(int slotId, int featureType)
+ throws RemoteException {
+ enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "getUtInterface");
+ synchronized (mFeatures) {
+ MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
+ if (feature != null) {
+ return feature.getUtInterface();
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public IImsConfig getConfigInterface(int slotId, int featureType)
+ throws RemoteException {
+ enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "getConfigInterface");
+ synchronized (mFeatures) {
+ MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
+ if (feature != null) {
+ return feature.getConfigInterface();
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void turnOnIms(int slotId, int featureType) throws RemoteException {
+ enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "turnOnIms");
+ synchronized (mFeatures) {
+ MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
+ if (feature != null) {
+ feature.turnOnIms();
+ }
+ }
+ }
+
+ @Override
+ public void turnOffIms(int slotId, int featureType) throws RemoteException {
+ enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "turnOffIms");
+ synchronized (mFeatures) {
+ MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
+ if (feature != null) {
+ feature.turnOffIms();
+ }
+ }
+ }
+
+ @Override
+ public IImsEcbm getEcbmInterface(int slotId, int featureType)
+ throws RemoteException {
+ enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "getEcbmInterface");
+ synchronized (mFeatures) {
+ MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
+ if (feature != null) {
+ return feature.getEcbmInterface();
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void setUiTTYMode(int slotId, int featureType, int uiTtyMode, Message onComplete)
+ throws RemoteException {
+ enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "setUiTTYMode");
+ synchronized (mFeatures) {
+ MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
+ if (feature != null) {
+ feature.setUiTTYMode(uiTtyMode, onComplete);
+ }
+ }
+ }
+
+ @Override
+ public IImsMultiEndpoint getMultiEndpointInterface(int slotId, int featureType)
+ throws RemoteException {
+ enforceCallingOrSelfPermission(MODIFY_PHONE_STATE, "getMultiEndpointInterface");
+ synchronized (mFeatures) {
+ MMTelFeature feature = resolveMMTelFeature(slotId, featureType);
+ if (feature != null) {
+ return feature.getMultiEndpointInterface();
+ }
+ }
+ return null;
+ }
+
+ };
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ if(SERVICE_INTERFACE.equals(intent.getAction())) {
+ return mImsServiceController;
+ }
+ return null;
+ }
+
+ /**
+ * Called from the ImsResolver to create the requested ImsFeature, as defined by the slot and
+ * featureType
+ * @param slotId An integer representing which SIM slot the ImsFeature is assigned to.
+ * @param featureType An integer representing the type of ImsFeature being created. This is
+ * defined in {@link ImsFeature}.
+ */
+ // Be sure to lock on mFeatures before accessing this method
+ private void onCreateImsFeatureInternal(int slotId, int featureType,
+ IImsFeatureStatusCallback c) {
+ SparseArray<ImsFeature> featureMap = mFeatures.get(slotId);
+ if (featureMap == null) {
+ featureMap = new SparseArray<>();
+ mFeatures.put(slotId, featureMap);
+ }
+ ImsFeature f = makeImsFeature(slotId, featureType);
+ if (f != null) {
+ f.setContext(this);
+ f.setSlotId(slotId);
+ f.setImsFeatureStatusCallback(c);
+ featureMap.put(featureType, f);
+ }
+
+ }
+ /**
+ * Called from the ImsResolver to remove an existing ImsFeature, as defined by the slot and
+ * featureType.
+ * @param slotId An integer representing which SIM slot the ImsFeature is assigned to.
+ * @param featureType An integer representing the type of ImsFeature being removed. This is
+ * defined in {@link ImsFeature}.
+ */
+ // Be sure to lock on mFeatures before accessing this method
+ private void onRemoveImsFeatureInternal(int slotId, int featureType) {
+ SparseArray<ImsFeature> featureMap = mFeatures.get(slotId);
+ if (featureMap == null) {
+ return;
+ }
+
+ ImsFeature featureToRemove = getImsFeatureFromType(featureMap, featureType);
+ if (featureToRemove != null) {
+ featureMap.remove(featureType);
+ featureToRemove.notifyFeatureRemoved(slotId);
+ // Remove reference to Binder
+ featureToRemove.setImsFeatureStatusCallback(null);
+ }
+ }
+
+ // Be sure to lock on mFeatures before accessing this method
+ private MMTelFeature resolveMMTelFeature(int slotId, int featureType) {
+ SparseArray<ImsFeature> features = getImsFeatureMap(slotId);
+ MMTelFeature feature = null;
+ if (features != null) {
+ feature = resolveImsFeature(features, featureType, MMTelFeature.class);
+ }
+ return feature;
+ }
+
+ // Be sure to lock on mFeatures before accessing this method
+ private <T extends ImsFeature> T resolveImsFeature(SparseArray<ImsFeature> set, int featureType,
+ Class<T> className) {
+ ImsFeature feature = getImsFeatureFromType(set, featureType);
+ if (feature == null) {
+ return null;
+ }
+ try {
+ return className.cast(feature);
+ } catch (ClassCastException e)
+ {
+ Log.e(LOG_TAG, "Can not cast ImsFeature! Exception: " + e.getMessage());
+ }
+ return null;
+ }
+
+ @VisibleForTesting
+ // Be sure to lock on mFeatures before accessing this method
+ public SparseArray<ImsFeature> getImsFeatureMap(int slotId) {
+ return mFeatures.get(slotId);
+ }
+
+ @VisibleForTesting
+ // Be sure to lock on mFeatures before accessing this method
+ public ImsFeature getImsFeatureFromType(SparseArray<ImsFeature> set, int featureType) {
+ return set.get(featureType);
+ }
+
+ private ImsFeature makeImsFeature(int slotId, int feature) {
+ switch (feature) {
+ case ImsFeature.EMERGENCY_MMTEL: {
+ return onCreateEmergencyMMTelImsFeature(slotId);
+ }
+ case ImsFeature.MMTEL: {
+ return onCreateMMTelImsFeature(slotId);
+ }
+ case ImsFeature.RCS: {
+ return onCreateRcsFeature(slotId);
+ }
+ }
+ // Tried to create feature that is not defined.
+ return null;
+ }
+
+ /**
+ * Check for both READ_PHONE_STATE and READ_PRIVILEGED_PHONE_STATE. READ_PHONE_STATE is a
+ * public permission and READ_PRIVILEGED_PHONE_STATE is only granted to system apps.
+ */
+ private void enforceReadPhoneStatePermission(String fn) {
+ if (checkCallingOrSelfPermission(READ_PRIVILEGED_PHONE_STATE)
+ != PackageManager.PERMISSION_GRANTED) {
+ enforceCallingOrSelfPermission(READ_PHONE_STATE, fn);
+ }
+ }
+
+ /**
+ * @return An implementation of MMTelFeature that will be used by the system for MMTel
+ * functionality. Must be able to handle emergency calls at any time as well.
+ */
+ public abstract MMTelFeature onCreateEmergencyMMTelImsFeature(int slotId);
+
+ /**
+ * @return An implementation of MMTelFeature that will be used by the system for MMTel
+ * functionality.
+ */
+ public abstract MMTelFeature onCreateMMTelImsFeature(int slotId);
+
+ /**
+ * @return An implementation of RcsFeature that will be used by the system for RCS.
+ */
+ public abstract RcsFeature onCreateRcsFeature(int slotId);
+}
diff --git a/telephony/java/android/telephony/ims/ImsServiceBase.java b/telephony/java/android/telephony/ims/ImsServiceBase.java
index 0b50eca..bb36862 100644
--- a/telephony/java/android/telephony/ims/ImsServiceBase.java
+++ b/telephony/java/android/telephony/ims/ImsServiceBase.java
@@ -19,17 +19,27 @@
import android.annotation.SystemApi;
import android.app.Service;
import android.content.Intent;
+import android.os.Binder;
import android.os.IBinder;
/**
- * Base ImsService Implementation, which is used by the ImsResolver to bind.
+ * Base ImsService Implementation, which is used by the ImsResolver to bind. ImsServices that do not
+ * need to provide an ImsService implementation but still wish to be managed by the ImsResolver
+ * lifecycle may implement this class directly.
* @hide
*/
@SystemApi
public class ImsServiceBase extends Service {
+ /**
+ * Binder connection that does nothing but keep the connection between this Service and the
+ * framework active. If this service crashes, the framework will be notified.
+ */
+ private IBinder mConnection = new Binder();
+
@Override
public IBinder onBind(Intent intent) {
- return null;
+ return mConnection;
}
+
}
diff --git a/telephony/java/android/telephony/ims/ImsServiceProxy.java b/telephony/java/android/telephony/ims/ImsServiceProxy.java
new file mode 100644
index 0000000..38ea6e6f
--- /dev/null
+++ b/telephony/java/android/telephony/ims/ImsServiceProxy.java
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) 2017 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.ims;
+
+import android.app.PendingIntent;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.ims.feature.IRcsFeature;
+import android.telephony.ims.feature.ImsFeature;
+import android.util.Log;
+
+import com.android.ims.ImsCallProfile;
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsCallSessionListener;
+import com.android.ims.internal.IImsConfig;
+import com.android.ims.internal.IImsEcbm;
+import com.android.ims.internal.IImsMultiEndpoint;
+import com.android.ims.internal.IImsRegistrationListener;
+import com.android.ims.internal.IImsServiceController;
+import com.android.ims.internal.IImsServiceFeatureListener;
+import com.android.ims.internal.IImsUt;
+
+/**
+ * A container of the IImsServiceController binder, which implements all of the ImsFeatures that
+ * the platform currently supports: MMTel and RCS.
+ * @hide
+ */
+
+public class ImsServiceProxy extends ImsServiceProxyCompat implements IRcsFeature {
+
+ protected String LOG_TAG = "ImsServiceProxy";
+ private final int mSupportedFeature;
+
+ // Start by assuming the proxy is available for usage.
+ private boolean mIsAvailable = true;
+ // ImsFeature Status from the ImsService. Cached.
+ private Integer mFeatureStatusCached = null;
+ private ImsServiceProxy.INotifyStatusChanged mStatusCallback;
+ private final Object mLock = new Object();
+
+ public interface INotifyStatusChanged {
+ void notifyStatusChanged();
+ }
+
+ private final IImsServiceFeatureListener mListenerBinder =
+ new IImsServiceFeatureListener.Stub() {
+
+ @Override
+ public void imsFeatureCreated(int slotId, int feature) throws RemoteException {
+ // The feature has been re-enabled. This may happen when the service crashes.
+ synchronized (mLock) {
+ if (!mIsAvailable && mSlotId == slotId && feature == mSupportedFeature) {
+ Log.i(LOG_TAG, "Feature enabled on slotId: " + slotId + " for feature: " +
+ feature);
+ mIsAvailable = true;
+ }
+ }
+ }
+
+ @Override
+ public void imsFeatureRemoved(int slotId, int feature) throws RemoteException {
+ synchronized (mLock) {
+ if (mIsAvailable && mSlotId == slotId && feature == mSupportedFeature) {
+ Log.i(LOG_TAG, "Feature disabled on slotId: " + slotId + " for feature: " +
+ feature);
+ mIsAvailable = false;
+ }
+ }
+ }
+
+ @Override
+ public void imsStatusChanged(int slotId, int feature, int status) throws RemoteException {
+ synchronized (mLock) {
+ Log.i(LOG_TAG, "imsStatusChanged: slot: " + slotId + " feature: " + feature +
+ " status: " + status);
+ if (mSlotId == slotId && feature == mSupportedFeature) {
+ mFeatureStatusCached = status;
+ }
+ }
+ if (mStatusCallback != null) {
+ mStatusCallback.notifyStatusChanged();
+ }
+ }
+ };
+
+ public ImsServiceProxy(int slotId, IBinder binder, int featureType) {
+ super(slotId, binder);
+ mSupportedFeature = featureType;
+ }
+
+ public ImsServiceProxy(int slotId, int featureType) {
+ super(slotId, null /*IBinder*/);
+ mSupportedFeature = featureType;
+ }
+
+ public IImsServiceFeatureListener getListener() {
+ return mListenerBinder;
+ }
+
+ public void setBinder(IBinder binder) {
+ mBinder = binder;
+ }
+
+ @Override
+ public int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener)
+ throws RemoteException {
+ synchronized (mLock) {
+ checkBinderConnection();
+ return getServiceInterface(mBinder).startSession(mSlotId, mSupportedFeature,
+ incomingCallIntent, listener);
+ }
+ }
+
+ @Override
+ public void endSession(int sessionId) throws RemoteException {
+ synchronized (mLock) {
+ checkBinderConnection();
+ getServiceInterface(mBinder).endSession(mSlotId, mSupportedFeature, sessionId);
+ }
+ }
+
+ @Override
+ public boolean isConnected(int callServiceType, int callType)
+ throws RemoteException {
+ synchronized (mLock) {
+ checkBinderConnection();
+ return getServiceInterface(mBinder).isConnected(mSlotId, mSupportedFeature,
+ callServiceType, callType);
+ }
+ }
+
+ @Override
+ public boolean isOpened() throws RemoteException {
+ synchronized (mLock) {
+ checkBinderConnection();
+ return getServiceInterface(mBinder).isOpened(mSlotId, mSupportedFeature);
+ }
+ }
+
+ @Override
+ public void addRegistrationListener(IImsRegistrationListener listener)
+ throws RemoteException {
+ synchronized (mLock) {
+ checkBinderConnection();
+ getServiceInterface(mBinder).addRegistrationListener(mSlotId, mSupportedFeature,
+ listener);
+ }
+ }
+
+ @Override
+ public void removeRegistrationListener(IImsRegistrationListener listener)
+ throws RemoteException {
+ synchronized (mLock) {
+ checkBinderConnection();
+ getServiceInterface(mBinder).removeRegistrationListener(mSlotId, mSupportedFeature,
+ listener);
+ }
+ }
+
+ @Override
+ public ImsCallProfile createCallProfile(int sessionId, int callServiceType, int callType)
+ throws RemoteException {
+ synchronized (mLock) {
+ checkBinderConnection();
+ return getServiceInterface(mBinder).createCallProfile(mSlotId, mSupportedFeature,
+ sessionId, callServiceType, callType);
+ }
+ }
+
+ @Override
+ public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
+ IImsCallSessionListener listener) throws RemoteException {
+ synchronized (mLock) {
+ checkBinderConnection();
+ return getServiceInterface(mBinder).createCallSession(mSlotId, mSupportedFeature,
+ sessionId, profile, listener);
+ }
+ }
+
+ @Override
+ public IImsCallSession getPendingCallSession(int sessionId, String callId)
+ throws RemoteException {
+ synchronized (mLock) {
+ checkBinderConnection();
+ return getServiceInterface(mBinder).getPendingCallSession(mSlotId, mSupportedFeature,
+ sessionId, callId);
+ }
+ }
+
+ @Override
+ public IImsUt getUtInterface() throws RemoteException {
+ synchronized (mLock) {
+ checkBinderConnection();
+ return getServiceInterface(mBinder).getUtInterface(mSlotId, mSupportedFeature);
+ }
+ }
+
+ @Override
+ public IImsConfig getConfigInterface() throws RemoteException {
+ synchronized (mLock) {
+ checkBinderConnection();
+ return getServiceInterface(mBinder).getConfigInterface(mSlotId, mSupportedFeature);
+ }
+ }
+
+ @Override
+ public void turnOnIms() throws RemoteException {
+ synchronized (mLock) {
+ checkBinderConnection();
+ getServiceInterface(mBinder).turnOnIms(mSlotId, mSupportedFeature);
+ }
+ }
+
+ @Override
+ public void turnOffIms() throws RemoteException {
+ synchronized (mLock) {
+ checkBinderConnection();
+ getServiceInterface(mBinder).turnOffIms(mSlotId, mSupportedFeature);
+ }
+ }
+
+ @Override
+ public IImsEcbm getEcbmInterface() throws RemoteException {
+ synchronized (mLock) {
+ checkBinderConnection();
+ return getServiceInterface(mBinder).getEcbmInterface(mSlotId, mSupportedFeature);
+ }
+ }
+
+ @Override
+ public void setUiTTYMode(int uiTtyMode, Message onComplete)
+ throws RemoteException {
+ synchronized (mLock) {
+ checkBinderConnection();
+ getServiceInterface(mBinder).setUiTTYMode(mSlotId, mSupportedFeature, uiTtyMode,
+ onComplete);
+ }
+ }
+
+ @Override
+ public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
+ synchronized (mLock) {
+ checkBinderConnection();
+ return getServiceInterface(mBinder).getMultiEndpointInterface(mSlotId,
+ mSupportedFeature);
+ }
+ }
+
+ @Override
+ public int getFeatureStatus() {
+ synchronized (mLock) {
+ if (mFeatureStatusCached != null) {
+ return mFeatureStatusCached;
+ }
+ }
+ // Don't synchronize on Binder call.
+ Integer status = retrieveFeatureStatus();
+ synchronized (mLock) {
+ if (status == null) {
+ return ImsFeature.STATE_NOT_AVAILABLE;
+ }
+ // Cache only non-null value for feature status.
+ mFeatureStatusCached = status;
+ }
+ return status;
+ }
+
+ /**
+ * Internal method used to retrieve the feature status from the corresponding ImsService.
+ */
+ private Integer retrieveFeatureStatus() {
+ if (mBinder != null) {
+ try {
+ return getServiceInterface(mBinder).getFeatureStatus(mSlotId, mSupportedFeature);
+ } catch (RemoteException e) {
+ // Status check failed, don't update cache
+ }
+ }
+ return null;
+ }
+
+ /**
+ * @param c Callback that will fire when the feature status has changed.
+ */
+ public void setStatusCallback(INotifyStatusChanged c) {
+ mStatusCallback = c;
+ }
+
+ @Override
+ public boolean isBinderAlive() {
+ return mIsAvailable && getFeatureStatus() == ImsFeature.STATE_READY && mBinder != null &&
+ mBinder.isBinderAlive();
+ }
+
+ private IImsServiceController getServiceInterface(IBinder b) {
+ return IImsServiceController.Stub.asInterface(b);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/ImsServiceProxyCompat.java b/telephony/java/android/telephony/ims/ImsServiceProxyCompat.java
new file mode 100644
index 0000000..bbd5f02
--- /dev/null
+++ b/telephony/java/android/telephony/ims/ImsServiceProxyCompat.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2017 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.ims;
+
+import android.app.PendingIntent;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telephony.ims.feature.IMMTelFeature;
+import android.telephony.ims.feature.ImsFeature;
+
+import com.android.ims.ImsCallProfile;
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsCallSessionListener;
+import com.android.ims.internal.IImsConfig;
+import com.android.ims.internal.IImsEcbm;
+import com.android.ims.internal.IImsMultiEndpoint;
+import com.android.ims.internal.IImsRegistrationListener;
+import com.android.ims.internal.IImsService;
+import com.android.ims.internal.IImsUt;
+
+/**
+ * Compatibility class that implements the new ImsService IMMTelFeature interface, but
+ * uses the old IImsService interface to support older devices that implement the deprecated
+ * opt/net/ims interface.
+ * @hide
+ */
+
+public class ImsServiceProxyCompat implements IMMTelFeature {
+
+ private static final int SERVICE_ID = ImsFeature.MMTEL;
+
+ protected final int mSlotId;
+ protected IBinder mBinder;
+
+ public ImsServiceProxyCompat(int slotId, IBinder binder) {
+ mSlotId = slotId;
+ mBinder = binder;
+ }
+
+ @Override
+ public int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener)
+ throws RemoteException {
+ checkBinderConnection();
+ return getServiceInterface(mBinder).open(mSlotId, ImsFeature.MMTEL, incomingCallIntent,
+ listener);
+ }
+
+ @Override
+ public void endSession(int sessionId) throws RemoteException {
+ checkBinderConnection();
+ getServiceInterface(mBinder).close(sessionId);
+ }
+
+ @Override
+ public boolean isConnected(int callServiceType, int callType)
+ throws RemoteException {
+ checkBinderConnection();
+ return getServiceInterface(mBinder).isConnected(SERVICE_ID, callServiceType, callType);
+ }
+
+ @Override
+ public boolean isOpened() throws RemoteException {
+ checkBinderConnection();
+ return getServiceInterface(mBinder).isOpened(SERVICE_ID);
+ }
+
+ @Override
+ public void addRegistrationListener(IImsRegistrationListener listener)
+ throws RemoteException {
+ checkBinderConnection();
+ getServiceInterface(mBinder).addRegistrationListener(mSlotId, ImsFeature.MMTEL, listener);
+ }
+
+ @Override
+ public void removeRegistrationListener(IImsRegistrationListener listener)
+ throws RemoteException {
+ // Not Implemented in old ImsService. If the registration listener becomes invalid, the
+ // ImsService will remove.
+ }
+
+ @Override
+ public ImsCallProfile createCallProfile(int sessionId, int callServiceType, int callType)
+ throws RemoteException {
+ checkBinderConnection();
+ return getServiceInterface(mBinder).createCallProfile(sessionId, callServiceType, callType);
+ }
+
+ @Override
+ public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
+ IImsCallSessionListener listener) throws RemoteException {
+ checkBinderConnection();
+ return getServiceInterface(mBinder).createCallSession(sessionId, profile, listener);
+ }
+
+ @Override
+ public IImsCallSession getPendingCallSession(int sessionId, String callId)
+ throws RemoteException {
+ checkBinderConnection();
+ return getServiceInterface(mBinder).getPendingCallSession(sessionId, callId);
+ }
+
+ @Override
+ public IImsUt getUtInterface() throws RemoteException {
+ checkBinderConnection();
+ return getServiceInterface(mBinder).getUtInterface(SERVICE_ID);
+ }
+
+ @Override
+ public IImsConfig getConfigInterface() throws RemoteException {
+ checkBinderConnection();
+ return getServiceInterface(mBinder).getConfigInterface(mSlotId);
+ }
+
+ @Override
+ public void turnOnIms() throws RemoteException {
+ checkBinderConnection();
+ getServiceInterface(mBinder).turnOnIms(mSlotId);
+ }
+
+ @Override
+ public void turnOffIms() throws RemoteException {
+ checkBinderConnection();
+ getServiceInterface(mBinder).turnOffIms(mSlotId);
+ }
+
+ @Override
+ public IImsEcbm getEcbmInterface() throws RemoteException {
+ checkBinderConnection();
+ return getServiceInterface(mBinder).getEcbmInterface(SERVICE_ID);
+ }
+
+ @Override
+ public void setUiTTYMode(int uiTtyMode, Message onComplete)
+ throws RemoteException {
+ checkBinderConnection();
+ getServiceInterface(mBinder).setUiTTYMode(SERVICE_ID, uiTtyMode, onComplete);
+ }
+
+ @Override
+ public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
+ checkBinderConnection();
+ return getServiceInterface(mBinder).getMultiEndpointInterface(SERVICE_ID);
+ }
+
+ /**
+ * Base implementation, always returns READY for compatibility with old ImsService.
+ */
+ public int getFeatureStatus() {
+ return ImsFeature.STATE_READY;
+ }
+
+ /**
+ * @return false if the binder connection is no longer alive.
+ */
+ public boolean isBinderAlive() {
+ return mBinder != null && mBinder.isBinderAlive();
+ }
+
+ private IImsService getServiceInterface(IBinder b) {
+ return IImsService.Stub.asInterface(b);
+ }
+
+ protected void checkBinderConnection() throws RemoteException {
+ if (!isBinderAlive()) {
+ throw new RemoteException("ImsServiceProxy is not available for that feature.");
+ }
+ }
+}
diff --git a/telephony/java/android/telephony/ims/feature/IMMTelFeature.java b/telephony/java/android/telephony/ims/feature/IMMTelFeature.java
new file mode 100644
index 0000000..d65e27e
--- /dev/null
+++ b/telephony/java/android/telephony/ims/feature/IMMTelFeature.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2017 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.ims.feature;
+
+import android.app.PendingIntent;
+import android.os.Message;
+import android.os.RemoteException;
+
+import com.android.ims.ImsCallProfile;
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsCallSessionListener;
+import com.android.ims.internal.IImsConfig;
+import com.android.ims.internal.IImsEcbm;
+import com.android.ims.internal.IImsMultiEndpoint;
+import com.android.ims.internal.IImsRegistrationListener;
+import com.android.ims.internal.IImsUt;
+
+/**
+ * MMTel interface for an ImsService. When updating this interface, ensure that base implementations
+ * of your changes are also present in MMTelFeature for compatibility with older versions of the
+ * MMTel feature.
+ * @hide
+ */
+
+public interface IMMTelFeature {
+
+ /**
+ * Notifies the MMTel feature that you would like to start a session. This should always be
+ * done before making/receiving IMS calls. The IMS service will register the device to the
+ * operator's network with the credentials (from ISIM) periodically in order to receive calls
+ * from the operator's network. When the IMS service receives a new call, it will send out an
+ * intent with the provided action string. The intent contains a call ID extra
+ * {@link IImsCallSession#getCallId} and it can be used to take a call.
+ *
+ * @param incomingCallIntent When an incoming call is received, the IMS service will call
+ * {@link PendingIntent#send} to send back the intent to the caller with
+ * {@link #INCOMING_CALL_RESULT_CODE} as the result code and the intent to fill in the call ID;
+ * It cannot be null.
+ * @param listener To listen to IMS registration events; It cannot be null
+ * @return an integer (greater than 0) representing the session id associated with the session
+ * that has been started.
+ */
+ int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener)
+ throws RemoteException;
+
+ /**
+ * End a previously started session using the associated sessionId.
+ * @param sessionId an integer (greater than 0) representing the ongoing session. See
+ * {@link #startSession}.
+ */
+ void endSession(int sessionId) throws RemoteException;
+
+ /**
+ * Checks if the IMS service has successfully registered to the IMS network with the specified
+ * service & call type.
+ *
+ * @param callServiceType a service type that is specified in {@link ImsCallProfile}
+ * {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
+ * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
+ * @param callType a call type that is specified in {@link ImsCallProfile}
+ * {@link ImsCallProfile#CALL_TYPE_VOICE_N_VIDEO}
+ * {@link ImsCallProfile#CALL_TYPE_VOICE}
+ * {@link ImsCallProfile#CALL_TYPE_VT}
+ * {@link ImsCallProfile#CALL_TYPE_VS}
+ * @return true if the specified service id is connected to the IMS network; false otherwise
+ * @throws RemoteException
+ */
+ boolean isConnected(int callServiceType, int callType) throws RemoteException;
+
+ /**
+ * Checks if the specified IMS service is opened.
+ *
+ * @return true if the specified service id is opened; false otherwise
+ */
+ boolean isOpened() throws RemoteException;
+
+ /**
+ * Add a new registration listener for the client associated with the session Id.
+ * @param listener An implementation of IImsRegistrationListener.
+ */
+ void addRegistrationListener(IImsRegistrationListener listener)
+ throws RemoteException;
+
+ /**
+ * Remove a previously registered listener using {@link #addRegistrationListener} for the client
+ * associated with the session Id.
+ * @param listener A previously registered IImsRegistrationListener
+ */
+ void removeRegistrationListener(IImsRegistrationListener listener)
+ throws RemoteException;
+
+ /**
+ * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
+ *
+ * @param sessionId a session id which is obtained from {@link #startSession}
+ * @param callServiceType a service type that is specified in {@link ImsCallProfile}
+ * {@link ImsCallProfile#SERVICE_TYPE_NONE}
+ * {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
+ * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
+ * @param callType a call type that is specified in {@link ImsCallProfile}
+ * {@link ImsCallProfile#CALL_TYPE_VOICE}
+ * {@link ImsCallProfile#CALL_TYPE_VT}
+ * {@link ImsCallProfile#CALL_TYPE_VT_TX}
+ * {@link ImsCallProfile#CALL_TYPE_VT_RX}
+ * {@link ImsCallProfile#CALL_TYPE_VT_NODIR}
+ * {@link ImsCallProfile#CALL_TYPE_VS}
+ * {@link ImsCallProfile#CALL_TYPE_VS_TX}
+ * {@link ImsCallProfile#CALL_TYPE_VS_RX}
+ * @return a {@link ImsCallProfile} object
+ */
+ ImsCallProfile createCallProfile(int sessionId, int callServiceType, int callType)
+ throws RemoteException;
+
+ /**
+ * Creates a {@link ImsCallSession} with the specified call profile.
+ * Use other methods, if applicable, instead of interacting with
+ * {@link ImsCallSession} directly.
+ *
+ * @param sessionId a session id which is obtained from {@link #startSession}
+ * @param profile a call profile to make the call
+ * @param listener An implementation of IImsCallSessionListener.
+ */
+ IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
+ IImsCallSessionListener listener) throws RemoteException;
+
+ /**
+ * Retrieves the call session associated with a pending call.
+ *
+ * @param sessionId a session id which is obtained from {@link #startSession}
+ * @param callId a call id to make the call
+ */
+ IImsCallSession getPendingCallSession(int sessionId, String callId) throws RemoteException;
+
+ /**
+ * @return The Ut interface for the supplementary service configuration.
+ */
+ IImsUt getUtInterface() throws RemoteException;
+
+ /**
+ * @return The config interface for IMS Configuration
+ */
+ IImsConfig getConfigInterface() throws RemoteException;
+
+ /**
+ * Signal the MMTelFeature to turn on IMS when it has been turned off using {@link #turnOffIms}
+ * @param sessionId a session id which is obtained from {@link #startSession}
+ */
+ void turnOnIms() throws RemoteException;
+
+ /**
+ * Signal the MMTelFeature to turn off IMS when it has been turned on using {@link #turnOnIms}
+ * @param sessionId a session id which is obtained from {@link #startSession}
+ */
+ void turnOffIms() throws RemoteException;
+
+ /**
+ * @return The Emergency call-back mode interface for emergency VoLTE calls that support it.
+ */
+ IImsEcbm getEcbmInterface() throws RemoteException;
+
+ /**
+ * Sets the current UI TTY mode for the MMTelFeature.
+ * @param uiTtyMode An integer containing the new UI TTY Mode.
+ * @param onComplete A {@link Message} to be used when the mode has been set.
+ * @throws RemoteException
+ */
+ void setUiTTYMode(int uiTtyMode, Message onComplete) throws RemoteException;
+
+ /**
+ * @return MultiEndpoint interface for DEP notifications
+ */
+ IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException;
+}
diff --git a/telephony/java/android/telephony/ims/feature/IRcsFeature.java b/telephony/java/android/telephony/ims/feature/IRcsFeature.java
new file mode 100644
index 0000000..e28e1b3
--- /dev/null
+++ b/telephony/java/android/telephony/ims/feature/IRcsFeature.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2017 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.ims.feature;
+
+/**
+ * Feature interface that provides access to RCS APIs. Currently empty until RCS support is added
+ * in the framework.
+ * @hide
+ */
+
+public interface IRcsFeature {
+}
diff --git a/telephony/java/android/telephony/ims/feature/ImsFeature.java b/telephony/java/android/telephony/ims/feature/ImsFeature.java
index 0509d60..988dd58 100644
--- a/telephony/java/android/telephony/ims/feature/ImsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/ImsFeature.java
@@ -16,18 +16,175 @@
package android.telephony.ims.feature;
+import android.annotation.IntDef;
+import android.content.Context;
+import android.content.Intent;
+import android.os.RemoteException;
+import android.telephony.SubscriptionManager;
+import android.util.Log;
+
+import com.android.ims.internal.IImsFeatureStatusCallback;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Base class for all IMS features that are supported by the framework.
* @hide
*/
-public class ImsFeature {
+public abstract class ImsFeature {
+
+ private static final String LOG_TAG = "ImsFeature";
+
+ /**
+ * Action to broadcast when ImsService is up.
+ * Internal use only.
+ * Only defined here separately compatibility purposes with the old ImsService.
+ * @hide
+ */
+ public static final String ACTION_IMS_SERVICE_UP =
+ "com.android.ims.IMS_SERVICE_UP";
+
+ /**
+ * Action to broadcast when ImsService is down.
+ * Internal use only.
+ * Only defined here separately for compatibility purposes with the old ImsService.
+ * @hide
+ */
+ public static final String ACTION_IMS_SERVICE_DOWN =
+ "com.android.ims.IMS_SERVICE_DOWN";
+
+ /**
+ * Part of the ACTION_IMS_SERVICE_UP or _DOWN intents.
+ * A long value; the phone ID corresponding to the IMS service coming up or down.
+ * Only defined here separately for compatibility purposes with the old ImsService.
+ * @hide
+ */
+ public static final String EXTRA_PHONE_ID = "android:phone_id";
// Invalid feature value
public static final int INVALID = -1;
- // ImsFeatures that are defined in the Manifests
+ // ImsFeatures that are defined in the Manifests. Ensure that these values match the previously
+ // defined values in ImsServiceClass for compatibility purposes.
public static final int EMERGENCY_MMTEL = 0;
public static final int MMTEL = 1;
public static final int RCS = 2;
// Total number of features defined
public static final int MAX = 3;
+
+ // Integer values defining the state of the ImsFeature at any time.
+ @IntDef(flag = true,
+ value = {
+ STATE_NOT_AVAILABLE,
+ STATE_INITIALIZING,
+ STATE_READY,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ImsState {}
+ public static final int STATE_NOT_AVAILABLE = 0;
+ public static final int STATE_INITIALIZING = 1;
+ public static final int STATE_READY = 2;
+
+ private List<INotifyFeatureRemoved> mRemovedListeners = new ArrayList<>();
+ private IImsFeatureStatusCallback mStatusCallback;
+ private @ImsState int mState = STATE_NOT_AVAILABLE;
+ private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
+ private Context mContext;
+
+ public interface INotifyFeatureRemoved {
+ void onFeatureRemoved(int slotId);
+ }
+
+ public void setContext(Context context) {
+ mContext = context;
+ }
+
+ public void setSlotId(int slotId) {
+ mSlotId = slotId;
+ }
+
+ public void addFeatureRemovedListener(INotifyFeatureRemoved listener) {
+ synchronized (mRemovedListeners) {
+ mRemovedListeners.add(listener);
+ }
+ }
+
+ public void removeFeatureRemovedListener(INotifyFeatureRemoved listener) {
+ synchronized (mRemovedListeners) {
+ mRemovedListeners.remove(listener);
+ }
+ }
+
+ // Not final for testing.
+ public void notifyFeatureRemoved(int slotId) {
+ synchronized (mRemovedListeners) {
+ mRemovedListeners.forEach(l -> l.onFeatureRemoved(slotId));
+ onFeatureRemoved();
+ }
+ }
+
+ public int getFeatureState() {
+ return mState;
+ }
+
+ protected final void setFeatureState(@ImsState int state) {
+ if (mState != state) {
+ mState = state;
+ notifyFeatureState(state);
+ }
+ }
+
+ // Not final for testing.
+ public void setImsFeatureStatusCallback(IImsFeatureStatusCallback c) {
+ mStatusCallback = c;
+ // If we have just connected, send queued status.
+ notifyFeatureState(mState);
+ }
+
+ /**
+ * Internal method called by ImsFeature when setFeatureState has changed.
+ * @param state
+ */
+ private void notifyFeatureState(@ImsState int state) {
+ if (mStatusCallback != null) {
+ try {
+ Log.i(LOG_TAG, "notifying ImsFeatureState");
+ mStatusCallback.notifyImsFeatureStatus(state);
+ } catch (RemoteException e) {
+ mStatusCallback = null;
+ Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage());
+ }
+ }
+ sendImsServiceIntent(state);
+ }
+
+ /**
+ * Provide backwards compatibility using deprecated service UP/DOWN intents.
+ */
+ private void sendImsServiceIntent(@ImsState int state) {
+ if(mContext == null || mSlotId == SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
+ return;
+ }
+ Intent intent;
+ switch (state) {
+ case ImsFeature.STATE_NOT_AVAILABLE:
+ case ImsFeature.STATE_INITIALIZING:
+ intent = new Intent(ACTION_IMS_SERVICE_DOWN);
+ break;
+ case ImsFeature.STATE_READY:
+ intent = new Intent(ACTION_IMS_SERVICE_UP);
+ break;
+ default:
+ intent = new Intent(ACTION_IMS_SERVICE_DOWN);
+ }
+ intent.putExtra(EXTRA_PHONE_ID, mSlotId);
+ mContext.sendBroadcast(intent);
+ }
+
+ /**
+ * Called when the feature is being removed and must be cleaned up.
+ */
+ public abstract void onFeatureRemoved();
}
diff --git a/telephony/java/android/telephony/ims/feature/MMTelFeature.java b/telephony/java/android/telephony/ims/feature/MMTelFeature.java
new file mode 100644
index 0000000..a71f0bf
--- /dev/null
+++ b/telephony/java/android/telephony/ims/feature/MMTelFeature.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2017 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.ims.feature;
+
+import android.app.PendingIntent;
+import android.os.Message;
+
+import com.android.ims.ImsCallProfile;
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsCallSessionListener;
+import com.android.ims.internal.IImsConfig;
+import com.android.ims.internal.IImsEcbm;
+import com.android.ims.internal.IImsMultiEndpoint;
+import com.android.ims.internal.IImsRegistrationListener;
+import com.android.ims.internal.IImsUt;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Base implementation, which implements all methods in IMMTelFeature. Any class wishing to use
+ * MMTelFeature should extend this class and implement all methods that the service supports.
+ *
+ * @hide
+ */
+
+public class MMTelFeature extends ImsFeature implements IMMTelFeature {
+
+ @Override
+ public int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener) {
+ return 0;
+ }
+
+ @Override
+ public void endSession(int sessionId) {
+ }
+
+ @Override
+ public boolean isConnected(int callSessionType, int callType) {
+ return false;
+ }
+
+ @Override
+ public boolean isOpened() {
+ return false;
+ }
+
+ @Override
+ public void addRegistrationListener(IImsRegistrationListener listener) {
+ }
+
+ @Override
+ public void removeRegistrationListener(IImsRegistrationListener listener) {
+ }
+
+ @Override
+ public ImsCallProfile createCallProfile(int sessionId, int callSessionType, int callType) {
+ return null;
+ }
+
+ @Override
+ public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
+ IImsCallSessionListener listener) {
+ return null;
+ }
+
+ @Override
+ public IImsCallSession getPendingCallSession(int sessionId, String callId) {
+ return null;
+ }
+
+ @Override
+ public IImsUt getUtInterface() {
+ return null;
+ }
+
+ @Override
+ public IImsConfig getConfigInterface() {
+ return null;
+ }
+
+ @Override
+ public void turnOnIms() {
+ }
+
+ @Override
+ public void turnOffIms() {
+ }
+
+ @Override
+ public IImsEcbm getEcbmInterface() {
+ return null;
+ }
+
+ @Override
+ public void setUiTTYMode(int uiTtyMode, Message onComplete) {
+ }
+
+ @Override
+ public IImsMultiEndpoint getMultiEndpointInterface() {
+ return null;
+ }
+
+ @Override
+ public void onFeatureRemoved() {
+
+ }
+}
diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java
new file mode 100644
index 0000000..9cddc1b
--- /dev/null
+++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 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.ims.feature;
+
+/**
+ * Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend
+ * this class and provide implementations of the IRcsFeature methods that they support.
+ * @hide
+ */
+
+public class RcsFeature extends ImsFeature implements IRcsFeature {
+
+ public RcsFeature() {
+ super();
+ }
+
+ @Override
+ public void onFeatureRemoved() {
+
+ }
+}
diff --git a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
new file mode 100644
index 0000000..69b8acc
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2017 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.ims.stub;
+
+import android.os.Message;
+import android.os.RemoteException;
+
+import com.android.ims.ImsCallProfile;
+import com.android.ims.ImsStreamMediaProfile;
+import com.android.ims.internal.ImsCallSession;
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsCallSessionListener;
+import com.android.ims.internal.IImsVideoCallProvider;
+
+/**
+ * Base implementation of IImsCallSession, which implements stub versions of the methods in the
+ * IImsCallSession AIDL. Override the methods that your implementation of ImsCallSession supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsCallSession maintained by other ImsServices.
+ *
+ * @hide
+ */
+
+public class ImsCallSessionImplBase extends IImsCallSession.Stub {
+
+ /**
+ * Closes the object. This object is not usable after being closed.
+ */
+ @Override
+ public void close() throws RemoteException {
+
+ }
+
+ /**
+ * Gets the call ID of the session.
+ *
+ * @return the call ID
+ */
+ @Override
+ public String getCallId() throws RemoteException {
+ return null;
+ }
+
+ /**
+ * Gets the call profile that this session is associated with
+ *
+ * @return the {@link ImsCallProfile} that this session is associated with
+ */
+ @Override
+ public ImsCallProfile getCallProfile() throws RemoteException {
+ return null;
+ }
+
+ /**
+ * Gets the local call profile that this session is associated with
+ *
+ * @return the local {@link ImsCallProfile} that this session is associated with
+ */
+ @Override
+ public ImsCallProfile getLocalCallProfile() throws RemoteException {
+ return null;
+ }
+
+ /**
+ * Gets the remote call profile that this session is associated with
+ *
+ * @return the remote {@link ImsCallProfile} that this session is associated with
+ */
+ @Override
+ public ImsCallProfile getRemoteCallProfile() throws RemoteException {
+ return null;
+ }
+
+ /**
+ * Gets the value associated with the specified property of this session.
+ *
+ * @return the string value associated with the specified property
+ */
+ @Override
+ public String getProperty(String name) throws RemoteException {
+ return null;
+ }
+
+ /**
+ * Gets the session state.
+ * The value returned must be one of the states in {@link ImsCallSession.State}.
+ *
+ * @return the session state
+ */
+ @Override
+ public int getState() throws RemoteException {
+ return ImsCallSession.State.INVALID;
+ }
+
+ /**
+ * Checks if the session is in call.
+ *
+ * @return true if the session is in call, false otherwise
+ */
+ @Override
+ public boolean isInCall() throws RemoteException {
+ return false;
+ }
+
+ /**
+ * Sets the listener to listen to the session events. An {@link ImsCallSession}
+ * can only hold one listener at a time. Subsequent calls to this method
+ * override the previous listener.
+ *
+ * @param listener to listen to the session events of this object
+ */
+ @Override
+ public void setListener(IImsCallSessionListener listener) throws RemoteException {
+ }
+
+ /**
+ * Mutes or unmutes the mic for the active call.
+ *
+ * @param muted true if the call is muted, false otherwise
+ */
+ @Override
+ public void setMute(boolean muted) throws RemoteException {
+ }
+
+ /**
+ * Initiates an IMS call with the specified target and call profile.
+ * The session listener set in {@link #setListener} is called back upon defined session events.
+ * The method is only valid to call when the session state is in
+ * {@link ImsCallSession.State#IDLE}.
+ *
+ * @param callee dialed string to make the call to
+ * @param profile call profile to make the call with the specified service type,
+ * call type and media information
+ * @see {@link ImsCallSession.Listener#callSessionStarted},
+ * {@link ImsCallSession.Listener#callSessionStartFailed}
+ */
+ @Override
+ public void start(String callee, ImsCallProfile profile) throws RemoteException {
+ }
+
+ /**
+ * Initiates an IMS call with the specified participants and call profile.
+ * The session listener set in {@link #setListener} is called back upon defined session events.
+ * The method is only valid to call when the session state is in
+ * {@link ImsCallSession.State#IDLE}.
+ *
+ * @param participants participant list to initiate an IMS conference call
+ * @param profile call profile to make the call with the specified service type,
+ * call type and media information
+ * @see {@link ImsCallSession.Listener#callSessionStarted},
+ * {@link ImsCallSession.Listener#callSessionStartFailed}
+ */
+ @Override
+ public void startConference(String[] participants, ImsCallProfile profile)
+ throws RemoteException {
+ }
+
+ /**
+ * Accepts an incoming call or session update.
+ *
+ * @param callType call type specified in {@link ImsCallProfile} to be answered
+ * @param profile stream media profile {@link ImsStreamMediaProfile} to be answered
+ * @see {@link ImsCallSession.Listener#callSessionStarted}
+ */
+ @Override
+ public void accept(int callType, ImsStreamMediaProfile profile) throws RemoteException {
+ }
+
+ /**
+ * Rejects an incoming call or session update.
+ *
+ * @param reason reason code to reject an incoming call, defined in
+ * com.android.ims.ImsReasonInfo
+ * {@link ImsCallSession.Listener#callSessionStartFailed}
+ */
+ @Override
+ public void reject(int reason) throws RemoteException {
+ }
+
+ /**
+ * Terminates a call.
+ *
+ * @param reason reason code to terminate a call, defined in
+ * com.android.ims.ImsReasonInfo
+ *
+ * @see {@link ImsCallSession.Listener#callSessionTerminated}
+ */
+ @Override
+ public void terminate(int reason) throws RemoteException {
+ }
+
+ /**
+ * Puts a call on hold. When it succeeds, {@link ImsCallSession.Listener#callSessionHeld} is
+ * called.
+ *
+ * @param profile stream media profile {@link ImsStreamMediaProfile} to hold the call
+ * @see {@link ImsCallSession.Listener#callSessionHeld},
+ * {@link ImsCallSession.Listener#callSessionHoldFailed}
+ */
+ @Override
+ public void hold(ImsStreamMediaProfile profile) throws RemoteException {
+ }
+
+ /**
+ * Continues a call that's on hold. When it succeeds,
+ * {@link ImsCallSession.Listener#callSessionResumed} is called.
+ *
+ * @param profile stream media profile with {@link ImsStreamMediaProfile} to resume the call
+ * @see {@link ImsCallSession.Listener#callSessionResumed},
+ * {@link ImsCallSession.Listener#callSessionResumeFailed}
+ */
+ @Override
+ public void resume(ImsStreamMediaProfile profile) throws RemoteException {
+ }
+
+ /**
+ * Merges the active & hold call. When the merge starts,
+ * {@link ImsCallSession.Listener#callSessionMergeStarted} is called.
+ * {@link ImsCallSession.Listener#callSessionMergeComplete} is called if the merge is
+ * successful, and {@link ImsCallSession.Listener#callSessionMergeFailed} is called if the merge
+ * fails.
+ *
+ * @see {@link ImsCallSession.Listener#callSessionMergeStarted},
+ * {@link ImsCallSession.Listener#callSessionMergeComplete},
+ * {@link ImsCallSession.Listener#callSessionMergeFailed}
+ */
+ @Override
+ public void merge() throws RemoteException {
+ }
+
+ /**
+ * Updates the current call's properties (ex. call mode change: video upgrade / downgrade).
+ *
+ * @param callType call type specified in {@link ImsCallProfile} to be updated
+ * @param profile stream media profile {@link ImsStreamMediaProfile} to be updated
+ * @see {@link ImsCallSession.Listener#callSessionUpdated},
+ * {@link ImsCallSession.Listener#callSessionUpdateFailed}
+ */
+ @Override
+ public void update(int callType, ImsStreamMediaProfile profile) throws RemoteException {
+ }
+
+ /**
+ * Extends this call to the conference call with the specified recipients.
+ *
+ * @param participants participant list to be invited to the conference call after extending the
+ * call
+ * @see {@link ImsCallSession.Listener#callSessionConferenceExtended},
+ * {@link ImsCallSession.Listener#callSessionConferenceExtendFailed}
+ */
+ @Override
+ public void extendToConference(String[] participants) throws RemoteException {
+ }
+
+ /**
+ * Requests the conference server to invite an additional participants to the conference.
+ *
+ * @param participants participant list to be invited to the conference call
+ * @see {@link ImsCallSession.Listener#callSessionInviteParticipantsRequestDelivered},
+ * {@link ImsCallSession.Listener#callSessionInviteParticipantsRequestFailed}
+ */
+ @Override
+ public void inviteParticipants(String[] participants) throws RemoteException {
+ }
+
+ /**
+ * Requests the conference server to remove the specified participants from the conference.
+ *
+ * @param participants participant list to be removed from the conference call
+ * @see {@link ImsCallSession.Listener#callSessionRemoveParticipantsRequestDelivered},
+ * {@link ImsCallSession.Listener#callSessionRemoveParticipantsRequestFailed}
+ */
+ @Override
+ public void removeParticipants(String[] participants) throws RemoteException {
+ }
+
+ /**
+ * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
+ * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
+ * and event flash to 16. Currently, event flash is not supported.
+ *
+ * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
+ */
+ @Override
+ public void sendDtmf(char c, Message result) throws RemoteException {
+ }
+
+ /**
+ * Start a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
+ * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
+ * and event flash to 16. Currently, event flash is not supported.
+ *
+ * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
+ */
+ @Override
+ public void startDtmf(char c) throws RemoteException {
+ }
+
+ /**
+ * Stop a DTMF code.
+ */
+ @Override
+ public void stopDtmf() throws RemoteException {
+ }
+
+ /**
+ * Sends an USSD message.
+ *
+ * @param ussdMessage USSD message to send
+ */
+ @Override
+ public void sendUssd(String ussdMessage) throws RemoteException {
+ }
+
+ /**
+ * Returns a binder for the video call provider implementation contained within the IMS service
+ * process. This binder is used by the VideoCallProvider subclass in Telephony which
+ * intermediates between the propriety implementation and Telecomm/InCall.
+ */
+ @Override
+ public IImsVideoCallProvider getVideoCallProvider() throws RemoteException {
+ return null;
+ }
+
+ /**
+ * Determines if the current session is multiparty.
+ * @return {@code True} if the session is multiparty.
+ */
+ @Override
+ public boolean isMultiparty() throws RemoteException {
+ return false;
+ }
+}
diff --git a/telephony/java/android/telephony/ims/stub/ImsCallSessionListenerImplBase.java b/telephony/java/android/telephony/ims/stub/ImsCallSessionListenerImplBase.java
new file mode 100644
index 0000000..46f8f80
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/ImsCallSessionListenerImplBase.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2017 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.ims.stub;
+
+import com.android.ims.ImsCallProfile;
+import com.android.ims.ImsConferenceState;
+import com.android.ims.ImsReasonInfo;
+import com.android.ims.ImsStreamMediaProfile;
+import com.android.ims.ImsSuppServiceNotification;
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsCallSessionListener;
+
+/**
+ * Base implementation of ImsCallSessionListenerBase, which implements stub versions of the methods
+ * in the IImsCallSessionListener AIDL. Override the methods that your implementation of
+ * ImsCallSessionListener supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsCallSessionListener maintained by other ImsServices.
+ *
+ * @hide
+ */
+public class ImsCallSessionListenerImplBase extends IImsCallSessionListener.Stub {
+ /**
+ * Notifies the result of the basic session operation (setup / terminate).
+ */
+ @Override
+ public void callSessionProgressing(IImsCallSession session, ImsStreamMediaProfile profile) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionStarted(IImsCallSession session, ImsCallProfile profile) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionStartFailed(IImsCallSession session, ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionTerminated(IImsCallSession session, ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ /**
+ * Notifies the result of the call hold/resume operation.
+ */
+ @Override
+ public void callSessionHeld(IImsCallSession session, ImsCallProfile profile) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionHoldFailed(IImsCallSession session, ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionHoldReceived(IImsCallSession session, ImsCallProfile profile) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionResumed(IImsCallSession session, ImsCallProfile profile) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionResumeFailed(IImsCallSession session, ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionResumeReceived(IImsCallSession session, ImsCallProfile profile) {
+ // no-op
+ }
+
+ /**
+ * Notifies the result of call merge operation.
+ */
+ @Override
+ public void callSessionMergeStarted(IImsCallSession session, IImsCallSession newSession,
+ ImsCallProfile profile) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionMergeComplete(IImsCallSession session) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionMergeFailed(IImsCallSession session, ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ /**
+ * Notifies the result of call upgrade / downgrade or any other call
+ * updates.
+ */
+ @Override
+ public void callSessionUpdated(IImsCallSession session, ImsCallProfile profile) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionUpdateFailed(IImsCallSession session, ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionUpdateReceived(IImsCallSession session, ImsCallProfile profile) {
+ // no-op
+ }
+
+ /**
+ * Notifies the result of conference extension.
+ */
+ @Override
+ public void callSessionConferenceExtended(IImsCallSession session, IImsCallSession newSession,
+ ImsCallProfile profile) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionConferenceExtendFailed(IImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionConferenceExtendReceived(IImsCallSession session,
+ IImsCallSession newSession,
+ ImsCallProfile profile) {
+ // no-op
+ }
+
+ /**
+ * Notifies the result of the participant invitation / removal to/from the
+ * conference session.
+ */
+ @Override
+ public void callSessionInviteParticipantsRequestDelivered(IImsCallSession session) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionInviteParticipantsRequestFailed(IImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionRemoveParticipantsRequestDelivered(IImsCallSession session) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionRemoveParticipantsRequestFailed(IImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ /**
+ * Notifies the changes of the conference info. the conference session.
+ */
+ @Override
+ public void callSessionConferenceStateUpdated(IImsCallSession session,
+ ImsConferenceState state) {
+ // no-op
+ }
+
+ /**
+ * Notifies the incoming USSD message.
+ */
+ @Override
+ public void callSessionUssdMessageReceived(IImsCallSession session, int mode,
+ String ussdMessage) {
+ // no-op
+ }
+
+ /**
+ * Notifies of handover information for this call
+ */
+ @Override
+ public void callSessionHandover(IImsCallSession session, int srcAccessTech,
+ int targetAccessTech,
+ ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ @Override
+ public void callSessionHandoverFailed(IImsCallSession session, int srcAccessTech,
+ int targetAccessTech,
+ ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ /**
+ * Notifies the TTY mode change by remote party.
+ *
+ * @param mode one of the following: -
+ * {@link com.android.internal.telephony.Phone#TTY_MODE_OFF} -
+ * {@link com.android.internal.telephony.Phone#TTY_MODE_FULL} -
+ * {@link com.android.internal.telephony.Phone#TTY_MODE_HCO} -
+ * {@link com.android.internal.telephony.Phone#TTY_MODE_VCO}
+ */
+ @Override
+ public void callSessionTtyModeReceived(IImsCallSession session, int mode) {
+ // no-op
+ }
+
+ /**
+ * Notifies of a change to the multiparty state for this
+ * {@code ImsCallSession}.
+ *
+ * @param session The call session.
+ * @param isMultiParty {@code true} if the session became multiparty,
+ * {@code false} otherwise.
+ */
+ @Override
+ public void callSessionMultipartyStateChanged(IImsCallSession session, boolean isMultiParty) {
+ // no-op
+ }
+
+ /**
+ * Notifies the supplementary service information for the current session.
+ */
+ @Override
+ public void callSessionSuppServiceReceived(IImsCallSession session,
+ ImsSuppServiceNotification suppSrvNotification) {
+ // no-op
+ }
+}
+
diff --git a/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
new file mode 100644
index 0000000..5a4db99
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/ImsConfigImplBase.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2017 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.ims.stub;
+
+import android.os.RemoteException;
+
+import com.android.ims.ImsConfig;
+import com.android.ims.ImsConfigListener;
+import com.android.ims.internal.IImsConfig;
+
+/**
+ * Base implementation of ImsConfig, which implements stub versions of the methods
+ * in the IImsConfig AIDL. Override the methods that your implementation of ImsConfig supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsConfig maintained by other ImsServices.
+ *
+ * Provides APIs to get/set the IMS service feature/capability/parameters.
+ * The config items include:
+ * 1) Items provisioned by the operator.
+ * 2) Items configured by user. Mainly service feature class.
+ *
+ * @hide
+ */
+
+public class ImsConfigImplBase extends IImsConfig.Stub {
+
+ /**
+ * Gets the value for ims service/capabilities parameters from the provisioned
+ * value storage. Synchronous blocking call.
+ *
+ * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @return value in Integer format.
+ */
+ @Override
+ public int getProvisionedValue(int item) throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Gets the value for ims service/capabilities parameters from the provisioned
+ * value storage. Synchronous blocking call.
+ *
+ * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @return value in String format.
+ */
+ @Override
+ public String getProvisionedStringValue(int item) throws RemoteException {
+ return null;
+ }
+
+ /**
+ * Sets the value for IMS service/capabilities parameters by the operator device
+ * management entity. It sets the config item value in the provisioned storage
+ * from which the master value is derived. Synchronous blocking call.
+ *
+ * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @param value in Integer format.
+ * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
+ */
+ @Override
+ public int setProvisionedValue(int item, int value) throws RemoteException {
+ return ImsConfig.OperationStatusConstants.FAILED;
+ }
+
+ /**
+ * Sets the value for IMS service/capabilities parameters by the operator device
+ * management entity. It sets the config item value in the provisioned storage
+ * from which the master value is derived. Synchronous blocking call.
+ *
+ * @param item as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @param value in String format.
+ * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants.
+ */
+ @Override
+ public int setProvisionedStringValue(int item, String value) throws RemoteException {
+ return ImsConfig.OperationStatusConstants.FAILED;
+ }
+
+ /**
+ * Gets the value of the specified IMS feature item for specified network type.
+ * This operation gets the feature config value from the master storage (i.e. final
+ * value). Asynchronous non-blocking call.
+ *
+ * @param feature as defined in com.android.ims.ImsConfig#FeatureConstants.
+ * @param network as defined in android.telephony.TelephonyManager#NETWORK_TYPE_XXX.
+ * @param listener feature value returned asynchronously through listener.
+ */
+ @Override
+ public void getFeatureValue(int feature, int network, ImsConfigListener listener)
+ throws RemoteException {
+ }
+
+ /**
+ * Sets the value for IMS feature item for specified network type.
+ * This operation stores the user setting in setting db from which master db
+ * is derived.
+ *
+ * @param feature as defined in com.android.ims.ImsConfig#FeatureConstants.
+ * @param network as defined in android.telephony.TelephonyManager#NETWORK_TYPE_XXX.
+ * @param value as defined in com.android.ims.ImsConfig#FeatureValueConstants.
+ * @param listener, provided if caller needs to be notified for set result.
+ */
+ @Override
+ public void setFeatureValue(int feature, int network, int value, ImsConfigListener listener)
+ throws RemoteException {
+ }
+
+ /**
+ * Gets the value for IMS VoLTE provisioned.
+ * This should be the same as the operator provisioned value if applies.
+ */
+ @Override
+ public boolean getVolteProvisioned() throws RemoteException {
+ return false;
+ }
+
+ /**
+ * Gets the value for IMS feature item video quality.
+ *
+ * @param listener Video quality value returned asynchronously through listener.
+ */
+ @Override
+ public void getVideoQuality(ImsConfigListener listener) throws RemoteException {
+ }
+
+ /**
+ * 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.
+ */
+ @Override
+ public void setVideoQuality(int quality, ImsConfigListener listener) throws RemoteException {
+ }
+}
diff --git a/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
new file mode 100644
index 0000000..89f95ff
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/ImsEcbmImplBase.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 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.ims.stub;
+
+import android.os.RemoteException;
+
+import com.android.ims.internal.IImsEcbm;
+import com.android.ims.internal.IImsEcbmListener;
+
+/**
+ * Base implementation of ImsEcbm, which implements stub versions of the methods
+ * in the IImsEcbm AIDL. Override the methods that your implementation of ImsEcbm supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsEcbm maintained by other ImsServices.
+ *
+ * @hide
+ */
+
+public class ImsEcbmImplBase extends IImsEcbm.Stub {
+
+ /**
+ * Sets the listener.
+ */
+ @Override
+ public void setListener(IImsEcbmListener listener) throws RemoteException {
+
+ }
+
+ /**
+ * Requests Modem to come out of ECBM mode
+ */
+ @Override
+ public void exitEmergencyCallbackMode() throws RemoteException {
+
+ }
+}
diff --git a/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
new file mode 100644
index 0000000..05da9da
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/ImsMultiEndpointImplBase.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2017 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.ims.stub;
+
+import android.os.RemoteException;
+
+import com.android.ims.internal.IImsExternalCallStateListener;
+import com.android.ims.internal.IImsMultiEndpoint;
+
+/**
+ * Base implementation of ImsMultiEndpoint, which implements stub versions of the methods
+ * in the IImsMultiEndpoint AIDL. Override the methods that your implementation of
+ * ImsMultiEndpoint supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsMultiEndpoint maintained by other ImsServices.
+ *
+ * @hide
+ */
+
+public class ImsMultiEndpointImplBase extends IImsMultiEndpoint.Stub {
+
+ /**
+ * Sets the listener.
+ */
+ @Override
+ public void setListener(IImsExternalCallStateListener listener) throws RemoteException {
+
+ }
+
+ /**
+ * Query API to get the latest Dialog Event Package information
+ * Should be invoked only after setListener is done
+ */
+ @Override
+ public void requestImsExternalCallStateInfo() throws RemoteException {
+
+ }
+}
diff --git a/telephony/java/android/telephony/ims/stub/ImsStreamMediaSessionImplBase.java b/telephony/java/android/telephony/ims/stub/ImsStreamMediaSessionImplBase.java
new file mode 100644
index 0000000..92f1a01
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/ImsStreamMediaSessionImplBase.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2017 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.ims.stub;
+
+import android.os.RemoteException;
+
+import com.android.ims.internal.IImsStreamMediaSession;
+
+/**
+ * Base implementation of ImsStreamMediaSession, which implements stub versions of the methods
+ * in the IImsStreamMediaSession AIDL. Override the methods that your implementation of
+ * ImsStreamMediaSession supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsStreamMediaSession maintained by other ImsServices.
+ *
+ * @hide
+ */
+
+public class ImsStreamMediaSessionImplBase extends IImsStreamMediaSession.Stub {
+
+ @Override
+ public void close() throws RemoteException {
+
+ }
+}
diff --git a/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
new file mode 100644
index 0000000..dc74094
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/ImsUtImplBase.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2017 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.ims.stub;
+
+import android.os.Bundle;
+import android.os.RemoteException;
+
+import com.android.ims.internal.IImsUt;
+import com.android.ims.internal.IImsUtListener;
+
+/**
+ * Base implementation of ImsUt, which implements stub versions of the methods
+ * in the IImsUt AIDL. Override the methods that your implementation of ImsUt supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsUt maintained by other ImsServices.
+ *
+ * Provides the Ut interface interworking to get/set the supplementary service configuration.
+ *
+ * @hide
+ */
+
+public class ImsUtImplBase extends IImsUt.Stub {
+
+ /**
+ * Closes the object. This object is not usable after being closed.
+ */
+ @Override
+ public void close() throws RemoteException {
+
+ }
+
+ /**
+ * Retrieves the configuration of the call barring.
+ */
+ @Override
+ public int queryCallBarring(int cbType) throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Retrieves the configuration of the call forward.
+ */
+ @Override
+ public int queryCallForward(int condition, String number) throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Retrieves the configuration of the call waiting.
+ */
+ @Override
+ public int queryCallWaiting() throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Retrieves the default CLIR setting.
+ */
+ @Override
+ public int queryCLIR() throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Retrieves the CLIP call setting.
+ */
+ @Override
+ public int queryCLIP() throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Retrieves the COLR call setting.
+ */
+ @Override
+ public int queryCOLR() throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Retrieves the COLP call setting.
+ */
+ @Override
+ public int queryCOLP() throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Updates or retrieves the supplementary service configuration.
+ */
+ @Override
+ public int transact(Bundle ssInfo) throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Updates the configuration of the call barring.
+ */
+ @Override
+ public int updateCallBarring(int cbType, int action, String[] barrList) throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Updates the configuration of the call forward.
+ */
+ @Override
+ public int updateCallForward(int action, int condition, String number, int serviceClass,
+ int timeSeconds) throws RemoteException {
+ return 0;
+ }
+
+ /**
+ * Updates the configuration of the call waiting.
+ */
+ @Override
+ public int updateCallWaiting(boolean enable, int serviceClass) throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Updates the configuration of the CLIR supplementary service.
+ */
+ @Override
+ public int updateCLIR(int clirMode) throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Updates the configuration of the CLIP supplementary service.
+ */
+ @Override
+ public int updateCLIP(boolean enable) throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Updates the configuration of the COLR supplementary service.
+ */
+ @Override
+ public int updateCOLR(int presentation) throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Updates the configuration of the COLP supplementary service.
+ */
+ @Override
+ public int updateCOLP(boolean enable) throws RemoteException {
+ return -1;
+ }
+
+ /**
+ * Sets the listener.
+ */
+ @Override
+ public void setListener(IImsUtListener listener) throws RemoteException {
+ }
+}
diff --git a/telephony/java/android/telephony/ims/stub/ImsUtListenerImplBase.java b/telephony/java/android/telephony/ims/stub/ImsUtListenerImplBase.java
new file mode 100644
index 0000000..b371efb
--- /dev/null
+++ b/telephony/java/android/telephony/ims/stub/ImsUtListenerImplBase.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2017 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.ims.stub;
+
+import android.os.Bundle;
+import android.os.RemoteException;
+
+import com.android.ims.ImsCallForwardInfo;
+import com.android.ims.ImsReasonInfo;
+import com.android.ims.ImsSsInfo;
+import com.android.ims.internal.IImsUt;
+import com.android.ims.internal.IImsUtListener;
+
+/**
+ * Base implementation of ImsUtListener, which implements stub versions of the methods
+ * in the IImsUtListener AIDL. Override the methods that your implementation of
+ * ImsUtListener supports.
+ *
+ * DO NOT remove or change the existing APIs, only add new ones to this Base implementation or you
+ * will break other implementations of ImsUtListener maintained by other ImsServices.
+ *
+ * @hide
+ */
+
+public class ImsUtListenerImplBase extends IImsUtListener.Stub {
+
+ /**
+ * Notifies the result of the supplementary service configuration udpate.
+ */
+ @Override
+ public void utConfigurationUpdated(IImsUt ut, int id) throws RemoteException {
+ }
+
+ @Override
+ public void utConfigurationUpdateFailed(IImsUt ut, int id, ImsReasonInfo error)
+ throws RemoteException {
+ }
+
+ /**
+ * Notifies the result of the supplementary service configuration query.
+ */
+ @Override
+ public void utConfigurationQueried(IImsUt ut, int id, Bundle ssInfo) throws RemoteException {
+ }
+
+ @Override
+ public void utConfigurationQueryFailed(IImsUt ut, int id, ImsReasonInfo error)
+ throws RemoteException {
+ }
+
+ /**
+ * Notifies the status of the call barring supplementary service.
+ */
+ @Override
+ public void utConfigurationCallBarringQueried(IImsUt ut, int id, ImsSsInfo[] cbInfo)
+ throws RemoteException {
+ }
+
+ /**
+ * Notifies the status of the call forwarding supplementary service.
+ */
+ @Override
+ public void utConfigurationCallForwardQueried(IImsUt ut, int id, ImsCallForwardInfo[] cfInfo)
+ throws RemoteException {
+ }
+
+ /**
+ * Notifies the status of the call waiting supplementary service.
+ */
+ @Override
+ public void utConfigurationCallWaitingQueried(IImsUt ut, int id, ImsSsInfo[] cwInfo)
+ throws RemoteException {
+ }
+}
diff --git a/telephony/java/com/android/ims/ImsConfig.java b/telephony/java/com/android/ims/ImsConfig.java
new file mode 100644
index 0000000..cd076b1
--- /dev/null
+++ b/telephony/java/com/android/ims/ImsConfig.java
@@ -0,0 +1,694 @@
+/*
+ * Copyright (C) 2017 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.ims;
+
+import android.content.Context;
+import android.os.RemoteException;
+import android.telephony.Rlog;
+
+import com.android.ims.internal.IImsConfig;
+
+/**
+ * Provides APIs to get/set the IMS service feature/capability/parameters.
+ * The config items include:
+ * 1) Items provisioned by the operator.
+ * 2) Items configured by user. Mainly service feature class.
+ *
+ * @hide
+ */
+public class ImsConfig {
+ private static final String TAG = "ImsConfig";
+ private boolean DBG = true;
+ private final IImsConfig miConfig;
+ private Context mContext;
+
+ /**
+ * Broadcast action: the feature enable status was changed
+ *
+ * @hide
+ */
+ public static final String ACTION_IMS_FEATURE_CHANGED =
+ "com.android.intent.action.IMS_FEATURE_CHANGED";
+
+ /**
+ * Broadcast action: the configuration was changed
+ *
+ * @hide
+ */
+ public static final String ACTION_IMS_CONFIG_CHANGED =
+ "com.android.intent.action.IMS_CONFIG_CHANGED";
+
+ /**
+ * Extra parameter "item" of intent ACTION_IMS_FEATURE_CHANGED and ACTION_IMS_CONFIG_CHANGED.
+ * It is the value of FeatureConstants or ConfigConstants.
+ *
+ * @hide
+ */
+ public static final String EXTRA_CHANGED_ITEM = "item";
+
+ /**
+ * Extra parameter "value" of intent ACTION_IMS_FEATURE_CHANGED and ACTION_IMS_CONFIG_CHANGED.
+ * It is the new value of "item".
+ *
+ * @hide
+ */
+ public static final String EXTRA_NEW_VALUE = "value";
+
+ /**
+ * Defines IMS service/capability feature constants.
+ */
+ public static class FeatureConstants {
+ public static final int FEATURE_TYPE_UNKNOWN = -1;
+
+ /**
+ * FEATURE_TYPE_VOLTE supports features defined in 3GPP and
+ * GSMA IR.92 over LTE.
+ */
+ public static final int FEATURE_TYPE_VOICE_OVER_LTE = 0;
+
+ /**
+ * FEATURE_TYPE_LVC supports features defined in 3GPP and
+ * GSMA IR.94 over LTE.
+ */
+ public static final int FEATURE_TYPE_VIDEO_OVER_LTE = 1;
+
+ /**
+ * FEATURE_TYPE_VOICE_OVER_WIFI supports features defined in 3GPP and
+ * GSMA IR.92 over WiFi.
+ */
+ public static final int FEATURE_TYPE_VOICE_OVER_WIFI = 2;
+
+ /**
+ * FEATURE_TYPE_VIDEO_OVER_WIFI supports features defined in 3GPP and
+ * GSMA IR.94 over WiFi.
+ */
+ public static final int FEATURE_TYPE_VIDEO_OVER_WIFI = 3;
+
+ /**
+ * FEATURE_TYPE_UT supports features defined in 3GPP and
+ * GSMA IR.92 over LTE.
+ */
+ public static final int FEATURE_TYPE_UT_OVER_LTE = 4;
+
+ /**
+ * FEATURE_TYPE_UT_OVER_WIFI supports features defined in 3GPP and
+ * GSMA IR.92 over WiFi.
+ */
+ public static final int FEATURE_TYPE_UT_OVER_WIFI = 5;
+ }
+
+ /**
+ * Defines IMS service/capability parameters.
+ */
+ public static class ConfigConstants {
+
+ // Define IMS config items
+ public static final int CONFIG_START = 0;
+
+ // Define operator provisioned config items
+ public static final int PROVISIONED_CONFIG_START = CONFIG_START;
+
+ /**
+ * AMR CODEC Mode Value set, 0-7 in comma separated sequence.
+ * Value is in String format.
+ */
+ public static final int VOCODER_AMRMODESET = CONFIG_START;
+
+ /**
+ * Wide Band AMR CODEC Mode Value set,0-7 in comma separated sequence.
+ * Value is in String format.
+ */
+ public static final int VOCODER_AMRWBMODESET = 1;
+
+ /**
+ * SIP Session Timer value (seconds).
+ * Value is in Integer format.
+ */
+ public static final int SIP_SESSION_TIMER = 2;
+
+ /**
+ * Minimum SIP Session Expiration Timer in (seconds).
+ * Value is in Integer format.
+ */
+ public static final int MIN_SE = 3;
+
+ /**
+ * SIP_INVITE cancellation time out value (in milliseconds). Integer format.
+ * Value is in Integer format.
+ */
+ public static final int CANCELLATION_TIMER = 4;
+
+ /**
+ * Delay time when an iRAT transition from eHRPD/HRPD/1xRTT to LTE.
+ * Value is in Integer format.
+ */
+ public static final int TDELAY = 5;
+
+ /**
+ * Silent redial status of Enabled (True), or Disabled (False).
+ * Value is in Integer format.
+ */
+ public static final int SILENT_REDIAL_ENABLE = 6;
+
+ /**
+ * SIP T1 timer value in milliseconds. See RFC 3261 for define.
+ * Value is in Integer format.
+ */
+ public static final int SIP_T1_TIMER = 7;
+
+ /**
+ * SIP T2 timer value in milliseconds. See RFC 3261 for define.
+ * Value is in Integer format.
+ */
+ public static final int SIP_T2_TIMER = 8;
+
+ /**
+ * SIP TF timer value in milliseconds. See RFC 3261 for define.
+ * Value is in Integer format.
+ */
+ public static final int SIP_TF_TIMER = 9;
+
+ /**
+ * VoLTE status for VLT/s status of Enabled (1), or Disabled (0).
+ * Value is in Integer format.
+ */
+ public static final int VLT_SETTING_ENABLED = 10;
+
+ /**
+ * VoLTE status for LVC/s status of Enabled (1), or Disabled (0).
+ * Value is in Integer format.
+ */
+ public static final int LVC_SETTING_ENABLED = 11;
+ /**
+ * Domain Name for the device to populate the request URI for REGISTRATION.
+ * Value is in String format.
+ */
+ public static final int DOMAIN_NAME = 12;
+ /**
+ * Device Outgoing SMS based on either 3GPP or 3GPP2 standards.
+ * Value is in Integer format. 3GPP2(0), 3GPP(1)
+ */
+ public static final int SMS_FORMAT = 13;
+ /**
+ * Turns IMS ON/OFF on the device.
+ * Value is in Integer format. ON (1), OFF(0).
+ */
+ public static final int SMS_OVER_IP = 14;
+ /**
+ * Requested expiration for Published Online availability.
+ * Value is in Integer format.
+ */
+ public static final int PUBLISH_TIMER = 15;
+ /**
+ * Requested expiration for Published Offline availability.
+ * Value is in Integer format.
+ */
+ public static final int PUBLISH_TIMER_EXTENDED = 16;
+ /**
+ *
+ * Value is in Integer format.
+ */
+ public static final int CAPABILITY_DISCOVERY_ENABLED = 17;
+ /**
+ * Period of time the capability information of the contact is cached on handset.
+ * Value is in Integer format.
+ */
+ public static final int CAPABILITIES_CACHE_EXPIRATION = 18;
+ /**
+ * Peiod of time the availability information of a contact is cached on device.
+ * Value is in Integer format.
+ */
+ public static final int AVAILABILITY_CACHE_EXPIRATION = 19;
+ /**
+ * Interval between successive capabilities polling.
+ * Value is in Integer format.
+ */
+ public static final int CAPABILITIES_POLL_INTERVAL = 20;
+ /**
+ * Minimum time between two published messages from the device.
+ * Value is in Integer format.
+ */
+ public static final int SOURCE_THROTTLE_PUBLISH = 21;
+ /**
+ * The Maximum number of MDNs contained in one Request Contained List.
+ * Value is in Integer format.
+ */
+ public static final int MAX_NUMENTRIES_IN_RCL = 22;
+ /**
+ * Expiration timer for subscription of a Request Contained List, used in capability
+ * polling.
+ * Value is in Integer format.
+ */
+ public static final int CAPAB_POLL_LIST_SUB_EXP = 23;
+ /**
+ * Applies compression to LIST Subscription.
+ * Value is in Integer format. Enable (1), Disable(0).
+ */
+ public static final int GZIP_FLAG = 24;
+ /**
+ * VOLTE Status for EAB/s status of Enabled (1), or Disabled (0).
+ * Value is in Integer format.
+ */
+ public static final int EAB_SETTING_ENABLED = 25;
+ /**
+ * Wi-Fi calling roaming status.
+ * Value is in Integer format. ON (1), OFF(0).
+ */
+ public static final int VOICE_OVER_WIFI_ROAMING = 26;
+ /**
+ * Wi-Fi calling modem - WfcModeFeatureValueConstants.
+ * Value is in Integer format.
+ */
+ public static final int VOICE_OVER_WIFI_MODE = 27;
+ /**
+ * VOLTE Status for voice over wifi status of Enabled (1), or Disabled (0).
+ * Value is in Integer format.
+ */
+ public static final int VOICE_OVER_WIFI_SETTING_ENABLED = 28;
+ /**
+ * Mobile data enabled.
+ * Value is in Integer format. On (1), OFF(0).
+ */
+ public static final int MOBILE_DATA_ENABLED = 29;
+ /**
+ * VoLTE user opted in status.
+ * Value is in Integer format. Opted-in (1) Opted-out (0).
+ */
+ public static final int VOLTE_USER_OPT_IN_STATUS = 30;
+ /**
+ * Proxy for Call Session Control Function(P-CSCF) address for Local-BreakOut(LBO).
+ * Value is in String format.
+ */
+ public static final int LBO_PCSCF_ADDRESS = 31;
+ /**
+ * Keep Alive Enabled for SIP.
+ * Value is in Integer format. On(1), OFF(0).
+ */
+ public static final int KEEP_ALIVE_ENABLED = 32;
+ /**
+ * Registration retry Base Time value in seconds.
+ * Value is in Integer format.
+ */
+ public static final int REGISTRATION_RETRY_BASE_TIME_SEC = 33;
+ /**
+ * Registration retry Max Time value in seconds.
+ * Value is in Integer format.
+ */
+ public static final int REGISTRATION_RETRY_MAX_TIME_SEC = 34;
+ /**
+ * Smallest RTP port for speech codec.
+ * Value is in integer format.
+ */
+ public static final int SPEECH_START_PORT = 35;
+ /**
+ * Largest RTP port for speech code.
+ * Value is in Integer format.
+ */
+ public static final int SPEECH_END_PORT = 36;
+ /**
+ * SIP Timer A's value in milliseconds. Timer A is the INVITE request
+ * retransmit interval, for UDP only.
+ * Value is in Integer format.
+ */
+ public static final int SIP_INVITE_REQ_RETX_INTERVAL_MSEC = 37;
+ /**
+ * SIP Timer B's value in milliseconds. Timer B is the wait time for
+ * INVITE message to be acknowledged.
+ * Value is in Integer format.
+ */
+ public static final int SIP_INVITE_RSP_WAIT_TIME_MSEC = 38;
+ /**
+ * SIP Timer D's value in milliseconds. Timer D is the wait time for
+ * response retransmits of the invite client transactions.
+ * Value is in Integer format.
+ */
+ public static final int SIP_INVITE_RSP_RETX_WAIT_TIME_MSEC = 39;
+ /**
+ * SIP Timer E's value in milliseconds. Timer E is the value Non-INVITE
+ * request retransmit interval, for UDP only.
+ * Value is in Integer format.
+ */
+ public static final int SIP_NON_INVITE_REQ_RETX_INTERVAL_MSEC = 40;
+ /**
+ * SIP Timer F's value in milliseconds. Timer F is the Non-INVITE transaction
+ * timeout timer.
+ * Value is in Integer format.
+ */
+ public static final int SIP_NON_INVITE_TXN_TIMEOUT_TIMER_MSEC = 41;
+ /**
+ * SIP Timer G's value in milliseconds. Timer G is the value of INVITE response
+ * retransmit interval.
+ * Value is in Integer format.
+ */
+ public static final int SIP_INVITE_RSP_RETX_INTERVAL_MSEC = 42;
+ /**
+ * SIP Timer H's value in milliseconds. Timer H is the value of wait time for
+ * ACK receipt.
+ * Value is in Integer format.
+ */
+ public static final int SIP_ACK_RECEIPT_WAIT_TIME_MSEC = 43;
+ /**
+ * SIP Timer I's value in milliseconds. Timer I is the value of wait time for
+ * ACK retransmits.
+ * Value is in Integer format.
+ */
+ public static final int SIP_ACK_RETX_WAIT_TIME_MSEC = 44;
+ /**
+ * SIP Timer J's value in milliseconds. Timer J is the value of wait time for
+ * non-invite request retransmission.
+ * Value is in Integer format.
+ */
+ public static final int SIP_NON_INVITE_REQ_RETX_WAIT_TIME_MSEC = 45;
+ /**
+ * SIP Timer K's value in milliseconds. Timer K is the value of wait time for
+ * non-invite response retransmits.
+ * Value is in Integer format.
+ */
+ public static final int SIP_NON_INVITE_RSP_RETX_WAIT_TIME_MSEC = 46;
+ /**
+ * AMR WB octet aligned dynamic payload type.
+ * Value is in Integer format.
+ */
+ public static final int AMR_WB_OCTET_ALIGNED_PT = 47;
+ /**
+ * AMR WB bandwidth efficient payload type.
+ * Value is in Integer format.
+ */
+ public static final int AMR_WB_BANDWIDTH_EFFICIENT_PT = 48;
+ /**
+ * AMR octet aligned dynamic payload type.
+ * Value is in Integer format.
+ */
+ public static final int AMR_OCTET_ALIGNED_PT = 49;
+ /**
+ * AMR bandwidth efficient payload type.
+ * Value is in Integer format.
+ */
+ public static final int AMR_BANDWIDTH_EFFICIENT_PT = 50;
+ /**
+ * DTMF WB payload type.
+ * Value is in Integer format.
+ */
+ public static final int DTMF_WB_PT = 51;
+ /**
+ * DTMF NB payload type.
+ * Value is in Integer format.
+ */
+ public static final int DTMF_NB_PT = 52;
+ /**
+ * AMR Default encoding mode.
+ * Value is in Integer format.
+ */
+ public static final int AMR_DEFAULT_MODE = 53;
+ /**
+ * SMS Public Service Identity.
+ * Value is in String format.
+ */
+ public static final int SMS_PSI = 54;
+ /**
+ * Video Quality - VideoQualityFeatureValuesConstants.
+ * Value is in Integer format.
+ */
+ public static final int VIDEO_QUALITY = 55;
+ /**
+ * LTE threshold.
+ * Handover from LTE to WiFi if LTE < THLTE1 and WiFi >= VOWT_A.
+ */
+ public static final int TH_LTE1 = 56;
+ /**
+ * LTE threshold.
+ * Handover from WiFi to LTE if LTE >= THLTE3 or (WiFi < VOWT_B and LTE >= THLTE2).
+ */
+ public static final int TH_LTE2 = 57;
+ /**
+ * LTE threshold.
+ * Handover from WiFi to LTE if LTE >= THLTE3 or (WiFi < VOWT_B and LTE >= THLTE2).
+ */
+ public static final int TH_LTE3 = 58;
+ /**
+ * 1x threshold.
+ * Handover from 1x to WiFi if 1x < TH1x
+ */
+ public static final int TH_1x = 59;
+ /**
+ * WiFi threshold.
+ * Handover from LTE to WiFi if LTE < THLTE1 and WiFi >= VOWT_A.
+ */
+ public static final int VOWT_A = 60;
+ /**
+ * WiFi threshold.
+ * Handover from WiFi to LTE if LTE >= THLTE3 or (WiFi < VOWT_B and LTE >= THLTE2).
+ */
+ public static final int VOWT_B = 61;
+ /**
+ * LTE ePDG timer.
+ * Device shall not handover back to LTE until the T_ePDG_LTE timer expires.
+ */
+ public static final int T_EPDG_LTE = 62;
+ /**
+ * WiFi ePDG timer.
+ * Device shall not handover back to WiFi until the T_ePDG_WiFi timer expires.
+ */
+ public static final int T_EPDG_WIFI = 63;
+ /**
+ * 1x ePDG timer.
+ * Device shall not re-register on 1x until the T_ePDG_1x timer expires.
+ */
+ public static final int T_EPDG_1X = 64;
+ /**
+ * MultiEndpoint status: Enabled (1), or Disabled (0).
+ * Value is in Integer format.
+ */
+ public static final int VICE_SETTING_ENABLED = 65;
+
+ // Expand the operator config items as needed here, need to change
+ // PROVISIONED_CONFIG_END after that.
+ public static final int PROVISIONED_CONFIG_END = VICE_SETTING_ENABLED;
+
+ // Expand the operator config items as needed here.
+ }
+
+ /**
+ * Defines IMS set operation status.
+ */
+ public static class OperationStatusConstants {
+ public static final int UNKNOWN = -1;
+ public static final int SUCCESS = 0;
+ public static final int FAILED = 1;
+ public static final int UNSUPPORTED_CAUSE_NONE = 2;
+ public static final int UNSUPPORTED_CAUSE_RAT = 3;
+ public static final int UNSUPPORTED_CAUSE_DISABLED = 4;
+ }
+
+ /**
+ * Defines IMS get operation values.
+ */
+ public static class OperationValuesConstants {
+ /**
+ * Values related to Video Quality
+ */
+ public static final int VIDEO_QUALITY_UNKNOWN = -1;
+ public static final int VIDEO_QUALITY_LOW = 0;
+ public static final int VIDEO_QUALITY_HIGH = 1;
+ }
+
+ /**
+ * Defines IMS video quality feature value.
+ */
+ public static class VideoQualityFeatureValuesConstants {
+ public static final int LOW = 0;
+ public static final int HIGH = 1;
+ }
+
+ /**
+ * Defines IMS feature value.
+ */
+ public static class FeatureValueConstants {
+ public static final int OFF = 0;
+ public static final int ON = 1;
+ }
+
+ /**
+ * Defines IMS feature value.
+ */
+ public static class WfcModeFeatureValueConstants {
+ public static final int WIFI_ONLY = 0;
+ public static final int CELLULAR_PREFERRED = 1;
+ public static final int WIFI_PREFERRED = 2;
+ }
+
+ public ImsConfig(IImsConfig iconfig, Context context) {
+ if (DBG) Rlog.d(TAG, "ImsConfig creates");
+ miConfig = iconfig;
+ mContext = context;
+ }
+
+ /**
+ * Gets the provisioned value for IMS service/capabilities parameters used by IMS stack.
+ * This function should not be called from the mainthread as it could block the
+ * mainthread.
+ *
+ * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @return the value in Integer format.
+ *
+ * @throws ImsException if calling the IMS service results in an error.
+ */
+ public int getProvisionedValue(int item) throws ImsException {
+ int ret = 0;
+ try {
+ ret = miConfig.getProvisionedValue(item);
+ } catch (RemoteException e) {
+ throw new ImsException("getValue()", e,
+ ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
+ }
+ if (DBG) Rlog.d(TAG, "getProvisionedValue(): item = " + item + ", ret =" + ret);
+
+ return ret;
+ }
+
+ /**
+ * Gets the provisioned value for IMS service/capabilities parameters used by IMS stack.
+ * This function should not be called from the mainthread as it could block the
+ * mainthread.
+ *
+ * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @return value in String format.
+ *
+ * @throws ImsException if calling the IMS service results in an error.
+ */
+ public String getProvisionedStringValue(int item) throws ImsException {
+ String ret = "Unknown";
+ try {
+ ret = miConfig.getProvisionedStringValue(item);
+ } catch (RemoteException e) {
+ throw new ImsException("getProvisionedStringValue()", e,
+ ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
+ }
+ if (DBG) Rlog.d(TAG, "getProvisionedStringValue(): item = " + item + ", ret =" + ret);
+
+ return ret;
+ }
+
+ /**
+ * Sets the value for IMS service/capabilities parameters by
+ * the operator device management entity.
+ * This function should not be called from main thread as it could block
+ * mainthread.
+ *
+ * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @param value in Integer format.
+ * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants
+ *
+ * @throws ImsException if calling the IMS service results in an error.
+ */
+ public int setProvisionedValue(int item, int value)
+ throws ImsException {
+ int ret = OperationStatusConstants.UNKNOWN;
+ if (DBG) {
+ Rlog.d(TAG, "setProvisionedValue(): item = " + item +
+ "value = " + value);
+ }
+ try {
+ ret = miConfig.setProvisionedValue(item, value);
+ } catch (RemoteException e) {
+ throw new ImsException("setProvisionedValue()", e,
+ ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
+ }
+ if (DBG) {
+ Rlog.d(TAG, "setProvisionedValue(): item = " + item +
+ " value = " + value + " ret = " + ret);
+ }
+ return ret;
+ }
+
+ /**
+ * Sets the value for IMS service/capabilities parameters by
+ * the operator device management entity.
+ * This function should not be called from main thread as it could block
+ * mainthread.
+ *
+ * @param item, as defined in com.android.ims.ImsConfig#ConfigConstants.
+ * @param value in String format.
+ * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants
+ *
+ * @throws ImsException if calling the IMS service results in an error.
+ */
+ public int setProvisionedStringValue(int item, String value)
+ throws ImsException {
+ int ret = OperationStatusConstants.UNKNOWN;
+ try {
+ ret = miConfig.setProvisionedStringValue(item, value);
+ } catch (RemoteException e) {
+ throw new ImsException("setProvisionedStringValue()", e,
+ ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
+ }
+ if (DBG) {
+ Rlog.d(TAG, "setProvisionedStringValue(): item = " + item +
+ ", value =" + value);
+ }
+ return ret;
+ }
+
+ /**
+ * Gets the value for IMS feature item for specified network type.
+ *
+ * @param feature, defined as in FeatureConstants.
+ * @param network, defined as in android.telephony.TelephonyManager#NETWORK_TYPE_XXX.
+ * @param listener, provided to be notified for the feature on/off status.
+ * @return void
+ *
+ * @throws ImsException if calling the IMS service results in an error.
+ */
+ public void getFeatureValue(int feature, int network,
+ ImsConfigListener listener) throws ImsException {
+ if (DBG) {
+ Rlog.d(TAG, "getFeatureValue: feature = " + feature + ", network =" + network +
+ ", listener =" + listener);
+ }
+ try {
+ miConfig.getFeatureValue(feature, network, listener);
+ } catch (RemoteException e) {
+ throw new ImsException("getFeatureValue()", e,
+ ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
+ }
+ }
+
+ /**
+ * Sets the value for IMS feature item for specified network type.
+ *
+ * @param feature, as defined in FeatureConstants.
+ * @param network, as defined in android.telephony.TelephonyManager#NETWORK_TYPE_XXX.
+ * @param value, as defined in FeatureValueConstants.
+ * @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.
+ */
+ public void setFeatureValue(int feature, int network, int value,
+ ImsConfigListener listener) throws ImsException {
+ if (DBG) {
+ Rlog.d(TAG, "setFeatureValue: feature = " + feature + ", network =" + network +
+ ", value =" + value + ", listener =" + listener);
+ }
+ try {
+ miConfig.setFeatureValue(feature, network, value, listener);
+ } catch (RemoteException e) {
+ throw new ImsException("setFeatureValue()", e,
+ ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE);
+ }
+ }
+}
diff --git a/telephony/java/com/android/ims/ImsException.java b/telephony/java/com/android/ims/ImsException.java
new file mode 100644
index 0000000..74b20f4
--- /dev/null
+++ b/telephony/java/com/android/ims/ImsException.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims;
+
+/**
+ * This class defines a general IMS-related exception.
+ *
+ * @hide
+ */
+public class ImsException extends Exception {
+
+ /**
+ * Refer to CODE_LOCAL_* in {@link ImsReasonInfo}
+ */
+ private int mCode;
+
+ public ImsException() {
+ }
+
+ public ImsException(String message, int code) {
+ super(message + ", code = " + code);
+ mCode = code;
+ }
+
+ public ImsException(String message, Throwable cause, int code) {
+ super(message, cause);
+ mCode = code;
+ }
+
+ /**
+ * Gets the detailed exception code when ImsException is throwed
+ *
+ * @return the exception code in {@link ImsReasonInfo}
+ */
+ public int getCode() {
+ return mCode;
+ }
+}
diff --git a/telephony/java/com/android/ims/ImsReasonInfo.java b/telephony/java/com/android/ims/ImsReasonInfo.java
index c71808c..e4f380f 100644
--- a/telephony/java/com/android/ims/ImsReasonInfo.java
+++ b/telephony/java/com/android/ims/ImsReasonInfo.java
@@ -313,6 +313,12 @@
public static final int CODE_WIFI_LOST = 1407;
/**
+ * Indicates the registration attempt on IWLAN failed due to IKEv2 authetication failure
+ * during tunnel establishment.
+ */
+ public static final int CODE_IKEV2_AUTH_FAILURE = 1408;
+
+ /**
* Network string error messages.
* mExtraMessage may have these values.
*/
diff --git a/telephony/java/com/android/ims/ImsUtInterface.java b/telephony/java/com/android/ims/ImsUtInterface.java
new file mode 100644
index 0000000..5984e78
--- /dev/null
+++ b/telephony/java/com/android/ims/ImsUtInterface.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims;
+
+import android.os.Message;
+
+/**
+ * Provides APIs for the supplementary service settings using IMS (Ut interface).
+ * It is created from 3GPP TS 24.623 (XCAP(XML Configuration Access Protocol)
+ * over the Ut interface for manipulating supplementary services).
+ *
+ * @hide
+ */
+public interface ImsUtInterface {
+ /**
+ * Actions
+ * @hide
+ */
+ public static final int ACTION_DEACTIVATION = 0;
+ public static final int ACTION_ACTIVATION = 1;
+ public static final int ACTION_REGISTRATION = 3;
+ public static final int ACTION_ERASURE = 4;
+ public static final int ACTION_INTERROGATION = 5;
+
+ /**
+ * OIR (Originating Identification Restriction, 3GPP TS 24.607)
+ * OIP (Originating Identification Presentation, 3GPP TS 24.607)
+ * TIR (Terminating Identification Restriction, 3GPP TS 24.608)
+ * TIP (Terminating Identification Presentation, 3GPP TS 24.608)
+ */
+ public static final int OIR_DEFAULT = 0; // "user subscription default value"
+ public static final int OIR_PRESENTATION_RESTRICTED = 1;
+ public static final int OIR_PRESENTATION_NOT_RESTRICTED = 2;
+
+ /**
+ * CW (Communication Waiting, 3GPP TS 24.615)
+ */
+
+ /**
+ * CDIV (Communication Diversion, 3GPP TS 24.604)
+ * actions: target, no reply timer
+ */
+ public static final int CDIV_CF_UNCONDITIONAL = 0;
+ public static final int CDIV_CF_BUSY = 1;
+ public static final int CDIV_CF_NO_REPLY = 2;
+ public static final int CDIV_CF_NOT_REACHABLE = 3;
+ // For CS service code: 002
+ public static final int CDIV_CF_ALL = 4;
+ // For CS service code: 004
+ public static final int CDIV_CF_ALL_CONDITIONAL = 5;
+ // It's only supported in the IMS service (CS does not define it).
+ // IR.92 recommends that an UE activates both the CFNRc and the CFNL
+ // (CDIV using condition not-registered) to the same target.
+ public static final int CDIV_CF_NOT_LOGGED_IN = 6;
+
+ /**
+ * CB (Communication Barring, 3GPP TS 24.611)
+ */
+ // Barring of All Incoming Calls
+ public static final int CB_BAIC = 1;
+ // Barring of All Outgoing Calls
+ public static final int CB_BAOC = 2;
+ // Barring of Outgoing International Calls
+ public static final int CB_BOIC = 3;
+ // Barring of Outgoing International Calls - excluding Home Country
+ public static final int CB_BOIC_EXHC = 4;
+ // Barring of Incoming Calls - when roaming
+ public static final int CB_BIC_WR = 5;
+ // Barring of Anonymous Communication Rejection (ACR) - a particular case of ICB service
+ public static final int CB_BIC_ACR = 6;
+ // Barring of All Calls
+ public static final int CB_BA_ALL = 7;
+ // Barring of Outgoing Services (Service Code 333 - 3GPP TS 22.030 Table B-1)
+ public static final int CB_BA_MO = 8;
+ // Barring of Incoming Services (Service Code 353 - 3GPP TS 22.030 Table B-1)
+ public static final int CB_BA_MT = 9;
+ // Barring of Specific Incoming calls
+ public static final int CB_BS_MT = 10;
+
+ /**
+ * Invalid result value.
+ */
+ public static final int INVALID = (-1);
+
+
+
+ /**
+ * Operations for the supplementary service configuration
+ */
+
+ /**
+ * Retrieves the configuration of the call barring.
+ * The return value of ((AsyncResult)result.obj) is an array of {@link ImsSsInfo}.
+ */
+ public void queryCallBarring(int cbType, Message result);
+
+ /**
+ * Retrieves the configuration of the call forward.
+ * The return value of ((AsyncResult)result.obj) is an array of {@link ImsCallForwardInfo}.
+ */
+ public void queryCallForward(int condition, String number, Message result);
+
+ /**
+ * Retrieves the configuration of the call waiting.
+ * The return value of ((AsyncResult)result.obj) is an array of {@link ImsSsInfo}.
+ */
+ public void queryCallWaiting(Message result);
+
+ /**
+ * Retrieves the default CLIR setting.
+ */
+ public void queryCLIR(Message result);
+
+ /**
+ * Retrieves the CLIP call setting.
+ */
+ public void queryCLIP(Message result);
+
+ /**
+ * Retrieves the COLR call setting.
+ */
+ public void queryCOLR(Message result);
+
+ /**
+ * Retrieves the COLP call setting.
+ */
+ public void queryCOLP(Message result);
+
+ /**
+ * Modifies the configuration of the call barring.
+ */
+ public void updateCallBarring(int cbType, int action,
+ Message result, String[] barrList);
+
+ /**
+ * Modifies the configuration of the call forward.
+ */
+ public void updateCallForward(int action, int condition, String number,
+ int serviceClass, int timeSeconds, Message result);
+
+ /**
+ * Modifies the configuration of the call waiting.
+ */
+ public void updateCallWaiting(boolean enable, int serviceClass, Message result);
+
+ /**
+ * Updates the configuration of the CLIR supplementary service.
+ */
+ public void updateCLIR(int clirMode, Message result);
+
+ /**
+ * Updates the configuration of the CLIP supplementary service.
+ */
+ public void updateCLIP(boolean enable, Message result);
+
+ /**
+ * Updates the configuration of the COLR supplementary service.
+ */
+ public void updateCOLR(int presentation, Message result);
+
+ /**
+ * Updates the configuration of the COLP supplementary service.
+ */
+ public void updateCOLP(boolean enable, Message result);
+}
diff --git a/telephony/java/com/android/ims/internal/IImsFeatureStatusCallback.aidl b/telephony/java/com/android/ims/internal/IImsFeatureStatusCallback.aidl
new file mode 100644
index 0000000..41b1042
--- /dev/null
+++ b/telephony/java/com/android/ims/internal/IImsFeatureStatusCallback.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2017 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.ims.internal;
+
+/**
+* Interface from ImsFeature in the ImsService to ImsServiceController.
+ * {@hide}
+ */
+oneway interface IImsFeatureStatusCallback {
+ void notifyImsFeatureStatus(int featureStatus);
+}
\ No newline at end of file
diff --git a/telephony/java/com/android/ims/internal/IImsServiceController.aidl b/telephony/java/com/android/ims/internal/IImsServiceController.aidl
index fa86a43..712816f 100644
--- a/telephony/java/com/android/ims/internal/IImsServiceController.aidl
+++ b/telephony/java/com/android/ims/internal/IImsServiceController.aidl
@@ -16,10 +16,49 @@
package com.android.ims.internal;
+import android.app.PendingIntent;
+
+import com.android.ims.ImsCallProfile;
+import com.android.ims.internal.IImsCallSession;
+import com.android.ims.internal.IImsCallSessionListener;
+import com.android.ims.internal.IImsConfig;
+import com.android.ims.internal.IImsEcbm;
+import com.android.ims.internal.IImsFeatureStatusCallback;
+import com.android.ims.internal.IImsMultiEndpoint;
+import com.android.ims.internal.IImsRegistrationListener;
+import com.android.ims.internal.IImsUt;
+
+import android.os.Message;
+
/**
+ * See ImsService and IMMTelFeature for more information.
* {@hide}
*/
interface IImsServiceController {
- void createImsFeature(int slotId, int feature);
+ // ImsService Control
+ void createImsFeature(int slotId, int feature, IImsFeatureStatusCallback c);
void removeImsFeature(int slotId, int feature);
+ // MMTel Feature
+ int startSession(int slotId, int featureType, in PendingIntent incomingCallIntent,
+ in IImsRegistrationListener listener);
+ void endSession(int slotId, int featureType, int sessionId);
+ boolean isConnected(int slotId, int featureType, int callSessionType, int callType);
+ boolean isOpened(int slotId, int featureType);
+ int getFeatureStatus(int slotId, int featureType);
+ void addRegistrationListener(int slotId, int featureType, in IImsRegistrationListener listener);
+ void removeRegistrationListener(int slotId, int featureType,
+ in IImsRegistrationListener listener);
+ ImsCallProfile createCallProfile(int slotId, int featureType, int sessionId,
+ int callSessionType, int callType);
+ IImsCallSession createCallSession(int slotId, int featureType, int sessionId,
+ in ImsCallProfile profile, IImsCallSessionListener listener);
+ IImsCallSession getPendingCallSession(int slotId, int featureType, int sessionId,
+ String callId);
+ IImsUt getUtInterface(int slotId, int featureType);
+ IImsConfig getConfigInterface(int slotId, int featureType);
+ void turnOnIms(int slotId, int featureType);
+ void turnOffIms(int slotId, int featureType);
+ IImsEcbm getEcbmInterface(int slotId, int featureType);
+ void setUiTTYMode(int slotId, int featureType, int uiTtyMode, in Message onComplete);
+ IImsMultiEndpoint getMultiEndpointInterface(int slotId, int featureType);
}
diff --git a/telephony/java/com/android/ims/internal/IImsServiceFeatureListener.aidl b/telephony/java/com/android/ims/internal/IImsServiceFeatureListener.aidl
index 0a36b6b..df10700 100644
--- a/telephony/java/com/android/ims/internal/IImsServiceFeatureListener.aidl
+++ b/telephony/java/com/android/ims/internal/IImsServiceFeatureListener.aidl
@@ -17,9 +17,12 @@
package com.android.ims.internal;
/**
+ * Interface from ImsResolver to ImsServiceProxy in ImsManager.
+ * Callback to ImsManager when a feature changes in the ImsServiceController.
* {@hide}
*/
oneway interface IImsServiceFeatureListener {
void imsFeatureCreated(int slotId, int feature);
void imsFeatureRemoved(int slotId, int feature);
+ void imsStatusChanged(int slotId, int feature, int status);
}
\ No newline at end of file
diff --git a/telephony/java/com/android/ims/internal/ImsCallSession.java b/telephony/java/com/android/ims/internal/ImsCallSession.java
new file mode 100644
index 0000000..8196b23
--- /dev/null
+++ b/telephony/java/com/android/ims/internal/ImsCallSession.java
@@ -0,0 +1,1290 @@
+/*
+ * Copyright (c) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.ims.internal;
+
+import android.os.Message;
+import android.os.RemoteException;
+
+import java.util.Objects;
+
+import android.telephony.ims.stub.ImsCallSessionListenerImplBase;
+import android.util.Log;
+import com.android.ims.ImsCallProfile;
+import com.android.ims.ImsConferenceState;
+import com.android.ims.ImsReasonInfo;
+import com.android.ims.ImsStreamMediaProfile;
+import com.android.ims.ImsSuppServiceNotification;
+
+/**
+ * Provides the call initiation/termination, and media exchange between two IMS endpoints.
+ * It directly communicates with IMS service which implements the IMS protocol behavior.
+ *
+ * @hide
+ */
+public class ImsCallSession {
+ private static final String TAG = "ImsCallSession";
+
+ /**
+ * Defines IMS call session state.
+ */
+ public static class State {
+ public static final int IDLE = 0;
+ public static final int INITIATED = 1;
+ public static final int NEGOTIATING = 2;
+ public static final int ESTABLISHING = 3;
+ public static final int ESTABLISHED = 4;
+
+ public static final int RENEGOTIATING = 5;
+ public static final int REESTABLISHING = 6;
+
+ public static final int TERMINATING = 7;
+ public static final int TERMINATED = 8;
+
+ public static final int INVALID = (-1);
+
+ /**
+ * Converts the state to string.
+ */
+ public static String toString(int state) {
+ switch (state) {
+ case IDLE:
+ return "IDLE";
+ case INITIATED:
+ return "INITIATED";
+ case NEGOTIATING:
+ return "NEGOTIATING";
+ case ESTABLISHING:
+ return "ESTABLISHING";
+ case ESTABLISHED:
+ return "ESTABLISHED";
+ case RENEGOTIATING:
+ return "RENEGOTIATING";
+ case REESTABLISHING:
+ return "REESTABLISHING";
+ case TERMINATING:
+ return "TERMINATING";
+ case TERMINATED:
+ return "TERMINATED";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ private State() {
+ }
+ }
+
+ /**
+ * Listener for events relating to an IMS session, such as when a session is being
+ * recieved ("on ringing") or a call is outgoing ("on calling").
+ * <p>Many of these events are also received by {@link ImsCall.Listener}.</p>
+ */
+ public static class Listener {
+ /**
+ * Called when a request is sent out to initiate a new session
+ * and 1xx response is received from the network.
+ *
+ * @param session the session object that carries out the IMS session
+ */
+ public void callSessionProgressing(ImsCallSession session,
+ ImsStreamMediaProfile profile) {
+ // no-op
+ }
+
+ /**
+ * Called when the session is established.
+ *
+ * @param session the session object that carries out the IMS session
+ */
+ public void callSessionStarted(ImsCallSession session,
+ ImsCallProfile profile) {
+ // no-op
+ }
+
+ /**
+ * Called when the session establishment is failed.
+ *
+ * @param session the session object that carries out the IMS session
+ * @param reasonInfo detailed reason of the session establishment failure
+ */
+ public void callSessionStartFailed(ImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ }
+
+ /**
+ * Called when the session is terminated.
+ *
+ * @param session the session object that carries out the IMS session
+ * @param reasonInfo detailed reason of the session termination
+ */
+ public void callSessionTerminated(ImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ }
+
+ /**
+ * Called when the session is in hold.
+ *
+ * @param session the session object that carries out the IMS session
+ */
+ public void callSessionHeld(ImsCallSession session,
+ ImsCallProfile profile) {
+ }
+
+ /**
+ * Called when the session hold is failed.
+ *
+ * @param session the session object that carries out the IMS session
+ * @param reasonInfo detailed reason of the session hold failure
+ */
+ public void callSessionHoldFailed(ImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ }
+
+ /**
+ * Called when the session hold is received from the remote user.
+ *
+ * @param session the session object that carries out the IMS session
+ */
+ public void callSessionHoldReceived(ImsCallSession session,
+ ImsCallProfile profile) {
+ }
+
+ /**
+ * Called when the session resume is done.
+ *
+ * @param session the session object that carries out the IMS session
+ */
+ public void callSessionResumed(ImsCallSession session,
+ ImsCallProfile profile) {
+ }
+
+ /**
+ * Called when the session resume is failed.
+ *
+ * @param session the session object that carries out the IMS session
+ * @param reasonInfo detailed reason of the session resume failure
+ */
+ public void callSessionResumeFailed(ImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ }
+
+ /**
+ * Called when the session resume is received from the remote user.
+ *
+ * @param session the session object that carries out the IMS session
+ */
+ public void callSessionResumeReceived(ImsCallSession session,
+ ImsCallProfile profile) {
+ }
+
+ /**
+ * Called when the session merge has been started. At this point, the {@code newSession}
+ * represents the session which has been initiated to the IMS conference server for the
+ * new merged conference.
+ *
+ * @param session the session object that carries out the IMS session
+ * @param newSession the session object that is merged with an active & hold session
+ */
+ public void callSessionMergeStarted(ImsCallSession session,
+ ImsCallSession newSession, ImsCallProfile profile) {
+ }
+
+ /**
+ * Called when the session merge is successful and the merged session is active.
+ *
+ * @param session the session object that carries out the IMS session
+ */
+ public void callSessionMergeComplete(ImsCallSession session) {
+ }
+
+ /**
+ * Called when the session merge has failed.
+ *
+ * @param session the session object that carries out the IMS session
+ * @param reasonInfo detailed reason of the call merge failure
+ */
+ public void callSessionMergeFailed(ImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ }
+
+ /**
+ * Called when the session is updated (except for hold/unhold).
+ *
+ * @param session the session object that carries out the IMS session
+ */
+ public void callSessionUpdated(ImsCallSession session,
+ ImsCallProfile profile) {
+ }
+
+ /**
+ * Called when the session update is failed.
+ *
+ * @param session the session object that carries out the IMS session
+ * @param reasonInfo detailed reason of the session update failure
+ */
+ public void callSessionUpdateFailed(ImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ }
+
+ /**
+ * Called when the session update is received from the remote user.
+ *
+ * @param session the session object that carries out the IMS session
+ */
+ public void callSessionUpdateReceived(ImsCallSession session,
+ ImsCallProfile profile) {
+ // no-op
+ }
+
+ /**
+ * Called when the session is extended to the conference session.
+ *
+ * @param session the session object that carries out the IMS session
+ * @param newSession the session object that is extended to the conference
+ * from the active session
+ */
+ public void callSessionConferenceExtended(ImsCallSession session,
+ ImsCallSession newSession, ImsCallProfile profile) {
+ }
+
+ /**
+ * Called when the conference extension is failed.
+ *
+ * @param session the session object that carries out the IMS session
+ * @param reasonInfo detailed reason of the conference extension failure
+ */
+ public void callSessionConferenceExtendFailed(ImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ }
+
+ /**
+ * Called when the conference extension is received from the remote user.
+ *
+ * @param session the session object that carries out the IMS session
+ */
+ public void callSessionConferenceExtendReceived(ImsCallSession session,
+ ImsCallSession newSession, ImsCallProfile profile) {
+ // no-op
+ }
+
+ /**
+ * Called when the invitation request of the participants is delivered to the conference
+ * server.
+ *
+ * @param session the session object that carries out the IMS session
+ */
+ public void callSessionInviteParticipantsRequestDelivered(ImsCallSession session) {
+ // no-op
+ }
+
+ /**
+ * Called when the invitation request of the participants is failed.
+ *
+ * @param session the session object that carries out the IMS session
+ * @param reasonInfo detailed reason of the conference invitation failure
+ */
+ public void callSessionInviteParticipantsRequestFailed(ImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ /**
+ * Called when the removal request of the participants is delivered to the conference
+ * server.
+ *
+ * @param session the session object that carries out the IMS session
+ */
+ public void callSessionRemoveParticipantsRequestDelivered(ImsCallSession session) {
+ // no-op
+ }
+
+ /**
+ * Called when the removal request of the participants is failed.
+ *
+ * @param session the session object that carries out the IMS session
+ * @param reasonInfo detailed reason of the conference removal failure
+ */
+ public void callSessionRemoveParticipantsRequestFailed(ImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ /**
+ * Called when the conference state is updated.
+ *
+ * @param session the session object that carries out the IMS session
+ */
+ public void callSessionConferenceStateUpdated(ImsCallSession session,
+ ImsConferenceState state) {
+ // no-op
+ }
+
+ /**
+ * Called when the USSD message is received from the network.
+ *
+ * @param mode mode of the USSD message (REQUEST / NOTIFY)
+ * @param ussdMessage USSD message
+ */
+ public void callSessionUssdMessageReceived(ImsCallSession session,
+ int mode, String ussdMessage) {
+ // no-op
+ }
+
+ /**
+ * Called when session access technology changes
+ *
+ * @param session IMS session object
+ * @param srcAccessTech original access technology
+ * @param targetAccessTech new access technology
+ * @param reasonInfo
+ */
+ public void callSessionHandover(ImsCallSession session,
+ int srcAccessTech, int targetAccessTech,
+ ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ /**
+ * Called when session access technology change fails
+ *
+ * @param session IMS session object
+ * @param srcAccessTech original access technology
+ * @param targetAccessTech new access technology
+ * @param reasonInfo handover failure reason
+ */
+ public void callSessionHandoverFailed(ImsCallSession session,
+ int srcAccessTech, int targetAccessTech,
+ ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ /**
+ * Called when TTY mode of remote party changed
+ *
+ * @param session IMS session object
+ * @param mode TTY mode of remote party
+ */
+ public void callSessionTtyModeReceived(ImsCallSession session,
+ int mode) {
+ // no-op
+ }
+
+ /**
+ * Notifies of a change to the multiparty state for this {@code ImsCallSession}.
+ *
+ * @param session The call session.
+ * @param isMultiParty {@code true} if the session became multiparty, {@code false}
+ * otherwise.
+ */
+ public void callSessionMultipartyStateChanged(ImsCallSession session,
+ boolean isMultiParty) {
+ // no-op
+ }
+
+ /**
+ * Called when the session supplementary service is received
+ *
+ * @param session the session object that carries out the IMS session
+ */
+ public void callSessionSuppServiceReceived(ImsCallSession session,
+ ImsSuppServiceNotification suppServiceInfo) {
+ }
+ }
+
+ private final IImsCallSession miSession;
+ private boolean mClosed = false;
+ private Listener mListener;
+
+ public ImsCallSession(IImsCallSession iSession) {
+ miSession = iSession;
+
+ if (iSession != null) {
+ try {
+ iSession.setListener(new IImsCallSessionListenerProxy());
+ } catch (RemoteException e) {
+ }
+ } else {
+ mClosed = true;
+ }
+ }
+
+ public ImsCallSession(IImsCallSession iSession, Listener listener) {
+ this(iSession);
+ setListener(listener);
+ }
+
+ /**
+ * Closes this object. This object is not usable after being closed.
+ */
+ public synchronized void close() {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.close();
+ mClosed = true;
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Gets the call ID of the session.
+ *
+ * @return the call ID
+ */
+ public String getCallId() {
+ if (mClosed) {
+ return null;
+ }
+
+ try {
+ return miSession.getCallId();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Gets the call profile that this session is associated with
+ *
+ * @return the call profile that this session is associated with
+ */
+ public ImsCallProfile getCallProfile() {
+ if (mClosed) {
+ return null;
+ }
+
+ try {
+ return miSession.getCallProfile();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Gets the local call profile that this session is associated with
+ *
+ * @return the local call profile that this session is associated with
+ */
+ public ImsCallProfile getLocalCallProfile() {
+ if (mClosed) {
+ return null;
+ }
+
+ try {
+ return miSession.getLocalCallProfile();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Gets the remote call profile that this session is associated with
+ *
+ * @return the remote call profile that this session is associated with
+ */
+ public ImsCallProfile getRemoteCallProfile() {
+ if (mClosed) {
+ return null;
+ }
+
+ try {
+ return miSession.getRemoteCallProfile();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Gets the video call provider for the session.
+ *
+ * @return The video call provider.
+ */
+ public IImsVideoCallProvider getVideoCallProvider() {
+ if (mClosed) {
+ return null;
+ }
+
+ try {
+ return miSession.getVideoCallProvider();
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Gets the value associated with the specified property of this session.
+ *
+ * @return the string value associated with the specified property
+ */
+ public String getProperty(String name) {
+ if (mClosed) {
+ return null;
+ }
+
+ try {
+ return miSession.getProperty(name);
+ } catch (RemoteException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Gets the session state.
+ * The value returned must be one of the states in {@link State}.
+ *
+ * @return the session state
+ */
+ public int getState() {
+ if (mClosed) {
+ return State.INVALID;
+ }
+
+ try {
+ return miSession.getState();
+ } catch (RemoteException e) {
+ return State.INVALID;
+ }
+ }
+
+ /**
+ * Determines if the {@link ImsCallSession} is currently alive (e.g. not in a terminated or
+ * closed state).
+ *
+ * @return {@code True} if the session is alive.
+ */
+ public boolean isAlive() {
+ if (mClosed) {
+ return false;
+ }
+
+ int state = getState();
+ switch (state) {
+ case State.IDLE:
+ case State.INITIATED:
+ case State.NEGOTIATING:
+ case State.ESTABLISHING:
+ case State.ESTABLISHED:
+ case State.RENEGOTIATING:
+ case State.REESTABLISHING:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Gets the native IMS call session.
+ * @hide
+ */
+ public IImsCallSession getSession() {
+ return miSession;
+ }
+
+ /**
+ * Checks if the session is in call.
+ *
+ * @return true if the session is in call
+ */
+ public boolean isInCall() {
+ if (mClosed) {
+ return false;
+ }
+
+ try {
+ return miSession.isInCall();
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Sets the listener to listen to the session events. A {@link ImsCallSession}
+ * can only hold one listener at a time. Subsequent calls to this method
+ * override the previous listener.
+ *
+ * @param listener to listen to the session events of this object
+ */
+ public void setListener(Listener listener) {
+ mListener = listener;
+ }
+
+ /**
+ * Mutes or unmutes the mic for the active call.
+ *
+ * @param muted true if the call is muted, false otherwise
+ */
+ public void setMute(boolean muted) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.setMute(muted);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Initiates an IMS call with the specified target and call profile.
+ * The session listener is called back upon defined session events.
+ * The method is only valid to call when the session state is in
+ * {@link ImsCallSession.State#IDLE}.
+ *
+ * @param callee dialed string to make the call to
+ * @param profile call profile to make the call with the specified service type,
+ * call type and media information
+ * @see Listener#callSessionStarted, Listener#callSessionStartFailed
+ */
+ public void start(String callee, ImsCallProfile profile) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.start(callee, profile);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Initiates an IMS conference call with the specified target and call profile.
+ * The session listener is called back upon defined session events.
+ * The method is only valid to call when the session state is in
+ * {@link ImsCallSession.State#IDLE}.
+ *
+ * @param participants participant list to initiate an IMS conference call
+ * @param profile call profile to make the call with the specified service type,
+ * call type and media information
+ * @see Listener#callSessionStarted, Listener#callSessionStartFailed
+ */
+ public void start(String[] participants, ImsCallProfile profile) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.startConference(participants, profile);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Accepts an incoming call or session update.
+ *
+ * @param callType call type specified in {@link ImsCallProfile} to be answered
+ * @param profile stream media profile {@link ImsStreamMediaProfile} to be answered
+ * @see Listener#callSessionStarted
+ */
+ public void accept(int callType, ImsStreamMediaProfile profile) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.accept(callType, profile);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Rejects an incoming call or session update.
+ *
+ * @param reason reason code to reject an incoming call
+ * @see Listener#callSessionStartFailed
+ */
+ public void reject(int reason) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.reject(reason);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Terminates a call.
+ *
+ * @see Listener#callSessionTerminated
+ */
+ public void terminate(int reason) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.terminate(reason);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Puts a call on hold. When it succeeds, {@link Listener#callSessionHeld} is called.
+ *
+ * @param profile stream media profile {@link ImsStreamMediaProfile} to hold the call
+ * @see Listener#callSessionHeld, Listener#callSessionHoldFailed
+ */
+ public void hold(ImsStreamMediaProfile profile) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.hold(profile);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Continues a call that's on hold. When it succeeds,
+ * {@link Listener#callSessionResumed} is called.
+ *
+ * @param profile stream media profile {@link ImsStreamMediaProfile} to resume the call
+ * @see Listener#callSessionResumed, Listener#callSessionResumeFailed
+ */
+ public void resume(ImsStreamMediaProfile profile) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.resume(profile);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Merges the active & hold call. When it succeeds,
+ * {@link Listener#callSessionMergeStarted} is called.
+ *
+ * @see Listener#callSessionMergeStarted , Listener#callSessionMergeFailed
+ */
+ public void merge() {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.merge();
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Updates the current call's properties (ex. call mode change: video upgrade / downgrade).
+ *
+ * @param callType call type specified in {@link ImsCallProfile} to be updated
+ * @param profile stream media profile {@link ImsStreamMediaProfile} to be updated
+ * @see Listener#callSessionUpdated, Listener#callSessionUpdateFailed
+ */
+ public void update(int callType, ImsStreamMediaProfile profile) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.update(callType, profile);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Extends this call to the conference call with the specified recipients.
+ *
+ * @param participants list to be invited to the conference call after extending the call
+ * @see Listener#callSessionConferenceExtended
+ * @see Listener#callSessionConferenceExtendFailed
+ */
+ public void extendToConference(String[] participants) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.extendToConference(participants);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Requests the conference server to invite an additional participants to the conference.
+ *
+ * @param participants list to be invited to the conference call
+ * @see Listener#callSessionInviteParticipantsRequestDelivered
+ * @see Listener#callSessionInviteParticipantsRequestFailed
+ */
+ public void inviteParticipants(String[] participants) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.inviteParticipants(participants);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Requests the conference server to remove the specified participants from the conference.
+ *
+ * @param participants participant list to be removed from the conference call
+ * @see Listener#callSessionRemoveParticipantsRequestDelivered
+ * @see Listener#callSessionRemoveParticipantsRequestFailed
+ */
+ public void removeParticipants(String[] participants) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.removeParticipants(participants);
+ } catch (RemoteException e) {
+ }
+ }
+
+
+ /**
+ * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
+ * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
+ * and event flash to 16. Currently, event flash is not supported.
+ *
+ * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
+ */
+ public void sendDtmf(char c, Message result) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.sendDtmf(c, result);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Starts a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
+ * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
+ * and event flash to 16. Currently, event flash is not supported.
+ *
+ * @param c the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
+ */
+ public void startDtmf(char c) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.startDtmf(c);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Stops a DTMF code.
+ */
+ public void stopDtmf() {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.stopDtmf();
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Sends an USSD message.
+ *
+ * @param ussdMessage USSD message to send
+ */
+ public void sendUssd(String ussdMessage) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.sendUssd(ussdMessage);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Determines if the session is multiparty.
+ *
+ * @return {@code True} if the session is multiparty.
+ */
+ public boolean isMultiparty() {
+ if (mClosed) {
+ return false;
+ }
+
+ try {
+ return miSession.isMultiparty();
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * A listener type for receiving notification on IMS call session events.
+ * When an event is generated for an {@link IImsCallSession},
+ * the application is notified by having one of the methods called on
+ * the {@link IImsCallSessionListener}.
+ */
+ private class IImsCallSessionListenerProxy extends ImsCallSessionListenerImplBase {
+ /**
+ * Notifies the result of the basic session operation (setup / terminate).
+ */
+ @Override
+ public void callSessionProgressing(IImsCallSession session,
+ ImsStreamMediaProfile profile) {
+ if (mListener != null) {
+ mListener.callSessionProgressing(ImsCallSession.this, profile);
+ }
+ }
+
+ @Override
+ public void callSessionStarted(IImsCallSession session,
+ ImsCallProfile profile) {
+ if (mListener != null) {
+ mListener.callSessionStarted(ImsCallSession.this, profile);
+ }
+ }
+
+ @Override
+ public void callSessionStartFailed(IImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionStartFailed(ImsCallSession.this, reasonInfo);
+ }
+ }
+
+ @Override
+ public void callSessionTerminated(IImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionTerminated(ImsCallSession.this, reasonInfo);
+ }
+ }
+
+ /**
+ * Notifies the result of the call hold/resume operation.
+ */
+ @Override
+ public void callSessionHeld(IImsCallSession session,
+ ImsCallProfile profile) {
+ if (mListener != null) {
+ mListener.callSessionHeld(ImsCallSession.this, profile);
+ }
+ }
+
+ @Override
+ public void callSessionHoldFailed(IImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionHoldFailed(ImsCallSession.this, reasonInfo);
+ }
+ }
+
+ @Override
+ public void callSessionHoldReceived(IImsCallSession session,
+ ImsCallProfile profile) {
+ if (mListener != null) {
+ mListener.callSessionHoldReceived(ImsCallSession.this, profile);
+ }
+ }
+
+ @Override
+ public void callSessionResumed(IImsCallSession session,
+ ImsCallProfile profile) {
+ if (mListener != null) {
+ mListener.callSessionResumed(ImsCallSession.this, profile);
+ }
+ }
+
+ @Override
+ public void callSessionResumeFailed(IImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionResumeFailed(ImsCallSession.this, reasonInfo);
+ }
+ }
+
+ @Override
+ public void callSessionResumeReceived(IImsCallSession session,
+ ImsCallProfile profile) {
+ if (mListener != null) {
+ mListener.callSessionResumeReceived(ImsCallSession.this, profile);
+ }
+ }
+
+ /**
+ * Notifies the start of a call merge operation.
+ *
+ * @param session The call session.
+ * @param newSession The merged call session.
+ * @param profile The call profile.
+ */
+ @Override
+ public void callSessionMergeStarted(IImsCallSession session,
+ IImsCallSession newSession, ImsCallProfile profile) {
+ // This callback can be used for future use to add additional
+ // functionality that may be needed between conference start and complete
+ Log.d(TAG, "callSessionMergeStarted");
+ }
+
+ /**
+ * Notifies the successful completion of a call merge operation.
+ *
+ * @param newSession The call session.
+ */
+ @Override
+ public void callSessionMergeComplete(IImsCallSession newSession) {
+ if (mListener != null) {
+ if (newSession != null) {
+ // Check if the active session is the same session that was
+ // active before the merge request was sent.
+ ImsCallSession validActiveSession = ImsCallSession.this;
+ try {
+ if (!Objects.equals(miSession.getCallId(), newSession.getCallId())) {
+ // New session created after conference
+ validActiveSession = new ImsCallSession(newSession);
+ }
+ } catch (RemoteException rex) {
+ Log.e(TAG, "callSessionMergeComplete: exception for getCallId!");
+ }
+ mListener.callSessionMergeComplete(validActiveSession);
+ } else {
+ // Session already exists. Hence no need to pass
+ mListener.callSessionMergeComplete(null);
+ }
+ }
+ }
+
+ /**
+ * Notifies of a failure to perform a call merge operation.
+ *
+ * @param session The call session.
+ * @param reasonInfo The merge failure reason.
+ */
+ @Override
+ public void callSessionMergeFailed(IImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionMergeFailed(ImsCallSession.this, reasonInfo);
+ }
+ }
+
+ /**
+ * Notifies the result of call upgrade / downgrade or any other call updates.
+ */
+ @Override
+ public void callSessionUpdated(IImsCallSession session,
+ ImsCallProfile profile) {
+ if (mListener != null) {
+ mListener.callSessionUpdated(ImsCallSession.this, profile);
+ }
+ }
+
+ @Override
+ public void callSessionUpdateFailed(IImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionUpdateFailed(ImsCallSession.this, reasonInfo);
+ }
+ }
+
+ @Override
+ public void callSessionUpdateReceived(IImsCallSession session,
+ ImsCallProfile profile) {
+ if (mListener != null) {
+ mListener.callSessionUpdateReceived(ImsCallSession.this, profile);
+ }
+ }
+
+ /**
+ * Notifies the result of conference extension.
+ */
+ @Override
+ public void callSessionConferenceExtended(IImsCallSession session,
+ IImsCallSession newSession, ImsCallProfile profile) {
+ if (mListener != null) {
+ mListener.callSessionConferenceExtended(ImsCallSession.this,
+ new ImsCallSession(newSession), profile);
+ }
+ }
+
+ @Override
+ public void callSessionConferenceExtendFailed(IImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionConferenceExtendFailed(ImsCallSession.this, reasonInfo);
+ }
+ }
+
+ @Override
+ public void callSessionConferenceExtendReceived(IImsCallSession session,
+ IImsCallSession newSession, ImsCallProfile profile) {
+ if (mListener != null) {
+ mListener.callSessionConferenceExtendReceived(ImsCallSession.this,
+ new ImsCallSession(newSession), profile);
+ }
+ }
+
+ /**
+ * Notifies the result of the participant invitation / removal to/from
+ * the conference session.
+ */
+ @Override
+ public void callSessionInviteParticipantsRequestDelivered(IImsCallSession session) {
+ if (mListener != null) {
+ mListener.callSessionInviteParticipantsRequestDelivered(ImsCallSession.this);
+ }
+ }
+
+ @Override
+ public void callSessionInviteParticipantsRequestFailed(IImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionInviteParticipantsRequestFailed(ImsCallSession.this,
+ reasonInfo);
+ }
+ }
+
+ @Override
+ public void callSessionRemoveParticipantsRequestDelivered(IImsCallSession session) {
+ if (mListener != null) {
+ mListener.callSessionRemoveParticipantsRequestDelivered(ImsCallSession.this);
+ }
+ }
+
+ @Override
+ public void callSessionRemoveParticipantsRequestFailed(IImsCallSession session,
+ ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionRemoveParticipantsRequestFailed(ImsCallSession.this,
+ reasonInfo);
+ }
+ }
+
+ /**
+ * Notifies the changes of the conference info. in the conference session.
+ */
+ @Override
+ public void callSessionConferenceStateUpdated(IImsCallSession session,
+ ImsConferenceState state) {
+ if (mListener != null) {
+ mListener.callSessionConferenceStateUpdated(ImsCallSession.this, state);
+ }
+ }
+
+ /**
+ * Notifies the incoming USSD message.
+ */
+ @Override
+ public void callSessionUssdMessageReceived(IImsCallSession session,
+ int mode, String ussdMessage) {
+ if (mListener != null) {
+ mListener.callSessionUssdMessageReceived(ImsCallSession.this, mode, ussdMessage);
+ }
+ }
+
+ /**
+ * Notifies of handover information for this call
+ */
+ @Override
+ public void callSessionHandover(IImsCallSession session,
+ int srcAccessTech, int targetAccessTech,
+ ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionHandover(ImsCallSession.this, srcAccessTech,
+ targetAccessTech, reasonInfo);
+ }
+ }
+
+ /**
+ * Notifies of handover failure info for this call
+ */
+ @Override
+ public void callSessionHandoverFailed(IImsCallSession session,
+ int srcAccessTech, int targetAccessTech,
+ ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionHandoverFailed(ImsCallSession.this, srcAccessTech,
+ targetAccessTech, reasonInfo);
+ }
+ }
+
+ /**
+ * Notifies the TTY mode received from remote party.
+ */
+ @Override
+ public void callSessionTtyModeReceived(IImsCallSession session,
+ int mode) {
+ if (mListener != null) {
+ mListener.callSessionTtyModeReceived(ImsCallSession.this, mode);
+ }
+ }
+
+ /**
+ * Notifies of a change to the multiparty state for this {@code ImsCallSession}.
+ *
+ * @param session The call session.
+ * @param isMultiParty {@code true} if the session became multiparty, {@code false}
+ * otherwise.
+ */
+ public void callSessionMultipartyStateChanged(IImsCallSession session,
+ boolean isMultiParty) {
+
+ if (mListener != null) {
+ mListener.callSessionMultipartyStateChanged(ImsCallSession.this, isMultiParty);
+ }
+ }
+
+ @Override
+ public void callSessionSuppServiceReceived(IImsCallSession session,
+ ImsSuppServiceNotification suppServiceInfo ) {
+ if (mListener != null) {
+ mListener.callSessionSuppServiceReceived(ImsCallSession.this, suppServiceInfo);
+ }
+ }
+
+ }
+
+ /**
+ * Provides a string representation of the {@link ImsCallSession}. Primarily intended for
+ * use in log statements.
+ *
+ * @return String representation of session.
+ */
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("[ImsCallSession objId:");
+ sb.append(System.identityHashCode(this));
+ sb.append(" state:");
+ sb.append(State.toString(getState()));
+ sb.append(" callId:");
+ sb.append(getCallId());
+ sb.append("]");
+ return sb.toString();
+ }
+}
diff --git a/telephony/java/com/android/ims/internal/ImsVideoCallProvider.java b/telephony/java/com/android/ims/internal/ImsVideoCallProvider.java
new file mode 100644
index 0000000..432dc39
--- /dev/null
+++ b/telephony/java/com/android/ims/internal/ImsVideoCallProvider.java
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2017 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.ims.internal;
+
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.telecom.Connection;
+import android.telecom.VideoProfile;
+import android.telecom.VideoProfile.CameraCapabilities;
+import android.view.Surface;
+
+import com.android.internal.os.SomeArgs;
+
+/**
+ * @hide
+ */
+public abstract class ImsVideoCallProvider {
+ private static final int MSG_SET_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;
+ private static final int MSG_SET_DEVICE_ORIENTATION = 5;
+ private static final int MSG_SET_ZOOM = 6;
+ private static final int MSG_SEND_SESSION_MODIFY_REQUEST = 7;
+ private static final int MSG_SEND_SESSION_MODIFY_RESPONSE = 8;
+ private static final int MSG_REQUEST_CAMERA_CAPABILITIES = 9;
+ private static final int MSG_REQUEST_CALL_DATA_USAGE = 10;
+ private static final int MSG_SET_PAUSE_IMAGE = 11;
+
+ private final ImsVideoCallProviderBinder mBinder;
+
+ private IImsVideoCallCallback mCallback;
+
+ /**
+ * Default handler used to consolidate binder method calls onto a single thread.
+ */
+ private final Handler mProviderHandler = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_SET_CALLBACK:
+ mCallback = (IImsVideoCallCallback) msg.obj;
+ break;
+ case MSG_SET_CAMERA:
+ {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ onSetCamera((String) args.arg1);
+ onSetCamera((String) args.arg1, args.argi1);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ case MSG_SET_PREVIEW_SURFACE:
+ onSetPreviewSurface((Surface) msg.obj);
+ break;
+ case MSG_SET_DISPLAY_SURFACE:
+ onSetDisplaySurface((Surface) msg.obj);
+ break;
+ case MSG_SET_DEVICE_ORIENTATION:
+ onSetDeviceOrientation(msg.arg1);
+ break;
+ case MSG_SET_ZOOM:
+ onSetZoom((Float) msg.obj);
+ break;
+ case MSG_SEND_SESSION_MODIFY_REQUEST: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ try {
+ VideoProfile fromProfile = (VideoProfile) args.arg1;
+ VideoProfile toProfile = (VideoProfile) args.arg2;
+
+ onSendSessionModifyRequest(fromProfile, toProfile);
+ } finally {
+ args.recycle();
+ }
+ break;
+ }
+ case MSG_SEND_SESSION_MODIFY_RESPONSE:
+ onSendSessionModifyResponse((VideoProfile) msg.obj);
+ break;
+ case MSG_REQUEST_CAMERA_CAPABILITIES:
+ onRequestCameraCapabilities();
+ break;
+ case MSG_REQUEST_CALL_DATA_USAGE:
+ onRequestCallDataUsage();
+ break;
+ case MSG_SET_PAUSE_IMAGE:
+ onSetPauseImage((Uri) msg.obj);
+ break;
+ default:
+ break;
+ }
+ }
+ };
+
+ /**
+ * IImsVideoCallProvider stub implementation.
+ */
+ private final class ImsVideoCallProviderBinder extends IImsVideoCallProvider.Stub {
+ public void setCallback(IImsVideoCallCallback callback) {
+ mProviderHandler.obtainMessage(MSG_SET_CALLBACK, callback).sendToTarget();
+ }
+
+ public void setCamera(String cameraId, int uid) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = cameraId;
+ args.argi1 = uid;
+ mProviderHandler.obtainMessage(MSG_SET_CAMERA, args).sendToTarget();
+ }
+
+ public void setPreviewSurface(Surface surface) {
+ mProviderHandler.obtainMessage(MSG_SET_PREVIEW_SURFACE, surface).sendToTarget();
+ }
+
+ public void setDisplaySurface(Surface surface) {
+ mProviderHandler.obtainMessage(MSG_SET_DISPLAY_SURFACE, surface).sendToTarget();
+ }
+
+ public void setDeviceOrientation(int rotation) {
+ mProviderHandler.obtainMessage(MSG_SET_DEVICE_ORIENTATION, rotation, 0).sendToTarget();
+ }
+
+ public void setZoom(float value) {
+ mProviderHandler.obtainMessage(MSG_SET_ZOOM, value).sendToTarget();
+ }
+
+ public void sendSessionModifyRequest(VideoProfile fromProfile, VideoProfile toProfile) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = fromProfile;
+ args.arg2 = toProfile;
+ mProviderHandler.obtainMessage(MSG_SEND_SESSION_MODIFY_REQUEST, args).sendToTarget();
+ }
+
+ public void sendSessionModifyResponse(VideoProfile responseProfile) {
+ mProviderHandler.obtainMessage(
+ MSG_SEND_SESSION_MODIFY_RESPONSE, responseProfile).sendToTarget();
+ }
+
+ public void requestCameraCapabilities() {
+ mProviderHandler.obtainMessage(MSG_REQUEST_CAMERA_CAPABILITIES).sendToTarget();
+ }
+
+ public void requestCallDataUsage() {
+ mProviderHandler.obtainMessage(MSG_REQUEST_CALL_DATA_USAGE).sendToTarget();
+ }
+
+ public void setPauseImage(Uri uri) {
+ mProviderHandler.obtainMessage(MSG_SET_PAUSE_IMAGE, uri).sendToTarget();
+ }
+ }
+
+ public ImsVideoCallProvider() {
+ mBinder = new ImsVideoCallProviderBinder();
+ }
+
+ /**
+ * Returns binder object which can be used across IPC methods.
+ */
+ public final IImsVideoCallProvider getInterface() {
+ return mBinder;
+ }
+
+ /** @see Connection.VideoProvider#onSetCamera */
+ public abstract void onSetCamera(String cameraId);
+
+ /**
+ * Similar to {@link #onSetCamera(String)}, except includes the UID of the calling process which
+ * the IMS service uses when opening the camera. This ensures camera permissions are verified
+ * by the camera service.
+ *
+ * @param cameraId The id of the camera to be opened.
+ * @param uid The uid of the caller, used when opening the camera for permission verification.
+ * @see Connection.VideoProvider#onSetCamera
+ */
+ public void onSetCamera(String cameraId, int uid) {
+ }
+
+ /** @see Connection.VideoProvider#onSetPreviewSurface */
+ public abstract void onSetPreviewSurface(Surface surface);
+
+ /** @see Connection.VideoProvider#onSetDisplaySurface */
+ public abstract void onSetDisplaySurface(Surface surface);
+
+ /** @see Connection.VideoProvider#onSetDeviceOrientation */
+ public abstract void onSetDeviceOrientation(int rotation);
+
+ /** @see Connection.VideoProvider#onSetZoom */
+ public abstract void onSetZoom(float value);
+
+ /** @see Connection.VideoProvider#onSendSessionModifyRequest */
+ public abstract void onSendSessionModifyRequest(VideoProfile fromProfile,
+ VideoProfile toProfile);
+
+ /** @see Connection.VideoProvider#onSendSessionModifyResponse */
+ public abstract void onSendSessionModifyResponse(VideoProfile responseProfile);
+
+ /** @see Connection.VideoProvider#onRequestCameraCapabilities */
+ public abstract void onRequestCameraCapabilities();
+
+ /** @see Connection.VideoProvider#onRequestCallDataUsage */
+ public abstract void onRequestCallDataUsage();
+
+ /** @see Connection.VideoProvider#onSetPauseImage */
+ public abstract void onSetPauseImage(Uri uri);
+
+ /** @see Connection.VideoProvider#receiveSessionModifyRequest */
+ public void receiveSessionModifyRequest(VideoProfile VideoProfile) {
+ if (mCallback != null) {
+ try {
+ mCallback.receiveSessionModifyRequest(VideoProfile);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ /** @see Connection.VideoProvider#receiveSessionModifyResponse */
+ public void receiveSessionModifyResponse(
+ int status, VideoProfile requestedProfile, VideoProfile responseProfile) {
+ if (mCallback != null) {
+ try {
+ mCallback.receiveSessionModifyResponse(status, requestedProfile, responseProfile);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ /** @see Connection.VideoProvider#handleCallSessionEvent */
+ public void handleCallSessionEvent(int event) {
+ if (mCallback != null) {
+ try {
+ mCallback.handleCallSessionEvent(event);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ /** @see Connection.VideoProvider#changePeerDimensions */
+ public void changePeerDimensions(int width, int height) {
+ if (mCallback != null) {
+ try {
+ mCallback.changePeerDimensions(width, height);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ /** @see Connection.VideoProvider#changeCallDataUsage */
+ public void changeCallDataUsage(long dataUsage) {
+ if (mCallback != null) {
+ try {
+ mCallback.changeCallDataUsage(dataUsage);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ /** @see Connection.VideoProvider#changeCameraCapabilities */
+ public void changeCameraCapabilities(CameraCapabilities CameraCapabilities) {
+ if (mCallback != null) {
+ try {
+ mCallback.changeCameraCapabilities(CameraCapabilities);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ /** @see Connection.VideoProvider#changeVideoQuality */
+ public void changeVideoQuality(int videoQuality) {
+ if (mCallback != null) {
+ try {
+ mCallback.changeVideoQuality(videoQuality);
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/CallerInfo.java b/telephony/java/com/android/internal/telephony/CallerInfo.java
index 5f3f773..c9c48dc 100644
--- a/telephony/java/com/android/internal/telephony/CallerInfo.java
+++ b/telephony/java/com/android/internal/telephony/CallerInfo.java
@@ -30,9 +30,9 @@
import android.provider.ContactsContract.PhoneLookup;
import android.provider.ContactsContract.RawContacts;
import android.telephony.PhoneNumberUtils;
+import android.telephony.Rlog;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
-import android.telephony.Rlog;
import android.util.Log;
import com.android.i18n.phonenumbers.geocoding.PhoneNumberOfflineGeocoder;
@@ -595,7 +595,8 @@
pn = util.parse(number, countryIso);
if (VDBG) Rlog.v(TAG, "- parsed number: " + pn);
} catch (NumberParseException e) {
- Rlog.w(TAG, "getGeoDescription: NumberParseException for incoming number '" + number + "'");
+ Rlog.w(TAG, "getGeoDescription: NumberParseException for incoming number '"
+ + Rlog.pii(TAG, number) + "'");
}
if (pn != null) {
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index f89c82b..d21efc6 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1218,7 +1218,6 @@
*/
void setPolicyDataEnabled(boolean enabled, int subId);
-
/**
* Get Client request stats which will contain statistical information
* on each request made by client.
@@ -1227,4 +1226,24 @@
* @hide
*/
List<ClientRequestStats> getClientRequestStats(String callingPackage, int subid);
+
+ /**
+ * Set SIM card power state. Request is equivalent to inserting or removing the card.
+ * @param slotId SIM slot id
+ * @param powerUp True if powering up the SIM, otherwise powering down
+ * @hide
+ * */
+ void setSimPowerStateForSlot(int slotId, boolean powerUp);
+
+ /**
+ * Returns a list of Forbidden PLMNs from the specified SIM App
+ * Returns null if the query fails.
+ *
+ *
+ * <p>Requires that the calling app has READ_PRIVILEGED_PHONE_STATE
+ *
+ * @param subId subscription ID used for authentication
+ * @param appType the icc application type, like {@link #APPTYPE_USIM}
+ */
+ String[] getForbiddenPlmns(int subId, int appType);
}
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index b770b19..1e1d7a6 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -411,6 +411,9 @@
int RIL_REQUEST_GET_ACTIVITY_INFO = 135;
int RIL_REQUEST_SET_ALLOWED_CARRIERS = 136;
int RIL_REQUEST_GET_ALLOWED_CARRIERS = 137;
+ int RIL_REQUEST_SEND_DEVICE_STATE = 138;
+ int RIL_REQUEST_SET_UNSOLICITED_RESPONSE_FILTER = 139;
+ int RIL_REQUEST_SET_SIM_CARD_POWER = 140;
int RIL_RESPONSE_ACKNOWLEDGEMENT = 800;
diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
index 0168874..682b672 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
@@ -264,15 +264,6 @@
= "android.intent.action.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS";
/**
- * Activity Action: Start this activity to invoke the carrier setup app.
- * The carrier app must be signed using a certificate that matches the UICC access rules.
- *
- * <p class="note">Callers of this should hold the android.permission.INVOKE_CARRIER_SETUP
- * permission.</p>
- */
- public static final String ACTION_CARRIER_SETUP = "android.intent.action.ACTION_CARRIER_SETUP";
-
- /**
* <p>Broadcast Action: Indicates that the action is forbidden by network.
* <p class="note">
* This is for the OEM applications to understand about possible provisioning issues.
@@ -411,7 +402,7 @@
* <p class="note">This is a protected intent that can only be sent by the system.</p>
*/
public static final String ACTION_CARRIER_SIGNAL_REDIRECTED =
- "android.intent.action.CARRIER_SIGNAL_REDIRECTED";
+ "com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED";
/**
* <p>Broadcast Action: when data connections setup fails.
* intended for sim/account status checks and only sent to the specified carrier app
@@ -424,7 +415,7 @@
* <p class="note">This is a protected intent that can only be sent by the system. </p>
*/
public static final String ACTION_CARRIER_SIGNAL_REQUEST_NETWORK_FAILED =
- "android.intent.action.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED";
+ "com.android.internal.telephony.CARRIER_SIGNAL_REQUEST_NETWORK_FAILED";
/**
* <p>Broadcast Action: when pco value is available.
@@ -441,7 +432,7 @@
* <p class="note">This is a protected intent that can only be sent by the system. </p>
*/
public static final String ACTION_CARRIER_SIGNAL_PCO_VALUE =
- "android.intent.action.CARRIER_SIGNAL_PCO_VALUE";
+ "com.android.internal.telephony.CARRIER_SIGNAL_PCO_VALUE";
// CARRIER_SIGNAL_ACTION extra keys
public static final String EXTRA_REDIRECTION_URL_KEY = "redirectionUrl";
diff --git a/test-runner/src/android/test/suitebuilder/TestMethod.java b/test-runner/src/android/test/suitebuilder/TestMethod.java
index 08568d5..ae1db5e 100644
--- a/test-runner/src/android/test/suitebuilder/TestMethod.java
+++ b/test-runner/src/android/test/suitebuilder/TestMethod.java
@@ -26,7 +26,11 @@
/**
* Represents a test to be run. Can be constructed without instantiating the TestCase or even
* loading the class.
+ *
+ * @deprecated New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
*/
+@Deprecated
public class TestMethod {
private final String enclosingClassname;
diff --git a/test-runner/src/android/test/suitebuilder/TestSuiteBuilder.java b/test-runner/src/android/test/suitebuilder/TestSuiteBuilder.java
index 8c89489..3b920cf 100644
--- a/test-runner/src/android/test/suitebuilder/TestSuiteBuilder.java
+++ b/test-runner/src/android/test/suitebuilder/TestSuiteBuilder.java
@@ -38,7 +38,11 @@
/**
* Build suites based on a combination of included packages, excluded packages,
* and predicates that must be satisfied.
+ *
+ * @deprecated New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
*/
+@Deprecated
public class TestSuiteBuilder {
private Context context;
@@ -223,7 +227,11 @@
/**
* A special {@link junit.framework.TestCase} used to indicate a failure during the build()
* step.
+ *
+ * @deprecated New tests should be written using the
+ * <a href="{@docRoot}tools/testing-support-library/index.html">Android Testing Support Library</a>.
*/
+ @Deprecated
public static class FailedToCreateTests extends TestCase {
private final Exception exception;
diff --git a/test-runner/src/junit/runner/package-info.java b/test-runner/src/junit/runner/package-info.java
index b746185..364e362 100644
--- a/test-runner/src/junit/runner/package-info.java
+++ b/test-runner/src/junit/runner/package-info.java
@@ -1,4 +1,4 @@
/**
- * Provides JUnit v3.x test runners.
+ * Utility classes supporting the junit test framework.
*/
package junit.runner;
\ No newline at end of file
diff --git a/test-runner/src/junit/runner/package.html b/test-runner/src/junit/runner/package.html
deleted file mode 100644
index f08fa70..0000000
--- a/test-runner/src/junit/runner/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<HTML>
-<BODY>
-Utility classes supporting the junit test framework.
-</BODY>
-</HTML>
diff --git a/test-runner/src/junit/textui/package-info.java b/test-runner/src/junit/textui/package-info.java
index 2dcc10c..28b2ef4 100644
--- a/test-runner/src/junit/textui/package-info.java
+++ b/test-runner/src/junit/textui/package-info.java
@@ -1,5 +1,5 @@
/**
- * Provides JUnit v3.x command line based tool to run tests.
+ * Utility classes supporting the junit test framework.
* {@hide}
*/
package junit.textui;
\ No newline at end of file
diff --git a/test-runner/src/junit/textui/package.html b/test-runner/src/junit/textui/package.html
deleted file mode 100644
index 723f2ae..0000000
--- a/test-runner/src/junit/textui/package.html
+++ /dev/null
@@ -1,6 +0,0 @@
-<HTML>
-<BODY>
-Utility classes supporting the junit test framework.
-{@hide} - Not needed for 1.0 SDK
-</BODY>
-</HTML>
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ListActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ListActivity.java
index db6421e..0defe92 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ListActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ListActivity.java
@@ -42,7 +42,7 @@
"Burkina Faso", "Burundi", "Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde",
"Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
"Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
- "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic",
+ "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czechia",
"Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic",
"East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea",
"Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland",
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/TransparentListActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/TransparentListActivity.java
index 535f865..ffb8689 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/TransparentListActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/TransparentListActivity.java
@@ -42,7 +42,7 @@
"Burkina Faso", "Burundi", "Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde",
"Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
"Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
- "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic",
+ "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czechia",
"Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic",
"East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea",
"Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland",
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity.java
index 0ddd7fd..7168478 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity.java
@@ -126,7 +126,7 @@
"Burkina Faso", "Burundi", "Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde",
"Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
"Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
- "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic",
+ "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czechia",
"Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic",
"East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea",
"Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland",
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity2.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity2.java
index e795f02..a037d70 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity2.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity2.java
@@ -76,7 +76,7 @@
"Burkina Faso", "Burundi", "Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde",
"Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
"Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
- "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic",
+ "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czechia",
"Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic",
"East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea",
"Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland",
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity3.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity3.java
index c8ae75b..e65dd63 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity3.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity3.java
@@ -73,7 +73,7 @@
"Burkina Faso", "Burundi", "Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde",
"Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
"Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
- "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic",
+ "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czechia",
"Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic",
"East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea",
"Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland",
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity4.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity4.java
index 6072c6e..17f78af 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity4.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity4.java
@@ -80,7 +80,7 @@
"Burkina Faso", "Burundi", "Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde",
"Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
"Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
- "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic",
+ "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czechia",
"Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic",
"East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea",
"Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland",
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity5.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity5.java
index cbbb7ef..2dd7b6a 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity5.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ViewLayersActivity5.java
@@ -145,7 +145,7 @@
"Burkina Faso", "Burundi", "Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde",
"Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
"Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
- "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic",
+ "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czechia",
"Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic",
"East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea",
"Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland",
diff --git a/tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityLandscape.java b/tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityLandscape.java
index b659135..5ab874f 100644
--- a/tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityLandscape.java
+++ b/tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityLandscape.java
@@ -66,7 +66,7 @@
"Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde",
"Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
"Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
- "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic",
+ "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czechia",
"Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic",
"East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea",
"Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland",
diff --git a/tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityPortrait.java b/tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityPortrait.java
index 5bfe456..570cb6b 100644
--- a/tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityPortrait.java
+++ b/tests/ImfTest/src/com/android/imftest/samples/AutoCompleteTextViewActivityPortrait.java
@@ -60,7 +60,7 @@
"Cote d'Ivoire", "Cambodia", "Cameroon", "Canada", "Cape Verde",
"Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
"Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
- "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czech Republic",
+ "Cook Islands", "Costa Rica", "Croatia", "Cuba", "Cyprus", "Czechia",
"Democratic Republic of the Congo", "Denmark", "Djibouti", "Dominica", "Dominican Republic",
"East Timor", "Ecuador", "Egypt", "El Salvador", "Equatorial Guinea", "Eritrea",
"Estonia", "Ethiopia", "Faeroe Islands", "Falkland Islands", "Fiji", "Finland",
diff --git a/tests/TtsTests/Android.mk b/tests/TtsTests/Android.mk
index ed63e12..3c3cd77 100644
--- a/tests/TtsTests/Android.mk
+++ b/tests/TtsTests/Android.mk
@@ -20,7 +20,7 @@
LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_STATIC_JAVA_LIBRARIES := littlemock junit legacy-android-test
+LOCAL_STATIC_JAVA_LIBRARIES := mockito-target legacy-android-test
LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_PACKAGE_NAME := TtsTests
diff --git a/tests/TtsTests/src/com/android/speech/tts/TextToSpeechTests.java b/tests/TtsTests/src/com/android/speech/tts/TextToSpeechTests.java
index faf6827..918873b 100644
--- a/tests/TtsTests/src/com/android/speech/tts/TextToSpeechTests.java
+++ b/tests/TtsTests/src/com/android/speech/tts/TextToSpeechTests.java
@@ -22,9 +22,12 @@
import android.test.InstrumentationTestCase;
import com.android.speech.tts.MockableTextToSpeechService.IDelegate;
-import com.google.testing.littlemock.ArgumentCaptor;
-import com.google.testing.littlemock.Behaviour;
-import com.google.testing.littlemock.LittleMock;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+import org.mockito.internal.stubbing.StubberImpl;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+import org.mockito.stubbing.Stubber;
import junit.framework.Assert;
import java.util.Locale;
@@ -40,16 +43,16 @@
@Override
public void setUp() throws Exception {
- IDelegate passThrough = LittleMock.mock(IDelegate.class);
+ IDelegate passThrough = Mockito.mock(IDelegate.class);
MockableTextToSpeechService.setMocker(passThrough);
// For the default voice selection
- LittleMock.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).when(passThrough)
+ Mockito.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).when(passThrough)
.onIsLanguageAvailable(
- LittleMock.anyString(), LittleMock.anyString(), LittleMock.anyString());
- LittleMock.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).when(passThrough)
+ Mockito.anyString(), Mockito.anyString(), Mockito.anyString());
+ Mockito.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).when(passThrough)
.onLoadLanguage(
- LittleMock.anyString(), LittleMock.anyString(), LittleMock.anyString());
+ Mockito.anyString(), Mockito.anyString(), Mockito.anyString());
blockingInitAndVerify(MOCK_ENGINE, TextToSpeech.SUCCESS);
assertEquals(MOCK_ENGINE, mTts.getCurrentEngine());
@@ -71,42 +74,42 @@
}
public void testSetLanguage_delegation() {
- IDelegate delegate = LittleMock.mock(IDelegate.class);
+ IDelegate delegate = Mockito.mock(IDelegate.class);
MockableTextToSpeechService.setMocker(delegate);
- LittleMock.doReturn(TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE).when(delegate).onIsLanguageAvailable(
+ Mockito.doReturn(TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE).when(delegate).onIsLanguageAvailable(
"eng", "USA", "variant");
- LittleMock.doReturn(TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE).when(delegate).onLoadLanguage(
+ Mockito.doReturn(TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE).when(delegate).onLoadLanguage(
"eng", "USA", "variant");
// Test 1 :Tests that calls to onLoadLanguage( ) are delegated through to the
// service without any caching or intermediate steps.
assertEquals(TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE, mTts.setLanguage(new Locale("eng", "USA", "variant")));
- LittleMock.verify(delegate, LittleMock.anyTimes()).onIsLanguageAvailable(
+ Mockito.verify(delegate, Mockito.atLeast(0)).onIsLanguageAvailable(
"eng", "USA", "variant");
- LittleMock.verify(delegate, LittleMock.anyTimes()).onLoadLanguage(
+ Mockito.verify(delegate, Mockito.atLeast(0)).onLoadLanguage(
"eng", "USA", "variant");
}
public void testSetLanguage_availableLanguage() throws Exception {
- IDelegate delegate = LittleMock.mock(IDelegate.class);
+ IDelegate delegate = Mockito.mock(IDelegate.class);
MockableTextToSpeechService.setMocker(delegate);
// ---------------------------------------------------------
// Test 2 : Tests that when the language is successfully set
// like above (returns LANG_COUNTRY_AVAILABLE). That the
// request language changes from that point on.
- LittleMock.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).when(delegate).onIsLanguageAvailable(
+ Mockito.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).when(delegate).onIsLanguageAvailable(
"eng", "USA", "variant");
- LittleMock.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).when(delegate).onIsLanguageAvailable(
+ Mockito.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).when(delegate).onIsLanguageAvailable(
"eng", "USA", "");
- LittleMock.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).when(delegate).onLoadLanguage(
+ Mockito.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).when(delegate).onLoadLanguage(
"eng", "USA", "");
mTts.setLanguage(new Locale("eng", "USA", "variant"));
blockingCallSpeak("foo bar", delegate);
- ArgumentCaptor<SynthesisRequest> req = LittleMock.createCaptor();
- LittleMock.verify(delegate, LittleMock.times(1)).onSynthesizeText(req.capture(),
- LittleMock.<SynthesisCallback>anyObject());
+ ArgumentCaptor<SynthesisRequest> req = ArgumentCaptor.forClass(SynthesisRequest.class);
+ Mockito.verify(delegate, Mockito.times(1)).onSynthesizeText(req.capture(),
+ Mockito.<SynthesisCallback>anyObject());
assertEquals("eng", req.getValue().getLanguage());
assertEquals("USA", req.getValue().getCountry());
@@ -115,21 +118,21 @@
}
public void testSetLanguage_unavailableLanguage() throws Exception {
- IDelegate delegate = LittleMock.mock(IDelegate.class);
+ IDelegate delegate = Mockito.mock(IDelegate.class);
MockableTextToSpeechService.setMocker(delegate);
// ---------------------------------------------------------
// TEST 3 : Tests that the language that is set does not change when the
// engine reports it could not load the specified language.
- LittleMock.doReturn(TextToSpeech.LANG_NOT_SUPPORTED).when(
+ Mockito.doReturn(TextToSpeech.LANG_NOT_SUPPORTED).when(
delegate).onIsLanguageAvailable("fra", "FRA", "");
- LittleMock.doReturn(TextToSpeech.LANG_NOT_SUPPORTED).when(
+ Mockito.doReturn(TextToSpeech.LANG_NOT_SUPPORTED).when(
delegate).onLoadLanguage("fra", "FRA", "");
mTts.setLanguage(Locale.FRANCE);
blockingCallSpeak("le fou barre", delegate);
- ArgumentCaptor<SynthesisRequest> req2 = LittleMock.createCaptor();
- LittleMock.verify(delegate, LittleMock.times(1)).onSynthesizeText(req2.capture(),
- LittleMock.<SynthesisCallback>anyObject());
+ ArgumentCaptor<SynthesisRequest> req2 = ArgumentCaptor.forClass(SynthesisRequest.class);
+ Mockito.verify(delegate, Mockito.times(1)).onSynthesizeText(req2.capture(),
+ Mockito.<SynthesisCallback>anyObject());
// The params are basically unchanged.
assertEquals("eng", req2.getValue().getLanguage());
@@ -139,41 +142,41 @@
}
public void testIsLanguageAvailable() {
- IDelegate delegate = LittleMock.mock(IDelegate.class);
+ IDelegate delegate = Mockito.mock(IDelegate.class);
MockableTextToSpeechService.setMocker(delegate);
// Test1: Simple end to end test.
- LittleMock.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).when(
+ Mockito.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).when(
delegate).onIsLanguageAvailable("eng", "USA", "");
assertEquals(TextToSpeech.LANG_COUNTRY_AVAILABLE, mTts.isLanguageAvailable(Locale.US));
- LittleMock.verify(delegate, LittleMock.times(1)).onIsLanguageAvailable(
+ Mockito.verify(delegate, Mockito.times(1)).onIsLanguageAvailable(
"eng", "USA", "");
}
public void testDefaultLanguage_setsVoiceName() throws Exception {
- IDelegate delegate = LittleMock.mock(IDelegate.class);
+ IDelegate delegate = Mockito.mock(IDelegate.class);
MockableTextToSpeechService.setMocker(delegate);
Locale defaultLocale = Locale.getDefault();
// ---------------------------------------------------------
// Test that default language also sets the default voice
// name
- LittleMock.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).
+ Mockito.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).
when(delegate).onIsLanguageAvailable(
defaultLocale.getISO3Language(),
defaultLocale.getISO3Country().toUpperCase(),
defaultLocale.getVariant());
- LittleMock.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).
+ Mockito.doReturn(TextToSpeech.LANG_COUNTRY_AVAILABLE).
when(delegate).onLoadLanguage(
defaultLocale.getISO3Language(),
defaultLocale.getISO3Country(),
defaultLocale.getVariant());
blockingCallSpeak("foo bar", delegate);
- ArgumentCaptor<SynthesisRequest> req = LittleMock.createCaptor();
- LittleMock.verify(delegate, LittleMock.times(1)).onSynthesizeText(req.capture(),
- LittleMock.<SynthesisCallback>anyObject());
+ ArgumentCaptor<SynthesisRequest> req = ArgumentCaptor.forClass(SynthesisRequest.class);
+ Mockito.verify(delegate, Mockito.times(1)).onSynthesizeText(req.capture(),
+ Mockito.<SynthesisCallback>anyObject());
assertEquals(defaultLocale.getISO3Language(), req.getValue().getLanguage());
assertEquals(defaultLocale.getISO3Country(), req.getValue().getCountry());
@@ -185,8 +188,8 @@
private void blockingCallSpeak(String speech, IDelegate mock) throws
InterruptedException {
final CountDownLatch latch = new CountDownLatch(1);
- doCountDown(latch).when(mock).onSynthesizeText(LittleMock.<SynthesisRequest>anyObject(),
- LittleMock.<SynthesisCallback>anyObject());
+ doCountDown(latch).when(mock).onSynthesizeText(Mockito.<SynthesisRequest>anyObject(),
+ Mockito.<SynthesisCallback>anyObject());
mTts.speak(speech, TextToSpeech.QUEUE_ADD, null);
awaitCountDown(latch, 5, TimeUnit.SECONDS);
@@ -194,7 +197,7 @@
private void blockingInitAndVerify(final String engine, int errorCode) throws
InterruptedException {
- TextToSpeech.OnInitListener listener = LittleMock.mock(
+ TextToSpeech.OnInitListener listener = Mockito.mock(
TextToSpeech.OnInitListener.class);
final CountDownLatch latch = new CountDownLatch(1);
@@ -206,18 +209,18 @@
awaitCountDown(latch, 5, TimeUnit.SECONDS);
}
- public interface CountDownBehaviour extends Behaviour {
+ public static abstract class CountDownBehaviour extends StubberImpl {
/** Used to mock methods that return a result. */
- Behaviour andReturn(Object result);
+ public abstract Stubber andReturn(Object result);
}
public static CountDownBehaviour doCountDown(final CountDownLatch latch) {
return new CountDownBehaviour() {
@Override
public <T> T when(T mock) {
- return LittleMock.doAnswer(new Callable<Void>() {
+ return Mockito.doAnswer(new Answer<Void>() {
@Override
- public Void call() throws Exception {
+ public Void answer(InvocationOnMock invocation) throws Exception {
latch.countDown();
return null;
}
@@ -225,13 +228,13 @@
}
@Override
- public Behaviour andReturn(final Object result) {
- return new Behaviour() {
+ public Stubber andReturn(final Object result) {
+ return new StubberImpl() {
@Override
public <T> T when(T mock) {
- return LittleMock.doAnswer(new Callable<Object>() {
+ return Mockito.doAnswer(new Answer<Object>() {
@Override
- public Object call() throws Exception {
+ public Object answer(InvocationOnMock invocation) throws Exception {
latch.countDown();
return result;
}
diff --git a/tests/net/java/android/net/ConnectivityMetricsLoggerTest.java b/tests/net/java/android/net/ConnectivityMetricsLoggerTest.java
deleted file mode 100644
index f896030..0000000
--- a/tests/net/java/android/net/ConnectivityMetricsLoggerTest.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2016, 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.Bundle;
-import android.os.Parcel;
-import android.test.suitebuilder.annotation.SmallTest;
-import java.util.List;
-import junit.framework.TestCase;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import static org.mockito.Mockito.any;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-public class ConnectivityMetricsLoggerTest extends TestCase {
-
- // use same Parcel object everywhere for pointer equality
- static final Bundle FAKE_EV = new Bundle();
- static final int FAKE_COMPONENT = 1;
- static final int FAKE_EVENT = 2;
-
- @Mock IConnectivityMetricsLogger mService;
- ArgumentCaptor<ConnectivityMetricsEvent> evCaptor;
- ArgumentCaptor<ConnectivityMetricsEvent[]> evArrayCaptor;
-
- ConnectivityMetricsLogger mLog;
-
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- evCaptor = ArgumentCaptor.forClass(ConnectivityMetricsEvent.class);
- evArrayCaptor = ArgumentCaptor.forClass(ConnectivityMetricsEvent[].class);
- mLog = new ConnectivityMetricsLogger(mService);
- }
-
- @SmallTest
- public void testLogEvents() throws Exception {
- mLog.logEvent(1, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV);
- mLog.logEvent(2, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV);
- mLog.logEvent(3, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV);
-
- List<ConnectivityMetricsEvent> gotEvents = verifyEvents(3);
- assertEventsEqual(expectedEvent(1), gotEvents.get(0));
- assertEventsEqual(expectedEvent(2), gotEvents.get(1));
- assertEventsEqual(expectedEvent(3), gotEvents.get(2));
- }
-
- @SmallTest
- public void testLogEventTriggerThrottling() throws Exception {
- when(mService.logEvent(any())).thenReturn(1234L);
-
- mLog.logEvent(1, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV);
- mLog.logEvent(2, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV);
-
- List<ConnectivityMetricsEvent> gotEvents = verifyEvents(1);
- assertEventsEqual(expectedEvent(1), gotEvents.get(0));
- }
-
- @SmallTest
- public void testLogEventFails() throws Exception {
- when(mService.logEvent(any())).thenReturn(-1L); // Error.
-
- mLog.logEvent(1, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV);
- mLog.logEvent(2, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV);
-
- List<ConnectivityMetricsEvent> gotEvents = verifyEvents(1);
- assertEventsEqual(expectedEvent(1), gotEvents.get(0));
- }
-
- @SmallTest
- public void testLogEventWhenThrottling() throws Exception {
- when(mService.logEvent(any())).thenReturn(Long.MAX_VALUE); // Throttled
-
- // No events are logged. The service is only called once
- // After that, throttling state is maintained locally.
- mLog.logEvent(1, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV);
- mLog.logEvent(2, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV);
-
- List<ConnectivityMetricsEvent> gotEvents = verifyEvents(1);
- assertEventsEqual(expectedEvent(1), gotEvents.get(0));
- }
-
- @SmallTest
- public void testLogEventRecoverFromThrottling() throws Exception {
- final long throttleTimeout = System.currentTimeMillis() + 10;
- when(mService.logEvent(any())).thenReturn(throttleTimeout, 0L);
-
- mLog.logEvent(1, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV);
- mLog.logEvent(2, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV);
- mLog.logEvent(3, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV);
- Thread.sleep(100);
- mLog.logEvent(53, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV);
-
- List<ConnectivityMetricsEvent> gotEvents = verifyEvents(1);
- assertEventsEqual(expectedEvent(1), gotEvents.get(0));
-
- verify(mService, times(1)).logEvents(evArrayCaptor.capture());
- ConnectivityMetricsEvent[] gotOtherEvents = evArrayCaptor.getAllValues().get(0);
- assertEquals(ConnectivityMetricsLogger.TAG_SKIPPED_EVENTS, gotOtherEvents[0].eventTag);
- assertEventsEqual(expectedEvent(53), gotOtherEvents[1]);
- }
-
- List<ConnectivityMetricsEvent> verifyEvents(int n) throws Exception {
- verify(mService, times(n)).logEvent(evCaptor.capture());
- return evCaptor.getAllValues();
- }
-
- static ConnectivityMetricsEvent expectedEvent(int timestamp) {
- return new ConnectivityMetricsEvent((long)timestamp, FAKE_COMPONENT, FAKE_EVENT, FAKE_EV);
- }
-
- /** Outer equality for ConnectivityMetricsEvent to avoid overriding equals() and hashCode(). */
- static void assertEventsEqual(ConnectivityMetricsEvent expected, ConnectivityMetricsEvent got) {
- assertEquals(expected.timestamp, got.timestamp);
- assertEquals(expected.componentTag, got.componentTag);
- assertEquals(expected.eventTag, got.eventTag);
- assertEquals(expected.data, got.data);
- }
-}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 39406a11..b033382 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -369,6 +369,11 @@
connect(false);
}
+ public void suspend() {
+ mNetworkInfo.setDetailedState(DetailedState.SUSPENDED, null, null);
+ mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+ }
+
public void disconnect() {
mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
mNetworkAgent.sendNetworkInfo(mNetworkInfo);
@@ -1048,6 +1053,7 @@
AVAILABLE,
NETWORK_CAPABILITIES,
LINK_PROPERTIES,
+ SUSPENDED,
LOSING,
LOST,
UNAVAILABLE
@@ -1061,7 +1067,7 @@
state = s; network = n; arg = o;
}
public String toString() {
- return String.format("%s (%s)", state, network);
+ return String.format("%s (%s) (%s)", state, network, arg);
}
@Override
public boolean equals(Object o) {
@@ -1099,11 +1105,26 @@
}
@Override
+ public void onCapabilitiesChanged(Network network, NetworkCapabilities netCap) {
+ setLastCallback(CallbackState.NETWORK_CAPABILITIES, network, netCap);
+ }
+
+ @Override
+ public void onLinkPropertiesChanged(Network network, LinkProperties linkProp) {
+ setLastCallback(CallbackState.LINK_PROPERTIES, network, linkProp);
+ }
+
+ @Override
public void onUnavailable() {
setLastCallback(CallbackState.UNAVAILABLE, null, null);
}
@Override
+ public void onNetworkSuspended(Network network) {
+ setLastCallback(CallbackState.SUSPENDED, network, null);
+ }
+
+ @Override
public void onLosing(Network network, int maxMsToLive) {
setLastCallback(CallbackState.LOSING, network, maxMsToLive /* autoboxed int */);
}
@@ -1126,11 +1147,12 @@
return cb;
}
- void expectCallback(CallbackState state, MockNetworkAgent mockAgent, int timeoutMs) {
- CallbackInfo expected = new CallbackInfo(
- state, (mockAgent != null) ? mockAgent.getNetwork() : null, 0);
+ CallbackInfo expectCallback(CallbackState state, MockNetworkAgent agent, int timeoutMs) {
+ final Network expectedNetwork = (agent != null) ? agent.getNetwork() : null;
+ CallbackInfo expected = new CallbackInfo(state, expectedNetwork, 0);
CallbackInfo actual = nextCallback(timeoutMs);
assertEquals("Unexpected callback:", expected, actual);
+
if (state == CallbackState.LOSING) {
String msg = String.format(
"Invalid linger time value %d, must be between %d and %d",
@@ -1138,10 +1160,46 @@
int maxMsToLive = (Integer) actual.arg;
assertTrue(msg, 0 <= maxMsToLive && maxMsToLive <= TEST_LINGER_DELAY_MS);
}
+
+ return actual;
}
- void expectCallback(CallbackState state, MockNetworkAgent mockAgent) {
- expectCallback(state, mockAgent, TIMEOUT_MS);
+ CallbackInfo expectCallback(CallbackState state, MockNetworkAgent agent) {
+ return expectCallback(state, agent, TIMEOUT_MS);
+ }
+
+ void expectAvailableCallbacks(MockNetworkAgent agent, boolean expectSuspended, int timeoutMs) {
+ expectCallback(CallbackState.AVAILABLE, agent, timeoutMs);
+ if (expectSuspended) {
+ expectCallback(CallbackState.SUSPENDED, agent, timeoutMs);
+ }
+ expectCallback(CallbackState.NETWORK_CAPABILITIES, agent, timeoutMs);
+ expectCallback(CallbackState.LINK_PROPERTIES, agent, timeoutMs);
+ }
+
+ void expectAvailableCallbacks(MockNetworkAgent agent) {
+ expectAvailableCallbacks(agent, false, TIMEOUT_MS);
+ }
+
+ void expectAvailableAndSuspendedCallbacks(MockNetworkAgent agent) {
+ expectAvailableCallbacks(agent, true, TIMEOUT_MS);
+ }
+
+ void expectAvailableAndValidatedCallbacks(MockNetworkAgent agent) {
+ expectAvailableCallbacks(agent, false, TIMEOUT_MS);
+ expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, agent);
+ }
+
+ void expectCapabilitiesWith(int capability, MockNetworkAgent agent) {
+ CallbackInfo cbi = expectCallback(CallbackState.NETWORK_CAPABILITIES, agent);
+ NetworkCapabilities nc = (NetworkCapabilities) cbi.arg;
+ assertTrue(nc.hasCapability(capability));
+ }
+
+ void expectCapabilitiesWithout(int capability, MockNetworkAgent agent) {
+ CallbackInfo cbi = expectCallback(CallbackState.NETWORK_CAPABILITIES, agent);
+ NetworkCapabilities nc = (NetworkCapabilities) cbi.arg;
+ assertFalse(nc.hasCapability(capability));
}
void assertNoCallback() {
@@ -1178,8 +1236,8 @@
ConditionVariable cv = waitForConnectivityBroadcasts(1);
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(false);
- genericNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
- cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ genericNetworkCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ cellNetworkCallback.expectAvailableCallbacks(mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
waitFor(cv);
assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
@@ -1193,8 +1251,8 @@
cv = waitForConnectivityBroadcasts(2);
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
- genericNetworkCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
- wifiNetworkCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ genericNetworkCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ wifiNetworkCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
waitFor(cv);
assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
@@ -1217,8 +1275,8 @@
// Test validated networks
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
- genericNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
- cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ genericNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+ cellNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
@@ -1230,9 +1288,10 @@
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
- genericNetworkCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ genericNetworkCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
genericNetworkCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
- wifiNetworkCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ genericNetworkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+ wifiNetworkCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
cellNetworkCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
assertNoCallbacks(genericNetworkCallback, wifiNetworkCallback, cellNetworkCallback);
@@ -1268,28 +1327,32 @@
mEthernetNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
mCellNetworkAgent.connect(true);
- callback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
- defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ callback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+ defaultCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
mWiFiNetworkAgent.connect(true);
// We get AVAILABLE on wifi when wifi connects and satisfies our unmetered request.
// We then get LOSING when wifi validates and cell is outscored.
- callback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ callback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ // TODO: Investigate sending validated before losing.
callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
- defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+ defaultCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
mEthernetNetworkAgent.connect(true);
- callback.expectCallback(CallbackState.AVAILABLE, mEthernetNetworkAgent);
+ callback.expectAvailableCallbacks(mEthernetNetworkAgent);
+ // TODO: Investigate sending validated before losing.
callback.expectCallback(CallbackState.LOSING, mWiFiNetworkAgent);
- defaultCallback.expectCallback(CallbackState.AVAILABLE, mEthernetNetworkAgent);
+ callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mEthernetNetworkAgent);
+ defaultCallback.expectAvailableAndValidatedCallbacks(mEthernetNetworkAgent);
assertEquals(mEthernetNetworkAgent.getNetwork(), mCm.getActiveNetwork());
mEthernetNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, mEthernetNetworkAgent);
defaultCallback.expectCallback(CallbackState.LOST, mEthernetNetworkAgent);
- defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ defaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
for (int i = 0; i < 4; i++) {
MockNetworkAgent oldNetwork, newNetwork;
@@ -1306,7 +1369,7 @@
callback.expectCallback(CallbackState.LOSING, oldNetwork);
// TODO: should we send an AVAILABLE callback to newNetwork, to indicate that it is no
// longer lingering?
- defaultCallback.expectCallback(CallbackState.AVAILABLE, newNetwork);
+ defaultCallback.expectAvailableCallbacks(newNetwork);
assertEquals(newNetwork.getNetwork(), mCm.getActiveNetwork());
}
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
@@ -1314,17 +1377,19 @@
// Verify that if a network no longer satisfies a request, we send LOST and not LOSING, even
// if the network is still up.
mWiFiNetworkAgent.removeCapability(NET_CAPABILITY_NOT_METERED);
+ // We expect a notification about the capabilities change, and nothing else.
+ defaultCallback.expectCapabilitiesWithout(NET_CAPABILITY_NOT_METERED, mWiFiNetworkAgent);
+ defaultCallback.assertNoCallback();
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
// Wifi no longer satisfies our listen, which is for an unmetered network.
// But because its score is 55, it's still up (and the default network).
- defaultCallback.assertNoCallback();
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
// Disconnect our test networks.
mWiFiNetworkAgent.disconnect();
defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
mCellNetworkAgent.disconnect();
defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
@@ -1340,22 +1405,22 @@
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(false); // Score: 10
- callback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
- defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ callback.expectAvailableCallbacks(mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
// Bring up wifi with a score of 20.
// Cell stays up because it would satisfy the default request if it validated.
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false); // Score: 20
- callback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
- defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ callback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ defaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
mWiFiNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
// Bring up wifi with a score of 70.
@@ -1363,31 +1428,33 @@
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.adjustScore(50);
mWiFiNetworkAgent.connect(false); // Score: 70
- callback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ callback.expectAvailableCallbacks(mWiFiNetworkAgent);
callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
- defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ defaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
// Tear down wifi.
mWiFiNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
assertEquals(mCellNetworkAgent.getNetwork(), mCm.getActiveNetwork());
// Bring up wifi, then validate it. Previous versions would immediately tear down cell, but
// it's arguably correct to linger it, since it was the default network before it validated.
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
- callback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ callback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ // TODO: Investigate sending validated before losing.
callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
- defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+ defaultCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
mWiFiNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
mCellNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
@@ -1395,13 +1462,15 @@
// If a network is lingering, and we add and remove a request from it, resume lingering.
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
- callback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
- defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ callback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+ defaultCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
- callback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
- defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ defaultCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
+ callback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ // TODO: Investigate sending validated before losing.
callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+ callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
NetworkRequest cellRequest = new NetworkRequest.Builder()
.addTransportType(TRANSPORT_CELLULAR).build();
@@ -1417,7 +1486,7 @@
mWiFiNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
defaultCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
// Cell is now the default network. Pin it with a cell-specific request.
noopCallback = new NetworkCallback(); // Can't reuse NetworkCallbacks. http://b/20701525
@@ -1426,8 +1495,8 @@
// Now connect wifi, and expect it to become the default network.
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
- callback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
- defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ callback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
+ defaultCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
// The default request is lingering on cell, but nothing happens to cell, and we send no
// callbacks for it, because it's kept up by cellRequest.
callback.assertNoCallback();
@@ -1613,7 +1682,7 @@
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.addCapability(NET_CAPABILITY_MMS);
mCellNetworkAgent.connectWithoutInternet();
- networkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ networkCallback.expectAvailableCallbacks(mCellNetworkAgent);
verifyActiveNetwork(TRANSPORT_WIFI);
// Test releasing NetworkRequest disconnects cellular with MMS
cv = mCellNetworkAgent.getDisconnectedCV();
@@ -1639,7 +1708,7 @@
MockNetworkAgent mmsNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mmsNetworkAgent.addCapability(NET_CAPABILITY_MMS);
mmsNetworkAgent.connectWithoutInternet();
- networkCallback.expectCallback(CallbackState.AVAILABLE, mmsNetworkAgent);
+ networkCallback.expectAvailableCallbacks(mmsNetworkAgent);
verifyActiveNetwork(TRANSPORT_CELLULAR);
// Test releasing MMS NetworkRequest does not disconnect main cellular NetworkAgent
cv = mmsNetworkAgent.getDisconnectedCV();
@@ -1665,7 +1734,7 @@
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
String firstRedirectUrl = "http://example.com/firstPath";
mWiFiNetworkAgent.connectWithCaptivePortal(firstRedirectUrl);
- captivePortalCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ captivePortalCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), firstRedirectUrl);
// Take down network.
@@ -1678,7 +1747,7 @@
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
String secondRedirectUrl = "http://example.com/secondPath";
mWiFiNetworkAgent.connectWithCaptivePortal(secondRedirectUrl);
- captivePortalCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ captivePortalCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
assertEquals(mWiFiNetworkAgent.waitForRedirectUrl(), secondRedirectUrl);
// Make captive portal disappear then revalidate.
@@ -1688,7 +1757,9 @@
captivePortalCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
// Expect NET_CAPABILITY_VALIDATED onAvailable callback.
- validatedCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ validatedCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ // TODO: Investigate only sending available callbacks.
+ validatedCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
// Break network connectivity.
// Expect NET_CAPABILITY_VALIDATED onLost callback.
@@ -1733,7 +1804,7 @@
mWiFiNetworkAgent.connectWithCaptivePortal(secondRedirectUrl);
// Expect NET_CAPABILITY_VALIDATED onAvailable callback.
- validatedCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ validatedCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
// But there should be no CaptivePortal callback.
captivePortalCallback.assertNoCallback();
}
@@ -1786,14 +1857,14 @@
// Bring up cell and expect CALLBACK_AVAILABLE.
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
- cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
- defaultNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ cellNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+ defaultNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
// Bring up wifi and expect CALLBACK_AVAILABLE.
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
cellNetworkCallback.assertNoCallback();
- defaultNetworkCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ defaultNetworkCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
// Bring down cell. Expect no default network callback, since it wasn't the default.
mCellNetworkAgent.disconnect();
@@ -1803,7 +1874,7 @@
// Bring up cell. Expect no default network callback, since it won't be the default.
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
- cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ cellNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
defaultNetworkCallback.assertNoCallback();
// Bring down wifi. Expect the default network callback to notified of LOST wifi
@@ -1811,28 +1882,16 @@
mWiFiNetworkAgent.disconnect();
cellNetworkCallback.assertNoCallback();
defaultNetworkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- defaultNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ defaultNetworkCallback.expectAvailableCallbacks(mCellNetworkAgent);
mCellNetworkAgent.disconnect();
cellNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
defaultNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
}
- private class TestRequestUpdateCallback extends TestNetworkCallback {
- @Override
- public void onCapabilitiesChanged(Network network, NetworkCapabilities netCap) {
- setLastCallback(CallbackState.NETWORK_CAPABILITIES, network, netCap);
- }
-
- @Override
- public void onLinkPropertiesChanged(Network network, LinkProperties linkProp) {
- setLastCallback(CallbackState.LINK_PROPERTIES, network, linkProp);
- }
- }
-
@SmallTest
- public void testRequestCallbackUpdates() throws Exception {
+ public void testAdditionalStateCallbacks() throws Exception {
// File a network request for mobile.
- final TestNetworkCallback cellNetworkCallback = new TestRequestUpdateCallback();
+ final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
final NetworkRequest cellRequest = new NetworkRequest.Builder()
.addTransportType(TRANSPORT_CELLULAR).build();
mCm.requestNetwork(cellRequest, cellNetworkCallback);
@@ -1841,10 +1900,10 @@
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
- // We should get onAvailable().
- cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
- // We should get onCapabilitiesChanged(), when the mobile network successfully validates.
- cellNetworkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES, mCellNetworkAgent);
+ // We should get onAvailable(), onCapabilitiesChanged(), and
+ // onLinkPropertiesChanged() in rapid succession. Additionally, we
+ // should get onCapabilitiesChanged() when the mobile network validates.
+ cellNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
cellNetworkCallback.assertNoCallback();
// Update LinkProperties.
@@ -1855,23 +1914,17 @@
cellNetworkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
cellNetworkCallback.assertNoCallback();
+ // Suspend the network.
+ mCellNetworkAgent.suspend();
+ cellNetworkCallback.expectCallback(CallbackState.SUSPENDED, mCellNetworkAgent);
+ cellNetworkCallback.assertNoCallback();
+
// Register a garden variety default network request.
- final TestNetworkCallback dfltNetworkCallback = new TestRequestUpdateCallback();
+ final TestNetworkCallback dfltNetworkCallback = new TestNetworkCallback();
mCm.registerDefaultNetworkCallback(dfltNetworkCallback);
- // Only onAvailable() is called; no other information is delivered.
- dfltNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
- dfltNetworkCallback.assertNoCallback();
-
- // Request a NetworkCapabilities update; only the requesting callback is notified.
- mCm.requestNetworkCapabilities(dfltNetworkCallback);
- dfltNetworkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES, mCellNetworkAgent);
- cellNetworkCallback.assertNoCallback();
- dfltNetworkCallback.assertNoCallback();
-
- // Request a LinkProperties update; only the requesting callback is notified.
- mCm.requestLinkProperties(dfltNetworkCallback);
- dfltNetworkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
- cellNetworkCallback.assertNoCallback();
+ // We should get onAvailable(), onCapabilitiesChanged(), onLinkPropertiesChanged(),
+ // as well as onNetworkSuspended() in rapid succession.
+ dfltNetworkCallback.expectAvailableAndSuspendedCallbacks(mCellNetworkAgent);
dfltNetworkCallback.assertNoCallback();
mCm.unregisterNetworkCallback(dfltNetworkCallback);
@@ -1911,18 +1964,20 @@
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
- callback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
- fgCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ callback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+ fgCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
assertTrue(isForegroundNetwork(mCellNetworkAgent));
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
// When wifi connects, cell lingers.
- callback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
- fgCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ callback.expectAvailableCallbacks(mWiFiNetworkAgent);
callback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+ callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
+ fgCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
fgCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent);
+ fgCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
assertTrue(isForegroundNetwork(mCellNetworkAgent));
assertTrue(isForegroundNetwork(mWiFiNetworkAgent));
@@ -1930,7 +1985,8 @@
mService.waitForIdle();
int timeoutMs = TEST_LINGER_DELAY_MS + TEST_LINGER_DELAY_MS / 4;
fgCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent, timeoutMs);
- callback.assertNoCallback();
+ // Expect a network capabilities update sans FOREGROUND.
+ callback.expectCapabilitiesWithout(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent);
assertFalse(isForegroundNetwork(mCellNetworkAgent));
assertTrue(isForegroundNetwork(mWiFiNetworkAgent));
@@ -1939,9 +1995,15 @@
.addTransportType(TRANSPORT_CELLULAR).build();
final TestNetworkCallback cellCallback = new TestNetworkCallback();
mCm.requestNetwork(cellRequest, cellCallback);
- cellCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
- fgCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
- callback.assertNoCallback(); // Because the network is already up.
+ // NOTE: This request causes the network's capabilities to change. This
+ // is currently delivered before the onAvailable() callbacks.
+ // TODO: Fix this.
+ cellCallback.expectCapabilitiesWith(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent);
+ cellCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ fgCallback.expectAvailableCallbacks(mCellNetworkAgent);
+ // Expect a network capabilities update with FOREGROUND, because the most recent
+ // request causes its state to change.
+ callback.expectCapabilitiesWith(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent);
assertTrue(isForegroundNetwork(mCellNetworkAgent));
assertTrue(isForegroundNetwork(mWiFiNetworkAgent));
@@ -1949,7 +2011,8 @@
// lingering.
mCm.unregisterNetworkCallback(cellCallback);
fgCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
- callback.assertNoCallback();
+ // Expect a network capabilities update sans FOREGROUND.
+ callback.expectCapabilitiesWithout(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent);
assertFalse(isForegroundNetwork(mCellNetworkAgent));
assertTrue(isForegroundNetwork(mWiFiNetworkAgent));
@@ -1957,7 +2020,7 @@
mWiFiNetworkAgent.disconnect();
callback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
fgCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- fgCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ fgCallback.expectAvailableCallbacks(mCellNetworkAgent);
assertTrue(isForegroundNetwork(mCellNetworkAgent));
mCm.unregisterNetworkCallback(callback);
@@ -2098,7 +2161,7 @@
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
testFactory.expectAddRequests(2); // Because the cell request changes score twice.
mCellNetworkAgent.connect(true);
- cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ cellNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
testFactory.waitForNetworkRequests(2);
assertFalse(testFactory.getMyStartRequested()); // Because the cell network outscores us.
@@ -2189,20 +2252,22 @@
// Bring up validated cell.
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
mCellNetworkAgent.connect(true);
- cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
- defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ cellNetworkCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
+ defaultCallback.expectAvailableAndValidatedCallbacks(mCellNetworkAgent);
Network cellNetwork = mCellNetworkAgent.getNetwork();
// Bring up validated wifi.
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
- defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
- validatedWifiCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ defaultCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
+ validatedWifiCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ validatedWifiCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
Network wifiNetwork = mWiFiNetworkAgent.getNetwork();
// Fail validation on wifi.
mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 599;
mCm.reportNetworkConnectivity(wifiNetwork, false);
+ defaultCallback.expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
validatedWifiCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
// Because avoid bad wifi is off, we don't switch to cellular.
@@ -2217,18 +2282,18 @@
// that we switch back to cell.
tracker.configRestrictsAvoidBadWifi = false;
tracker.reevaluate();
- defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
assertEquals(mCm.getActiveNetwork(), cellNetwork);
// Switch back to a restrictive carrier.
tracker.configRestrictsAvoidBadWifi = true;
tracker.reevaluate();
- defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ defaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
assertEquals(mCm.getActiveNetwork(), wifiNetwork);
// Simulate the user selecting "switch" on the dialog, and check that we switch to cell.
mCm.setAvoidUnvalidated(wifiNetwork);
- defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
assertFalse(mCm.getNetworkCapabilities(wifiNetwork).hasCapability(
NET_CAPABILITY_VALIDATED));
assertTrue(mCm.getNetworkCapabilities(cellNetwork).hasCapability(
@@ -2239,13 +2304,15 @@
mWiFiNetworkAgent.disconnect();
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
- defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
- validatedWifiCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ defaultCallback.expectAvailableAndValidatedCallbacks(mWiFiNetworkAgent);
+ validatedWifiCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
+ validatedWifiCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
wifiNetwork = mWiFiNetworkAgent.getNetwork();
// Fail validation on wifi and expect the dialog to appear.
mWiFiNetworkAgent.getWrappedNetworkMonitor().gen204ProbeResult = 599;
mCm.reportNetworkConnectivity(wifiNetwork, false);
+ defaultCallback.expectCapabilitiesWithout(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
validatedWifiCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
// Simulate the user selecting "switch" and checking the don't ask again checkbox.
@@ -2253,7 +2320,7 @@
tracker.reevaluate();
// We now switch to cell.
- defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
assertFalse(mCm.getNetworkCapabilities(wifiNetwork).hasCapability(
NET_CAPABILITY_VALIDATED));
assertTrue(mCm.getNetworkCapabilities(cellNetwork).hasCapability(
@@ -2264,17 +2331,17 @@
// We switch to wifi and then to cell.
Settings.Global.putString(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, null);
tracker.reevaluate();
- defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ defaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
assertEquals(mCm.getActiveNetwork(), wifiNetwork);
Settings.Global.putInt(cr, Settings.Global.NETWORK_AVOID_BAD_WIFI, 1);
tracker.reevaluate();
- defaultCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ defaultCallback.expectAvailableCallbacks(mCellNetworkAgent);
assertEquals(mCm.getActiveNetwork(), cellNetwork);
// If cell goes down, we switch to wifi.
mCellNetworkAgent.disconnect();
defaultCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
- defaultCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent);
+ defaultCallback.expectAvailableCallbacks(mWiFiNetworkAgent);
validatedWifiCallback.assertNoCallback();
mCm.unregisterNetworkCallback(cellNetworkCallback);
@@ -2296,7 +2363,7 @@
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
- networkCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent, timeoutMs);
+ networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, timeoutMs);
// pass timeout and validate that UNAVAILABLE is not called
networkCallback.assertNoCallback();
@@ -2317,7 +2384,7 @@
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
final int assertTimeoutMs = 150;
- networkCallback.expectCallback(CallbackState.AVAILABLE, mWiFiNetworkAgent, assertTimeoutMs);
+ networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, assertTimeoutMs);
sleepFor(20);
mWiFiNetworkAgent.disconnect();
networkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
diff --git a/tests/net/java/com/android/server/connectivity/MetricsLoggerServiceTest.java b/tests/net/java/com/android/server/connectivity/MetricsLoggerServiceTest.java
deleted file mode 100644
index 5981f48..0000000
--- a/tests/net/java/com/android/server/connectivity/MetricsLoggerServiceTest.java
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * Copyright (C) 2016, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.connectivity;
-
-import android.content.Context;
-import android.net.ConnectivityMetricsEvent;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.test.suitebuilder.annotation.SmallTest;
-import static android.net.ConnectivityMetricsEvent.Reference;
-
-import junit.framework.TestCase;
-import org.junit.Before;
-import org.junit.Test;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertArrayEquals;
-
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.io.FileDescriptor;
-import java.io.FileOutputStream;
-import java.util.Arrays;
-import java.util.Comparator;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/*
- * TODO:
- * - allow overriding MetricsLoggerService constants in tests.
- * - test intents are correctly sent after the notification threshold.
- * - test oldest events are correctly pushed out when internal deque is full.
- * - test throttling triggers correctly.
- */
-public class MetricsLoggerServiceTest extends TestCase {
-
- static final int COMPONENT_TAG = 1;
- static final long N_EVENTS = 10L;
- static final ConnectivityMetricsEvent EVENTS[] = new ConnectivityMetricsEvent[(int)N_EVENTS];
- static {
- for (int i = 0; i < N_EVENTS; i++) {
- EVENTS[i] = new ConnectivityMetricsEvent(i, COMPONENT_TAG, i, new Bundle());
- }
- }
-
- static final ConnectivityMetricsEvent NO_EVENTS[] = new ConnectivityMetricsEvent[0];
-
- @Mock Context mContext;
- MetricsLoggerService mService;
-
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- mService = new MetricsLoggerService(mContext);
- mService.onStart();
- }
-
- @SmallTest
- public void testGetNoEvents() throws Exception {
- Reference r = new Reference(0);
- assertArrayEquals(NO_EVENTS, mService.mBinder.getEvents(r));
- assertEquals(0, r.getValue());
- }
-
- @SmallTest
- public void testLogAndGetEvents() throws Exception {
- mService.mBinder.logEvents(EVENTS);
-
- Reference r = new Reference(0);
-
- assertArrayEquals(EVENTS, mService.mBinder.getEvents(r));
- assertEquals(N_EVENTS, r.getValue());
-
- assertArrayEquals(NO_EVENTS, mService.mBinder.getEvents(r));
- assertEquals(N_EVENTS, r.getValue());
- }
-
- @SmallTest
- public void testLogOneByOne() throws Exception {
- for (ConnectivityMetricsEvent ev : EVENTS) {
- mService.mBinder.logEvent(ev);
- }
-
- Reference r = new Reference(0);
-
- assertArrayEquals(EVENTS, mService.mBinder.getEvents(r));
- assertEquals(N_EVENTS, r.getValue());
-
- assertArrayEquals(NO_EVENTS, mService.mBinder.getEvents(r));
- assertEquals(N_EVENTS, r.getValue());
- }
-
- @SmallTest
- public void testInterleavedLogAndGet() throws Exception {
- mService.mBinder.logEvents(Arrays.copyOfRange(EVENTS, 0, 3));
-
- Reference r = new Reference(0);
-
- assertArrayEquals(Arrays.copyOfRange(EVENTS, 0, 3), mService.mBinder.getEvents(r));
- assertEquals(3, r.getValue());
-
- mService.mBinder.logEvents(Arrays.copyOfRange(EVENTS, 3, 8));
- mService.mBinder.logEvents(Arrays.copyOfRange(EVENTS, 8, 10));
-
- assertArrayEquals(Arrays.copyOfRange(EVENTS, 3, 10), mService.mBinder.getEvents(r));
- assertEquals(N_EVENTS, r.getValue());
-
- assertArrayEquals(NO_EVENTS, mService.mBinder.getEvents(r));
- assertEquals(N_EVENTS, r.getValue());
- }
-
- @SmallTest
- public void testMultipleGetAll() throws Exception {
- mService.mBinder.logEvents(Arrays.copyOf(EVENTS, 3));
-
- Reference r1 = new Reference(0);
- assertArrayEquals(Arrays.copyOf(EVENTS, 3), mService.mBinder.getEvents(r1));
- assertEquals(3, r1.getValue());
-
- mService.mBinder.logEvents(Arrays.copyOfRange(EVENTS, 3, 10));
-
- Reference r2 = new Reference(0);
- assertArrayEquals(EVENTS, mService.mBinder.getEvents(r2));
- assertEquals(N_EVENTS, r2.getValue());
- }
-
- @SmallTest
- public void testLogAndDumpConcurrently() throws Exception {
- for (int i = 0; i < 50; i++) {
- mContext = null;
- mService = null;
- setUp();
- logAndDumpConcurrently();
- }
- }
-
- public void logAndDumpConcurrently() throws Exception {
- final CountDownLatch latch = new CountDownLatch((int)N_EVENTS);
- final FileDescriptor fd = new FileOutputStream("/dev/null").getFD();
-
- for (ConnectivityMetricsEvent ev : EVENTS) {
- new Thread() {
- public void run() {
- mService.mBinder.logEvent(ev);
- latch.countDown();
- }
- }.start();
- }
-
- new Thread() {
- public void run() {
- while (latch.getCount() > 0) {
- mService.mBinder.dump(fd, new String[]{"--all"});
- }
- }
- }.start();
-
- latch.await(100, TimeUnit.MILLISECONDS);
-
- Reference r = new Reference(0);
- ConnectivityMetricsEvent[] got = mService.mBinder.getEvents(r);
- Arrays.sort(got, new EventComparator());
- assertArrayEquals(EVENTS, got);
- assertEquals(N_EVENTS, r.getValue());
- }
-
- static class EventComparator implements Comparator<ConnectivityMetricsEvent> {
- public int compare(ConnectivityMetricsEvent ev1, ConnectivityMetricsEvent ev2) {
- return Long.compare(ev1.timestamp, ev2.timestamp);
- }
- public boolean equal(Object o) {
- return o instanceof EventComparator;
- }
- };
-}
diff --git a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
index 3ed56df..c72efb0 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
@@ -32,6 +32,8 @@
import static org.mockito.Mockito.verifyNoMoreInteractions;
import android.content.Context;
+import android.os.Handler;
+import android.os.Message;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.IConnectivityManager;
@@ -42,6 +44,10 @@
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+
+import org.junit.After;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.junit.Test;
@@ -49,6 +55,7 @@
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -63,6 +70,7 @@
@Mock private Context mContext;
@Mock private IConnectivityManager mCS;
+ private TestStateMachine mSM;
private TestConnectivityManager mCM;
private UpstreamNetworkMonitor mUNM;
@@ -72,7 +80,15 @@
reset(mCS);
mCM = spy(new TestConnectivityManager(mContext, mCS));
- mUNM = new UpstreamNetworkMonitor(null, EVENT_UNM_UPDATE, (ConnectivityManager) mCM);
+ mSM = new TestStateMachine();
+ mUNM = new UpstreamNetworkMonitor(mSM, EVENT_UNM_UPDATE, (ConnectivityManager) mCM);
+ }
+
+ @After public void tearDown() throws Exception {
+ if (mSM != null) {
+ mSM.quit();
+ mSM = null;
+ }
}
@Test
@@ -139,15 +155,17 @@
mUNM.start();
verify(mCM, Mockito.times(1)).registerNetworkCallback(
- any(NetworkRequest.class), any(NetworkCallback.class));
- verify(mCM, Mockito.times(1)).registerDefaultNetworkCallback(any(NetworkCallback.class));
+ any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class));
+ verify(mCM, Mockito.times(1)).registerDefaultNetworkCallback(
+ any(NetworkCallback.class), any(Handler.class));
assertFalse(mUNM.mobileNetworkRequested());
assertEquals(0, mCM.requested.size());
mUNM.updateMobileRequiresDun(true);
mUNM.registerMobileNetworkRequest();
verify(mCM, Mockito.times(1)).requestNetwork(
- any(NetworkRequest.class), any(NetworkCallback.class), anyInt(), anyInt());
+ any(NetworkRequest.class), any(NetworkCallback.class), anyInt(), anyInt(),
+ any(Handler.class));
assertTrue(mUNM.mobileNetworkRequested());
assertUpstreamTypeRequested(TYPE_MOBILE_DUN);
@@ -224,6 +242,7 @@
}
public static class TestConnectivityManager extends ConnectivityManager {
+ public Map<NetworkCallback, Handler> allCallbacks = new HashMap<>();
public Set<NetworkCallback> trackingDefault = new HashSet<>();
public Map<NetworkCallback, NetworkRequest> listening = new HashMap<>();
public Map<NetworkCallback, NetworkRequest> requested = new HashMap<>();
@@ -234,7 +253,8 @@
}
boolean hasNoCallbacks() {
- return trackingDefault.isEmpty() &&
+ return allCallbacks.isEmpty() &&
+ trackingDefault.isEmpty() &&
listening.isEmpty() &&
requested.isEmpty() &&
legacyTypeMap.isEmpty();
@@ -262,14 +282,23 @@
}
@Override
- public void requestNetwork(NetworkRequest req, NetworkCallback cb) {
+ public void requestNetwork(NetworkRequest req, NetworkCallback cb, Handler h) {
+ assertFalse(allCallbacks.containsKey(cb));
+ allCallbacks.put(cb, h);
assertFalse(requested.containsKey(cb));
requested.put(cb, req);
}
@Override
+ public void requestNetwork(NetworkRequest req, NetworkCallback cb) {
+ fail("Should never be called.");
+ }
+
+ @Override
public void requestNetwork(NetworkRequest req, NetworkCallback cb,
- int timeoutMs, int legacyType) {
+ int timeoutMs, int legacyType, Handler h) {
+ assertFalse(allCallbacks.containsKey(cb));
+ allCallbacks.put(cb, h);
assertFalse(requested.containsKey(cb));
requested.put(cb, req);
assertFalse(legacyTypeMap.containsKey(cb));
@@ -279,18 +308,32 @@
}
@Override
- public void registerNetworkCallback(NetworkRequest req, NetworkCallback cb) {
+ public void registerNetworkCallback(NetworkRequest req, NetworkCallback cb, Handler h) {
+ assertFalse(allCallbacks.containsKey(cb));
+ allCallbacks.put(cb, h);
assertFalse(listening.containsKey(cb));
listening.put(cb, req);
}
@Override
- public void registerDefaultNetworkCallback(NetworkCallback cb) {
+ public void registerNetworkCallback(NetworkRequest req, NetworkCallback cb) {
+ fail("Should never be called.");
+ }
+
+ @Override
+ public void registerDefaultNetworkCallback(NetworkCallback cb, Handler h) {
+ assertFalse(allCallbacks.containsKey(cb));
+ allCallbacks.put(cb, h);
assertFalse(trackingDefault.contains(cb));
trackingDefault.add(cb);
}
@Override
+ public void registerDefaultNetworkCallback(NetworkCallback cb) {
+ fail("Should never be called.");
+ }
+
+ @Override
public void unregisterNetworkCallback(NetworkCallback cb) {
if (trackingDefault.contains(cb)) {
trackingDefault.remove(cb);
@@ -302,10 +345,35 @@
} else {
fail("Unexpected callback removed");
}
+ allCallbacks.remove(cb);
+ assertFalse(allCallbacks.containsKey(cb));
assertFalse(trackingDefault.contains(cb));
assertFalse(listening.containsKey(cb));
assertFalse(requested.containsKey(cb));
}
}
+
+ public static class TestStateMachine extends StateMachine {
+ public final ArrayList<Message> messages = new ArrayList<>();
+ private final State mLoggingState = new LoggingState();
+
+ class LoggingState extends State {
+ @Override public void enter() { messages.clear(); }
+
+ @Override public void exit() { messages.clear(); }
+
+ @Override public boolean processMessage(Message msg) {
+ messages.add(msg);
+ return true;
+ }
+ }
+
+ public TestStateMachine() {
+ super("UpstreamNetworkMonitor.TestStateMachine");
+ addState(mLoggingState);
+ setInitialState(mLoggingState);
+ super.start();
+ }
+ }
}
diff --git a/tools/fonts/fontchain_lint.py b/tools/fonts/fontchain_lint.py
index 7ec46a3e..c5598f0 100755
--- a/tools/fonts/fontchain_lint.py
+++ b/tools/fonts/fontchain_lint.py
@@ -14,7 +14,9 @@
LANG_TO_SCRIPT = {
'as': 'Beng',
+ 'bg': 'Cyrl',
'bn': 'Beng',
+ 'cu': 'Cyrl',
'cy': 'Latn',
'da': 'Latn',
'de': 'Latn',
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 306f6f5..6095c86 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -376,7 +376,9 @@
/**
* Flag indicating if this network is provided by a home Passpoint provider or a roaming
- * Passpoint provider.
+ * Passpoint provider. This flag will be {@code true} if this network is provided by
+ * a home Passpoint provider and {@code false} if is provided by a roaming Passpoint provider
+ * or is a non-Passpoint network.
*/
public boolean isHomeProviderNetwork;
@@ -845,6 +847,7 @@
* This network is disabled because EAP-TLS failure
*/
public static final int DISABLED_TLS_VERSION_MISMATCH = 7;
+ // Values above are for temporary disablement; values below are for permanent disablement.
/**
* This network is disabled due to absence of user credentials
*/
@@ -969,6 +972,28 @@
private boolean mHasEverConnected;
/**
+ * Boolean indicating whether {@link com.android.server.wifi.RecommendedNetworkEvaluator}
+ * chose not to connect to this network in the last qualified network selection process.
+ */
+ private boolean mNotRecommended;
+
+ /**
+ * Set whether {@link com.android.server.wifi.RecommendedNetworkEvaluator} does not
+ * recommend connecting to this network.
+ */
+ public void setNotRecommended(boolean notRecommended) {
+ mNotRecommended = notRecommended;
+ }
+
+ /**
+ * Returns whether {@link com.android.server.wifi.RecommendedNetworkEvaluator} does not
+ * recommend connecting to this network.
+ */
+ public boolean isNotRecommended() {
+ return mNotRecommended;
+ }
+
+ /**
* set whether this network is visible in latest Qualified Network Selection
* @param seen value set to candidate
*/
@@ -1272,6 +1297,7 @@
setConnectChoice(source.getConnectChoice());
setConnectChoiceTimestamp(source.getConnectChoiceTimestamp());
setHasEverConnected(source.getHasEverConnected());
+ setNotRecommended(source.isNotRecommended());
}
public void writeToParcel(Parcel dest) {
@@ -1291,6 +1317,7 @@
dest.writeInt(CONNECT_CHOICE_NOT_EXISTS);
}
dest.writeInt(getHasEverConnected() ? 1 : 0);
+ dest.writeInt(isNotRecommended() ? 1 : 0);
}
public void readFromParcel(Parcel in) {
@@ -1310,6 +1337,7 @@
setConnectChoiceTimestamp(INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP);
}
setHasEverConnected(in.readInt() != 0);
+ setNotRecommended(in.readInt() != 0);
}
}
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index f790332..4268f24 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -236,11 +236,11 @@
public static final int TTLS = 2;
/** EAP-Password */
public static final int PWD = 3;
- /** EAP-Subscriber Identity Module */
+ /** EAP-Subscriber Identity Module [RFC-4186] */
public static final int SIM = 4;
- /** EAP-Authentication and Key Agreement */
+ /** EAP-Authentication and Key Agreement [RFC-4187] */
public static final int AKA = 5;
- /** EAP-Authentication and Key Agreement Prime */
+ /** EAP-Authentication and Key Agreement Prime [RFC-5448] */
public static final int AKA_PRIME = 6;
/** Hotspot 2.0 r2 OSEN */
public static final int UNAUTH_TLS = 7;
@@ -263,11 +263,11 @@
public static final int MSCHAPV2 = 3;
/** Generic Token Card */
public static final int GTC = 4;
- /** EAP-Subscriber Identity Module */
+ /** EAP-Subscriber Identity Module [RFC-4186] */
public static final int SIM = 5;
- /** EAP-Authentication and Key Agreement */
+ /** EAP-Authentication and Key Agreement [RFC-4187] */
public static final int AKA = 6;
- /** EAP-Authentication and Key Agreement Prime */
+ /** EAP-Authentication and Key Agreement Prime [RFC-5448] */
public static final int AKA_PRIME = 7;
private static final String AUTH_PREFIX = "auth=";
private static final String AUTHEAP_PREFIX = "autheap=";
@@ -756,8 +756,8 @@
* key entry when the config is saved and removing the key entry when
* the config is removed.
- * @param privateKey
- * @param clientCertificate
+ * @param privateKey a PrivateKey instance for the end certificate.
+ * @param clientCertificate an X509Certificate representing the end certificate.
* @throws IllegalArgumentException for an invalid key or certificate.
*/
public void setClientKeyEntry(PrivateKey privateKey, X509Certificate clientCertificate) {
@@ -775,9 +775,11 @@
* with this configuration. The framework takes care of installing the
* key entry when the config is saved and removing the key entry when
* the config is removed.
-
- * @param privateKey
- * @param clientCertificateChain
+ *
+ * @param privateKey a PrivateKey instance for the end certificate.
+ * @param clientCertificateChain an array of X509Certificate instances which starts with
+ * end certificate and continues with additional CA certificates necessary to
+ * link the end certificate with some root certificate known by the authenticator.
* @throws IllegalArgumentException for an invalid key or certificate.
*/
public void setClientKeyEntryWithCertificateChain(PrivateKey privateKey,
@@ -835,7 +837,15 @@
}
/**
- * Get the complete client certificate chain
+ * Get the complete client certificate chain in the same order as it was last supplied.
+ *
+ * <p>If the chain was last supplied by a call to
+ * {@link #setClientKeyEntry(java.security.PrivateKey, java.security.cert.X509Certificate)}
+ * with a non-null * certificate instance, a single-element array containing the certificate
+ * will be * returned. If {@link #setClientKeyEntryWithCertificateChain(
+ * java.security.PrivateKey, java.security.cert.X509Certificate[])} was last called with a
+ * non-empty array, this array will be returned in the same order as it was supplied.
+ * Otherwise, {@code null} will be returned.
*
* @return X.509 client certificates
*/
diff --git a/wifi/java/android/net/wifi/aware/ConfigRequest.java b/wifi/java/android/net/wifi/aware/ConfigRequest.java
index 6a5957b..cc14ab2 100644
--- a/wifi/java/android/net/wifi/aware/ConfigRequest.java
+++ b/wifi/java/android/net/wifi/aware/ConfigRequest.java
@@ -19,6 +19,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Arrays;
+
/**
* Defines a request object to configure a Wi-Fi Aware network. Built using
* {@link ConfigRequest.Builder}. Configuration is requested using
@@ -41,6 +43,18 @@
public static final int CLUSTER_ID_MAX = 0xFFFF;
/**
+ * Indices for configuration variables which are specified per band.
+ */
+ public static final int NAN_BAND_24GHZ = 0;
+ public static final int NAN_BAND_5GHZ = 1;
+
+ /**
+ * Magic values for Discovery Window (DW) interval configuration
+ */
+ public static final int DW_INTERVAL_NOT_INIT = -1;
+ public static final int DW_DISABLE = 0; // only valid for 5GHz
+
+ /**
* Indicates whether 5G band support is requested.
*/
public final boolean mSupport5gBand;
@@ -62,19 +76,26 @@
*/
public final int mClusterHigh;
+ /**
+ * Specifies the discovery window interval for the device on NAN_BAND_*.
+ */
+ public final int mDiscoveryWindowInterval[];
+
private ConfigRequest(boolean support5gBand, int masterPreference, int clusterLow,
- int clusterHigh) {
+ int clusterHigh, int discoveryWindowInterval[]) {
mSupport5gBand = support5gBand;
mMasterPreference = masterPreference;
mClusterLow = clusterLow;
mClusterHigh = clusterHigh;
+ mDiscoveryWindowInterval = discoveryWindowInterval;
}
@Override
public String toString() {
return "ConfigRequest [mSupport5gBand=" + mSupport5gBand + ", mMasterPreference="
+ mMasterPreference + ", mClusterLow=" + mClusterLow + ", mClusterHigh="
- + mClusterHigh + "]";
+ + mClusterHigh + ", mDiscoveryWindowInterval="
+ + Arrays.toString(mDiscoveryWindowInterval) + "]";
}
@Override
@@ -88,6 +109,7 @@
dest.writeInt(mMasterPreference);
dest.writeInt(mClusterLow);
dest.writeInt(mClusterHigh);
+ dest.writeIntArray(mDiscoveryWindowInterval);
}
public static final Creator<ConfigRequest> CREATOR = new Creator<ConfigRequest>() {
@@ -102,7 +124,10 @@
int masterPreference = in.readInt();
int clusterLow = in.readInt();
int clusterHigh = in.readInt();
- return new ConfigRequest(support5gBand, masterPreference, clusterLow, clusterHigh);
+ int discoveryWindowInterval[] = in.createIntArray();
+
+ return new ConfigRequest(support5gBand, masterPreference, clusterLow, clusterHigh,
+ discoveryWindowInterval);
}
};
@@ -119,17 +144,8 @@
ConfigRequest lhs = (ConfigRequest) o;
return mSupport5gBand == lhs.mSupport5gBand && mMasterPreference == lhs.mMasterPreference
- && mClusterLow == lhs.mClusterLow && mClusterHigh == lhs.mClusterHigh;
- }
-
- /**
- * Checks whether the configuration's settings are non-default.
- *
- * @return true if any of the settings are non-default.
- */
- public boolean isNonDefault() {
- return mSupport5gBand || mMasterPreference != 0 || mClusterLow != CLUSTER_ID_MIN
- || mClusterHigh != CLUSTER_ID_MAX;
+ && mClusterLow == lhs.mClusterLow && mClusterHigh == lhs.mClusterHigh
+ && Arrays.equals(mDiscoveryWindowInterval, lhs.mDiscoveryWindowInterval);
}
@Override
@@ -140,6 +156,7 @@
result = 31 * result + mMasterPreference;
result = 31 * result + mClusterLow;
result = 31 * result + mClusterHigh;
+ result = 31 * result + Arrays.hashCode(mDiscoveryWindowInterval);
return result;
}
@@ -173,6 +190,23 @@
throw new IllegalArgumentException(
"Invalid argument combination - must have Cluster Low <= Cluster High");
}
+ if (mDiscoveryWindowInterval.length != 2) {
+ throw new IllegalArgumentException(
+ "Invalid discovery window interval: must have 2 elements (2.4 & 5");
+ }
+ if (mDiscoveryWindowInterval[NAN_BAND_24GHZ] != DW_INTERVAL_NOT_INIT &&
+ (mDiscoveryWindowInterval[NAN_BAND_24GHZ] < 1 // valid for 2.4GHz: [1-5]
+ || mDiscoveryWindowInterval[NAN_BAND_24GHZ] > 5)) {
+ throw new IllegalArgumentException(
+ "Invalid discovery window interval for 2.4GHz: valid is UNSET or [1,5]");
+ }
+ if (mDiscoveryWindowInterval[NAN_BAND_5GHZ] != DW_INTERVAL_NOT_INIT &&
+ (mDiscoveryWindowInterval[NAN_BAND_5GHZ] < 0 // valid for 5GHz: [0-5]
+ || mDiscoveryWindowInterval[NAN_BAND_5GHZ] > 5)) {
+ throw new IllegalArgumentException(
+ "Invalid discovery window interval for 5GHz: valid is UNSET or [0,5]");
+ }
+
}
/**
@@ -183,6 +217,7 @@
private int mMasterPreference = 0;
private int mClusterLow = CLUSTER_ID_MIN;
private int mClusterHigh = CLUSTER_ID_MAX;
+ private int mDiscoveryWindowInterval[] = {DW_INTERVAL_NOT_INIT, DW_INTERVAL_NOT_INIT};
/**
* Specify whether 5G band support is required in this request. Disabled by default.
@@ -271,6 +306,33 @@
}
/**
+ * The discovery window interval specifies the discovery windows in which the device will be
+ * awake. The configuration enables trading off latency vs. power (higher interval means
+ * higher discovery latency but lower power).
+ *
+ * @param band Either {@link #NAN_BAND_24GHZ} or {@link #NAN_BAND_5GHZ}.
+ * @param interval A value of 1, 2, 3, 4, or 5 indicating an interval of 2^(interval-1). For
+ * the 5GHz band a value of 0 indicates that the device will not be awake
+ * for any discovery windows.
+ *
+ * @return The builder itself to facilitate chaining operations
+ * {@code builder.setDiscoveryWindowInterval(...).setMasterPreference(...)}.
+ */
+ public Builder setDiscoveryWindowInterval(int band, int interval) {
+ if (band != NAN_BAND_24GHZ && band != NAN_BAND_5GHZ) {
+ throw new IllegalArgumentException("Invalid band value");
+ }
+ if ((band == NAN_BAND_24GHZ && (interval < 1 || interval > 5))
+ || (band == NAN_BAND_5GHZ && (interval < 0 || interval > 5))) {
+ throw new IllegalArgumentException(
+ "Invalid interval value: 2.4 GHz [1,5] or 5GHz [0,5]");
+ }
+
+ mDiscoveryWindowInterval[band] = interval;
+ return this;
+ }
+
+ /**
* Build {@link ConfigRequest} given the current requests made on the
* builder.
*/
@@ -280,7 +342,8 @@
"Invalid argument combination - must have Cluster Low <= Cluster High");
}
- return new ConfigRequest(mSupport5gBand, mMasterPreference, mClusterLow, mClusterHigh);
+ return new ConfigRequest(mSupport5gBand, mMasterPreference, mClusterLow, mClusterHigh,
+ mDiscoveryWindowInterval);
}
}
}
diff --git a/wifi/java/android/net/wifi/aware/DiscoverySession.java b/wifi/java/android/net/wifi/aware/DiscoverySession.java
index adf189b..82b3792 100644
--- a/wifi/java/android/net/wifi/aware/DiscoverySession.java
+++ b/wifi/java/android/net/wifi/aware/DiscoverySession.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.net.wifi.RttManager;
import android.util.Log;
@@ -31,10 +32,10 @@
* {@link PublishDiscoverySession} and {@link SubscribeDiscoverySession}. This
* class provides functionality common to both publish and subscribe discovery sessions:
* <ul>
- * <li>Sending messages: {@link #sendMessage(PeerHandle, int, byte[])} or
- * {@link #sendMessage(PeerHandle, int, byte[], int)} methods.
+ * <li>Sending messages: {@link #sendMessage(PeerHandle, int, byte[])} method.
* <li>Creating a network-specifier when requesting a Aware connection:
- * {@link #createNetworkSpecifier(PeerHandle, byte[])}.
+ * {@link #createNetworkSpecifierOpen(PeerHandle)} or
+ * {@link #createNetworkSpecifierPassphrase(PeerHandle, String)}.
* </ul>
* The {@link #destroy()} method must be called to destroy discovery sessions once they are
* no longer needed.
@@ -62,6 +63,8 @@
* {@link #sendMessage(PeerHandle, int, byte[], int)}.
*
* @return Maximum retry count when sending messages.
+ *
+ * @hide
*/
public static int getMaxSendRetryCount() {
return MAX_SEND_RETRY_COUNT;
@@ -115,6 +118,7 @@
Log.w(TAG, "terminate: already terminated.");
return;
}
+
mTerminated = true;
mMgr.clear();
mCloseGuard.close();
@@ -163,21 +167,23 @@
* or MAC level) retries should be attempted if there is no ACK from the receiver
* (note: no retransmissions are attempted in other failure cases). A value of 0
* indicates no retries. Max permitted value is {@link #getMaxSendRetryCount()}.
+ *
+ * @hide
*/
public void sendMessage(@NonNull PeerHandle peerHandle, int messageId,
@Nullable byte[] message, int retryCount) {
if (mTerminated) {
Log.w(TAG, "sendMessage: called on terminated session");
return;
- } else {
- WifiAwareManager mgr = mMgr.get();
- if (mgr == null) {
- Log.w(TAG, "sendMessage: called post GC on WifiAwareManager");
- return;
- }
-
- mgr.sendMessage(mClientId, mSessionId, peerHandle, message, messageId, retryCount);
}
+
+ WifiAwareManager mgr = mMgr.get();
+ if (mgr == null) {
+ Log.w(TAG, "sendMessage: called post GC on WifiAwareManager");
+ return;
+ }
+
+ mgr.sendMessage(mClientId, mSessionId, peerHandle, message, messageId, retryCount);
}
/**
@@ -195,8 +201,6 @@
* The peer will get a callback indicating a message was received using
* {@link DiscoverySessionCallback#onMessageReceived(PeerHandle,
* byte[])}.
- * Equivalent to {@link #sendMessage(PeerHandle, int, byte[], int)}
- * with a {@code retryCount} of 0.
*
* @param peerHandle The peer's handle for the message. Must be a result of an
* {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle,
@@ -234,45 +238,96 @@
if (mTerminated) {
Log.w(TAG, "startRanging: called on terminated session");
return;
- } else {
- WifiAwareManager mgr = mMgr.get();
- if (mgr == null) {
- Log.w(TAG, "startRanging: called post GC on WifiAwareManager");
- return;
- }
-
- mgr.startRanging(mClientId, mSessionId, params, listener);
}
+
+ WifiAwareManager mgr = mMgr.get();
+ if (mgr == null) {
+ Log.w(TAG, "startRanging: called post GC on WifiAwareManager");
+ return;
+ }
+
+ mgr.startRanging(mClientId, mSessionId, params, listener);
}
/**
- * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for a
- * WiFi Aware connection to the specified peer. The
+ * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for an
+ * unencrypted WiFi Aware connection (link) to the specified peer. The
* {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to
* {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}.
* <p>
* This method should be used when setting up a connection with a peer discovered through Aware
* discovery or communication (in such scenarios the MAC address of the peer is shielded by
- * an opaque peer ID handle). If a Aware connection is needed to a peer discovered using other
+ * an opaque peer ID handle). If an Aware connection is needed to a peer discovered using other
* OOB (out-of-band) mechanism then use the alternative
- * {@link WifiAwareSession#createNetworkSpecifier(int, byte[], byte[])} method - which uses the
+ * {@link WifiAwareSession#createNetworkSpecifierOpen(int, byte[])} method - which uses the
* peer's MAC address.
* <p>
* Note: per the Wi-Fi Aware specification the roles are fixed - a Subscriber is an INITIATOR
* and a Publisher is a RESPONDER.
+ * <p>
+ * To set up an encrypted link use the
+ * {@link #createNetworkSpecifierPassphrase(PeerHandle, String)} API.
+ *
+ * @param peerHandle The peer's handle obtained through
+ * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, byte[], java.util.List)}
+ * or
+ * {@link DiscoverySessionCallback#onMessageReceived(PeerHandle, byte[])}.
+ * On a RESPONDER this value is used to gate the acceptance of a connection
+ * request from only that peer. A RESPONDER may specify a {@code null} -
+ * indicating that it will accept connection requests from any device.
+ *
+ * @return A string to be used to construct
+ * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to
+ * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest,
+ * android.net.ConnectivityManager.NetworkCallback)}
+ * [or other varieties of that API].
+ */
+ public String createNetworkSpecifierOpen(@Nullable PeerHandle peerHandle) {
+ if (mTerminated) {
+ Log.w(TAG, "createNetworkSpecifierOpen: called on terminated session");
+ return null;
+ }
+
+ WifiAwareManager mgr = mMgr.get();
+ if (mgr == null) {
+ Log.w(TAG, "createNetworkSpecifierOpen: called post GC on WifiAwareManager");
+ return null;
+ }
+
+ int role = this instanceof SubscribeDiscoverySession
+ ? WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
+ : WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER;
+
+ return mgr.createNetworkSpecifier(mClientId, role, mSessionId, peerHandle, null, null);
+ }
+
+ /**
+ * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for an
+ * encrypted WiFi Aware connection (link) to the specified peer. The
+ * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to
+ * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}.
+ * <p>
+ * This method should be used when setting up a connection with a peer discovered through Aware
+ * discovery or communication (in such scenarios the MAC address of the peer is shielded by
+ * an opaque peer ID handle). If an Aware connection is needed to a peer discovered using other
+ * OOB (out-of-band) mechanism then use the alternative
+ * {@link WifiAwareSession#createNetworkSpecifierPassphrase(int, byte[], String)} method -
+ * which uses the peer's MAC address.
+ * <p>
+ * Note: per the Wi-Fi Aware specification the roles are fixed - a Subscriber is an INITIATOR
+ * and a Publisher is a RESPONDER.
*
* @param peerHandle The peer's handle obtained through
* {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle,
* byte[], java.util.List)} or
* {@link DiscoverySessionCallback#onMessageReceived(PeerHandle,
* byte[])}. On a RESPONDER this value is used to gate the acceptance of a connection request
- * from only that peer. A RESPONDER may specified a null - indicating that
- * it will accept connection requests from any device.
- * @param token An arbitrary token (message) to be used to match connection initiation request
- * to a responder setup. A RESPONDER is set up with a {@code token} which must
- * be matched by the token provided by the INITIATOR. A null token is permitted
- * on the RESPONDER and matches any peer token. An empty ({@code ""}) token is
- * not the same as a null token and requires the peer token to be empty as well.
+ * from only that peer. A RESPONDER may specify a {@code null} - indicating
+ * that it will accept connection requests from any device.
+ * @param passphrase The passphrase to be used to encrypt the link. The PMK is generated from
+ * the passphrase. Use the
+ * {@link #createNetworkSpecifierOpen(PeerHandle)} API to
+ * specify an open (unencrypted) link.
*
* @return A string to be used to construct
* {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to
@@ -280,23 +335,90 @@
* android.net.ConnectivityManager.NetworkCallback)}
* [or other varieties of that API].
*/
- public String createNetworkSpecifier(@Nullable PeerHandle peerHandle,
- @Nullable byte[] token) {
- if (mTerminated) {
- Log.w(TAG, "createNetworkSpecifier: called on terminated session");
- return null;
- } else {
- WifiAwareManager mgr = mMgr.get();
- if (mgr == null) {
- Log.w(TAG, "createNetworkSpecifier: called post GC on WifiAwareManager");
- return null;
- }
-
- int role = this instanceof SubscribeDiscoverySession
- ? WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
- : WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER;
-
- return mgr.createNetworkSpecifier(mClientId, role, mSessionId, peerHandle, token);
+ public String createNetworkSpecifierPassphrase(@Nullable PeerHandle peerHandle,
+ @NonNull String passphrase) {
+ if (passphrase == null || passphrase.length() == 0) {
+ throw new IllegalArgumentException("Passphrase must not be null or empty");
}
+
+ if (mTerminated) {
+ Log.w(TAG, "createNetworkSpecifierPassphrase: called on terminated session");
+ return null;
+ }
+
+ WifiAwareManager mgr = mMgr.get();
+ if (mgr == null) {
+ Log.w(TAG, "createNetworkSpecifierPassphrase: called post GC on WifiAwareManager");
+ return null;
+ }
+
+ int role = this instanceof SubscribeDiscoverySession
+ ? WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
+ : WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER;
+
+ return mgr.createNetworkSpecifier(mClientId, role, mSessionId, peerHandle, null,
+ passphrase);
+ }
+
+ /**
+ * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for an
+ * encrypted WiFi Aware connection (link) to the specified peer. The
+ * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to
+ * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}.
+ * <p>
+ * This method should be used when setting up a connection with a peer discovered through Aware
+ * discovery or communication (in such scenarios the MAC address of the peer is shielded by
+ * an opaque peer ID handle). If an Aware connection is needed to a peer discovered using other
+ * OOB (out-of-band) mechanism then use the alternative
+ * {@link WifiAwareSession#createNetworkSpecifierPmk(int, byte[], byte[])} method - which uses
+ * the peer's MAC address.
+ * <p>
+ * Note: per the Wi-Fi Aware specification the roles are fixed - a Subscriber is an INITIATOR
+ * and a Publisher is a RESPONDER.
+ *
+ * @param peerHandle The peer's handle obtained through
+ * {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle,
+ * byte[], java.util.List)} or
+ * {@link DiscoverySessionCallback#onMessageReceived(PeerHandle,
+ * byte[])}. On a RESPONDER this value is used to gate the acceptance of a connection request
+ * from only that peer. A RESPONDER may specify a null - indicating that
+ * it will accept connection requests from any device.
+ * @param pmk A PMK (pairwise master key, see IEEE 802.11i) specifying the key to use for
+ * encrypting the data-path. Use the
+ * {@link #createNetworkSpecifierPassphrase(PeerHandle, String)} to specify a
+ * Passphrase or {@link #createNetworkSpecifierOpen(PeerHandle)} to specify an
+ * open (unencrypted) link.
+ *
+ * @return A string to be used to construct
+ * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to
+ * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest,
+ * android.net.ConnectivityManager.NetworkCallback)}
+ * [or other varieties of that API].
+ *
+ * @hide
+ */
+ @SystemApi
+ public String createNetworkSpecifierPmk(@Nullable PeerHandle peerHandle,
+ @NonNull byte[] pmk) {
+ if (pmk == null || pmk.length == 0) {
+ throw new IllegalArgumentException("PMK must not be null or empty");
+ }
+
+ if (mTerminated) {
+ Log.w(TAG, "createNetworkSpecifierPmk: called on terminated session");
+ return null;
+ }
+
+ WifiAwareManager mgr = mMgr.get();
+ if (mgr == null) {
+ Log.w(TAG, "createNetworkSpecifierPmk: called post GC on WifiAwareManager");
+ return null;
+ }
+
+ int role = this instanceof SubscribeDiscoverySession
+ ? WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
+ : WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER;
+
+ return mgr.createNetworkSpecifier(mClientId, role, mSessionId, peerHandle, pmk, null);
}
}
diff --git a/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java b/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java
index 33da182..9645b1d 100644
--- a/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java
+++ b/wifi/java/android/net/wifi/aware/DiscoverySessionCallback.java
@@ -124,10 +124,9 @@
}
/**
- * Called when message transmission fails - when no ACK is received from the peer.
- * Retries when ACKs are not received are done by hardware, MAC, and in the Aware stack (using
- * the {@link DiscoverySession#sendMessage(PeerHandle, int,
- * byte[], int)} method) - this event is received after all retries are exhausted.
+ * Called when message transmission initiated with
+ * {@link DiscoverySession#sendMessage(PeerHandle, int, byte[])} fails. E.g. when no ACK is
+ * received from the peer.
* <p>
* Note that either this callback or
* {@link DiscoverySessionCallback#onMessageSendSucceeded(int)} will be received
@@ -141,9 +140,7 @@
/**
* Called when a message is received from a discovery session peer - in response to the
- * peer's {@link DiscoverySession#sendMessage(PeerHandle, int,
- * byte[])} or {@link DiscoverySession#sendMessage(PeerHandle,
- * int, byte[], int)}.
+ * peer's {@link DiscoverySession#sendMessage(PeerHandle, int, byte[])}.
*
* @param peerHandle An opaque handle to the peer matching our discovery operation.
* @param message A byte array containing the message.
diff --git a/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl b/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl
index 794c142..0f4910f 100644
--- a/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl
+++ b/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl
@@ -34,8 +34,6 @@
interface IWifiAwareManager
{
// Aware API
- void enableUsage();
- void disableUsage();
boolean isUsageEnabled();
Characteristics getCharacteristics();
diff --git a/wifi/java/android/net/wifi/aware/IdentityChangedListener.java b/wifi/java/android/net/wifi/aware/IdentityChangedListener.java
index cae8706..81a06e8 100644
--- a/wifi/java/android/net/wifi/aware/IdentityChangedListener.java
+++ b/wifi/java/android/net/wifi/aware/IdentityChangedListener.java
@@ -24,7 +24,8 @@
* your identity - e.g. by starting a discovery session. This actual MAC address of the
* interface may also be useful if the application uses alternative (non-Aware) discovery but needs
* to set up a Aware connection. The provided Aware discovery interface MAC address can then be used
- * in {@link WifiAwareSession#createNetworkSpecifier(int, byte[], byte[])}.
+ * in {@link WifiAwareSession#createNetworkSpecifierOpen(int, byte[])} or
+ * {@link WifiAwareSession#createNetworkSpecifierPassphrase(int, byte[], String)}.
*/
public class IdentityChangedListener {
/**
diff --git a/wifi/java/android/net/wifi/aware/PeerHandle.java b/wifi/java/android/net/wifi/aware/PeerHandle.java
index bbe9f54..cd45c52 100644
--- a/wifi/java/android/net/wifi/aware/PeerHandle.java
+++ b/wifi/java/android/net/wifi/aware/PeerHandle.java
@@ -19,9 +19,10 @@
/**
* Opaque object used to represent a Wi-Fi Aware peer. Obtained from discovery sessions in
* {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, byte[], java.util.List)}, used
- * when sending messages e,g, {@link PublishDiscoverySession#sendMessage(PeerHandle, int, byte[])},
+ * when sending messages e,g, {@link DiscoverySession#sendMessage(PeerHandle, int, byte[])},
* or when configuring a network link to a peer, e.g.
- * {@link PublishDiscoverySession#createNetworkSpecifier(PeerHandle, byte[])}.
+ * {@link DiscoverySession#createNetworkSpecifierOpen(PeerHandle)} or
+ * {@link DiscoverySession#createNetworkSpecifierPassphrase(PeerHandle, String)}.
*/
public class PeerHandle {
/** @hide */
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
index a9e38ce..4d3957a 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
@@ -65,8 +65,10 @@
* <li>Create a Aware network specifier to be used with
* {@link ConnectivityManager#requestNetwork(NetworkRequest, ConnectivityManager.NetworkCallback)}
* to set-up a Aware connection with a peer. Refer to
- * {@link DiscoverySession#createNetworkSpecifier(PeerHandle, byte[])} and
- * {@link WifiAwareSession#createNetworkSpecifier(int, byte[], byte[])}.
+ * {@link DiscoverySession#createNetworkSpecifierOpen(PeerHandle)},
+ * {@link DiscoverySession#createNetworkSpecifierPassphrase(PeerHandle, String)},
+ * {@link WifiAwareSession#createNetworkSpecifierOpen(int, byte[])}, and
+ * {@link WifiAwareSession#createNetworkSpecifierPassphrase(int, byte[], String)}.
* </ul>
* <p>
* Aware may not be usable when Wi-Fi is disabled (and other conditions). To validate that
@@ -115,8 +117,10 @@
* <li>{@link NetworkRequest.Builder#addTransportType(int)} of
* {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}.
* <li>{@link NetworkRequest.Builder#setNetworkSpecifier(String)} using
- * {@link WifiAwareSession#createNetworkSpecifier(int, byte[], byte[])} or
- * {@link DiscoverySession#createNetworkSpecifier(PeerHandle, byte[])}.
+ * {@link WifiAwareSession#createNetworkSpecifierOpen(int, byte[])},
+ * {@link WifiAwareSession#createNetworkSpecifierPassphrase(int, byte[], String)},
+ * {@link DiscoverySession#createNetworkSpecifierOpen(PeerHandle)}, or
+ * {@link DiscoverySession#createNetworkSpecifierPassphrase(PeerHandle, String)}.
* </ul>
*/
public class WifiAwareManager {
@@ -130,55 +134,34 @@
*/
/**
- * TYPE_1A: role, client_id, session_id, peer_id, token
+ * TYPE: in band, specific peer: role, client_id, session_id, peer_id, pmk/passphrase optional
* @hide
*/
- public static final int NETWORK_SPECIFIER_TYPE_1A = 0;
+ public static final int NETWORK_SPECIFIER_TYPE_IB = 0;
/**
- * TYPE_1B: role, client_id, session_id, peer_id [only permitted for RESPONDER]
+ * TYPE: in band, any peer: role, client_id, session_id, pmk/passphrase optional
+ * [only permitted for RESPONDER]
* @hide
*/
- public static final int NETWORK_SPECIFIER_TYPE_1B = 1;
+ public static final int NETWORK_SPECIFIER_TYPE_IB_ANY_PEER = 1;
/**
- * TYPE_1C: role, client_id, session_id, token [only permitted for RESPONDER]
+ * TYPE: out-of-band: role, client_id, peer_mac, pmk/passphrase optional
* @hide
*/
- public static final int NETWORK_SPECIFIER_TYPE_1C = 2;
+ public static final int NETWORK_SPECIFIER_TYPE_OOB = 2;
/**
- * TYPE_1C: role, client_id, session_id [only permitted for RESPONDER]
+ * TYPE: out-of-band, any peer: role, client_id, pmk/passphrase optional
+ * [only permitted for RESPONDER]
* @hide
*/
- public static final int NETWORK_SPECIFIER_TYPE_1D = 3;
+ public static final int NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER = 3;
- /**
- * TYPE_2A: role, client_id, peer_mac, token
- * @hide
- */
- public static final int NETWORK_SPECIFIER_TYPE_2A = 4;
-
- /**
- * TYPE_2B: role, client_id, peer_mac [only permitted for RESPONDER]
- * @hide
- */
- public static final int NETWORK_SPECIFIER_TYPE_2B = 5;
-
- /**
- * TYPE_2C: role, client_id, token [only permitted for RESPONDER]
- * @hide
- */
- public static final int NETWORK_SPECIFIER_TYPE_2C = 6;
-
- /**
- * TYPE_2D: role, client_id [only permitted for RESPONDER]
- * @hide
- */
- public static final int NETWORK_SPECIFIER_TYPE_2D = 7;
/** @hide */
- public static final int NETWORK_SPECIFIER_TYPE_MAX_VALID = NETWORK_SPECIFIER_TYPE_2D;
+ public static final int NETWORK_SPECIFIER_TYPE_MAX_VALID = NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER;
/** @hide */
public static final String NETWORK_SPECIFIER_KEY_TYPE = "type";
@@ -199,7 +182,10 @@
public static final String NETWORK_SPECIFIER_KEY_PEER_MAC = "peer_mac";
/** @hide */
- public static final String NETWORK_SPECIFIER_KEY_TOKEN = "token";
+ public static final String NETWORK_SPECIFIER_KEY_PMK = "pmk";
+
+ /** @hide */
+ public static final String NETWORK_SPECIFIER_KEY_PASSPHRASE = "passphrase";
/**
* Broadcast intent action to indicate that the state of Wi-Fi Aware availability has changed.
@@ -224,8 +210,10 @@
* Connection creation role is that of INITIATOR. Used to create a network specifier string
* when requesting a Aware network.
*
- * @see DiscoverySession#createNetworkSpecifier(PeerHandle, byte[])
- * @see WifiAwareSession#createNetworkSpecifier(int, byte[], byte[])
+ * @see DiscoverySession#createNetworkSpecifierOpen(PeerHandle)
+ * @see DiscoverySession#createNetworkSpecifierPassphrase(PeerHandle, String)
+ * @see WifiAwareSession#createNetworkSpecifierOpen(int, byte[])
+ * @see WifiAwareSession#createNetworkSpecifierPassphrase(int, byte[], String)
*/
public static final int WIFI_AWARE_DATA_PATH_ROLE_INITIATOR = 0;
@@ -233,8 +221,10 @@
* Connection creation role is that of RESPONDER. Used to create a network specifier string
* when requesting a Aware network.
*
- * @see DiscoverySession#createNetworkSpecifier(PeerHandle, byte[])
- * @see WifiAwareSession#createNetworkSpecifier(int, byte[], byte[])
+ * @see DiscoverySession#createNetworkSpecifierOpen(PeerHandle)
+ * @see DiscoverySession#createNetworkSpecifierPassphrase(PeerHandle, String)
+ * @see WifiAwareSession#createNetworkSpecifierOpen(int, byte[])
+ * @see WifiAwareSession#createNetworkSpecifierPassphrase(int, byte[], String)
*/
public static final int WIFI_AWARE_DATA_PATH_ROLE_RESPONDER = 1;
@@ -253,36 +243,6 @@
}
/**
- * Enable the usage of the Aware API. Doesn't actually turn on Aware cluster formation - that
- * only happens when an attach is attempted. {@link #ACTION_WIFI_AWARE_STATE_CHANGED} broadcast
- * will be triggered.
- *
- * @hide
- */
- public void enableUsage() {
- try {
- mService.enableUsage();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Disable the usage of the Aware API. All attempts to attach() will be rejected. All open
- * connections and sessions will be terminated. {@link #ACTION_WIFI_AWARE_STATE_CHANGED}
- * broadcast will be triggered.
- *
- * @hide
- */
- public void disableUsage() {
- try {
- mService.disableUsage();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* Returns the current status of Aware API: whether or not Aware is available. To track
* changes in the state of Aware API register for the
* {@link #ACTION_WIFI_AWARE_STATE_CHANGED} broadcast.
@@ -524,23 +484,16 @@
/** @hide */
public String createNetworkSpecifier(int clientId, int role, int sessionId,
- PeerHandle peerHandle, byte[] token) {
+ PeerHandle peerHandle, @Nullable byte[] pmk, @Nullable String passphrase) {
if (VDBG) {
Log.v(TAG, "createNetworkSpecifier: role=" + role + ", sessionId=" + sessionId
+ ", peerHandle=" + ((peerHandle == null) ? peerHandle : peerHandle.peerId)
- + ", token=" + token);
+ + ", pmk=" + ((pmk == null) ? "null" : "non-null")
+ + ", passphrase=" + ((passphrase == null) ? "null" : "non-null"));
}
- int type;
- if (token != null && peerHandle != null) {
- type = NETWORK_SPECIFIER_TYPE_1A;
- } else if (token == null && peerHandle != null) {
- type = NETWORK_SPECIFIER_TYPE_1B;
- } else if (token != null && peerHandle == null) {
- type = NETWORK_SPECIFIER_TYPE_1C;
- } else {
- type = NETWORK_SPECIFIER_TYPE_1D;
- }
+ int type = (peerHandle == null) ? NETWORK_SPECIFIER_TYPE_IB_ANY_PEER
+ : NETWORK_SPECIFIER_TYPE_IB;
if (role != WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
&& role != WIFI_AWARE_DATA_PATH_ROLE_RESPONDER) {
@@ -549,10 +502,6 @@
+ "specifier");
}
if (role == WIFI_AWARE_DATA_PATH_ROLE_INITIATOR) {
- if (token == null) {
- throw new IllegalArgumentException(
- "createNetworkSpecifier: Invalid null token - not permitted on INITIATOR");
- }
if (peerHandle == null) {
throw new IllegalArgumentException(
"createNetworkSpecifier: Invalid peer handle (value of null) - not "
@@ -570,10 +519,16 @@
if (peerHandle != null) {
json.put(NETWORK_SPECIFIER_KEY_PEER_ID, peerHandle.peerId);
}
- if (token != null) {
- json.put(NETWORK_SPECIFIER_KEY_TOKEN,
- Base64.encodeToString(token, 0, token.length, Base64.DEFAULT));
+ if (pmk == null) {
+ pmk = new byte[0];
}
+ json.put(NETWORK_SPECIFIER_KEY_PMK,
+ Base64.encodeToString(pmk, 0, pmk.length, Base64.DEFAULT));
+ if (passphrase == null) {
+ passphrase = new String();
+ }
+ json.put(NETWORK_SPECIFIER_KEY_PASSPHRASE, passphrase);
+
} catch (JSONException e) {
return "";
}
@@ -583,21 +538,15 @@
/** @hide */
public String createNetworkSpecifier(int clientId, @DataPathRole int role,
- @Nullable byte[] peer, @Nullable byte[] token) {
+ @Nullable byte[] peer, @Nullable byte[] pmk, @Nullable String passphrase) {
if (VDBG) {
- Log.v(TAG, "createNetworkSpecifier: role=" + role + ", token=" + token);
+ Log.v(TAG, "createNetworkSpecifier: role=" + role
+ + ", pmk=" + ((pmk == null) ? "null" : "non-null")
+ + ", passphrase=" + ((passphrase == null) ? "null" : "non-null"));
}
- int type;
- if (token != null && peer != null) {
- type = NETWORK_SPECIFIER_TYPE_2A;
- } else if (token == null && peer != null) {
- type = NETWORK_SPECIFIER_TYPE_2B;
- } else if (token != null && peer == null) {
- type = NETWORK_SPECIFIER_TYPE_2C;
- } else { // both are null
- type = NETWORK_SPECIFIER_TYPE_2D;
- }
+ int type = (peer == null) ?
+ NETWORK_SPECIFIER_TYPE_OOB_ANY_PEER : NETWORK_SPECIFIER_TYPE_OOB;
if (role != WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
&& role != WIFI_AWARE_DATA_PATH_ROLE_RESPONDER) {
@@ -606,19 +555,13 @@
+ "specifier");
}
if (role == WIFI_AWARE_DATA_PATH_ROLE_INITIATOR) {
- if (peer == null || peer.length != 6) {
- throw new IllegalArgumentException(
- "createNetworkSpecifier: Invalid peer MAC address");
+ if (peer == null) {
+ throw new IllegalArgumentException("createNetworkSpecifier: Invalid peer MAC "
+ + "address - null not permitted on INITIATOR");
}
- if (token == null) {
- throw new IllegalArgumentException(
- "createNetworkSpecifier: Invalid null token - not permitted on INITIATOR");
- }
- } else {
- if (peer != null && peer.length != 6) {
- throw new IllegalArgumentException(
- "createNetworkSpecifier: Invalid peer MAC address");
- }
+ }
+ if (peer != null && peer.length != 6) {
+ throw new IllegalArgumentException("createNetworkSpecifier: Invalid peer MAC address");
}
JSONObject json;
@@ -630,10 +573,15 @@
if (peer != null) {
json.put(NETWORK_SPECIFIER_KEY_PEER_MAC, new String(HexEncoding.encode(peer)));
}
- if (token != null) {
- json.put(NETWORK_SPECIFIER_KEY_TOKEN,
- Base64.encodeToString(token, 0, token.length, Base64.DEFAULT));
+ if (pmk == null) {
+ pmk = new byte[0];
}
+ json.put(NETWORK_SPECIFIER_KEY_PMK,
+ Base64.encodeToString(pmk, 0, pmk.length, Base64.DEFAULT));
+ if (passphrase == null) {
+ passphrase = new String();
+ }
+ json.put(NETWORK_SPECIFIER_KEY_PASSPHRASE, passphrase);
} catch (JSONException e) {
return "";
}
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareSession.java b/wifi/java/android/net/wifi/aware/WifiAwareSession.java
index 8696920..895defb 100644
--- a/wifi/java/android/net/wifi/aware/WifiAwareSession.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareSession.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.os.Binder;
import android.os.Handler;
import android.os.Looper;
@@ -183,29 +184,26 @@
}
/**
- * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for a
- * WiFi Aware connection to the specified peer. The
+ * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for an
+ * unencrypted WiFi Aware connection (link) to the specified peer. The
* {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to
* {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}.
* <p>
* This API is targeted for applications which can obtain the peer MAC address using OOB
* (out-of-band) discovery. Aware discovery does not provide the MAC address of the peer -
* when using Aware discovery use the alternative network specifier method -
- * {@link DiscoverySession#createNetworkSpecifier(PeerHandle,
- * byte[])}.
+ * {@link DiscoverySession#createNetworkSpecifierOpen(PeerHandle)}.
+ * <p>
+ * To set up an encrypted link use the
+ * {@link #createNetworkSpecifierPassphrase(int, byte[], String)} API.
*
* @param role The role of this device:
* {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_INITIATOR} or
* {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_RESPONDER}
* @param peer The MAC address of the peer's Aware discovery interface. On a RESPONDER this
* value is used to gate the acceptance of a connection request from only that
- * peer. A RESPONDER may specified a null - indicating that it will accept
+ * peer. A RESPONDER may specify a {@code null} - indicating that it will accept
* connection requests from any device.
- * @param token An arbitrary token (message) to be used to match connection initiation request
- * to a responder setup. A RESPONDER is set up with a {@code token} which must
- * be matched by the token provided by the INITIATOR. A null token is permitted
- * on the RESPONDER and matches any peer token. An empty ({@code ""}) token is
- * not the same as a null token and requires the peer token to be empty as well.
*
* @return A string to be used to construct
* {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to
@@ -213,17 +211,112 @@
* android.net.ConnectivityManager.NetworkCallback)}
* [or other varieties of that API].
*/
- public String createNetworkSpecifier(@WifiAwareManager.DataPathRole int role,
- @Nullable byte[] peer, @Nullable byte[] token) {
+ public String createNetworkSpecifierOpen(@WifiAwareManager.DataPathRole int role,
+ @Nullable byte[] peer) {
WifiAwareManager mgr = mMgr.get();
if (mgr == null) {
- Log.e(TAG, "createNetworkSpecifier: called post GC on WifiAwareManager");
+ Log.e(TAG, "createNetworkSpecifierOpen: called post GC on WifiAwareManager");
return "";
}
if (mTerminated) {
- Log.e(TAG, "createNetworkSpecifier: called after termination");
+ Log.e(TAG, "createNetworkSpecifierOpen: called after termination");
return "";
}
- return mgr.createNetworkSpecifier(mClientId, role, peer, token);
+ return mgr.createNetworkSpecifier(mClientId, role, peer, null, null);
+ }
+
+ /**
+ * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for an
+ * encrypted WiFi Aware connection (link) to the specified peer. The
+ * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to
+ * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}.
+ * <p>
+ * This API is targeted for applications which can obtain the peer MAC address using OOB
+ * (out-of-band) discovery. Aware discovery does not provide the MAC address of the peer -
+ * when using Aware discovery use the alternative network specifier method -
+ * {@link DiscoverySession#createNetworkSpecifierPassphrase(PeerHandle, String)}.
+ *
+ * @param role The role of this device:
+ * {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_INITIATOR} or
+ * {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_RESPONDER}
+ * @param peer The MAC address of the peer's Aware discovery interface. On a RESPONDER this
+ * value is used to gate the acceptance of a connection request from only that
+ * peer. A RESPONDER may specify a {@code null} - indicating that it will accept
+ * connection requests from any device.
+ * @param passphrase The passphrase to be used to encrypt the link. The PMK is generated from
+ * the passphrase. Use {@link #createNetworkSpecifierOpen(int, byte[])} to
+ * specify an open (unencrypted) link.
+ *
+ * @return A string to be used to construct
+ * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to
+ * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest,
+ * android.net.ConnectivityManager.NetworkCallback)}
+ * [or other varieties of that API].
+ */
+ public String createNetworkSpecifierPassphrase(@WifiAwareManager.DataPathRole int role,
+ @Nullable byte[] peer, @NonNull String passphrase) {
+ WifiAwareManager mgr = mMgr.get();
+ if (mgr == null) {
+ Log.e(TAG, "createNetworkSpecifierPassphrase: called post GC on WifiAwareManager");
+ return "";
+ }
+ if (mTerminated) {
+ Log.e(TAG, "createNetworkSpecifierPassphrase: called after termination");
+ return "";
+ }
+ if (passphrase == null || passphrase.length() == 0) {
+ throw new IllegalArgumentException("Passphrase must not be null or empty");
+ }
+ return mgr.createNetworkSpecifier(mClientId, role, peer, null, passphrase);
+ }
+
+ /**
+ * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for an
+ * encrypted WiFi Aware connection (link) to the specified peer. The
+ * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to
+ * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}.
+ * <p>
+ * This API is targeted for applications which can obtain the peer MAC address using OOB
+ * (out-of-band) discovery. Aware discovery does not provide the MAC address of the peer -
+ * when using Aware discovery use the alternative network specifier method -
+ * {@link DiscoverySession#createNetworkSpecifierPassphrase(PeerHandle, String)}.
+ *
+ * @param role The role of this device:
+ * {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_INITIATOR} or
+ * {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_RESPONDER}
+ * @param peer The MAC address of the peer's Aware discovery interface. On a RESPONDER this
+ * value is used to gate the acceptance of a connection request from only that
+ * peer. A RESPONDER may specify a null - indicating that it will accept
+ * connection requests from any device.
+ * @param pmk A PMK (pairwise master key, see IEEE 802.11i) specifying the key to use for
+ * encrypting the data-path. Use the
+ * {@link #createNetworkSpecifierPassphrase(int, byte[], String)} to specify a
+ * Passphrase or {@link #createNetworkSpecifierOpen(int, byte[])} to specify an
+ * open (unencrypted) link.
+ *
+ * @return A string to be used to construct
+ * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to
+ * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest,
+ * android.net.ConnectivityManager.NetworkCallback)}
+ * [or other varieties of that API].
+ *
+ * @hide
+ */
+ @SystemApi
+ public String createNetworkSpecifierPmk(@WifiAwareManager.DataPathRole int role,
+ @Nullable byte[] peer, @NonNull byte[] pmk) {
+ WifiAwareManager mgr = mMgr.get();
+ if (mgr == null) {
+ Log.e(TAG, "createNetworkSpecifierPmk: called post GC on WifiAwareManager");
+ return "";
+ }
+ if (mTerminated) {
+ Log.e(TAG, "createNetworkSpecifierPmk: called after termination");
+ return "";
+ }
+ if (pmk == null || pmk.length == 0) {
+ throw new IllegalArgumentException("PMK must not be null or empty");
+ }
+ return mgr.createNetworkSpecifier(mClientId, role, peer, pmk, null);
}
}
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
index 620759d..2388841 100644
--- a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
+++ b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
@@ -122,12 +122,22 @@
private static final int MAX_PASSWORD_BYTES = 255;
/**
+ * Supported authentication methods.
+ * @hide
+ */
+ public static final String AUTH_METHOD_PAP = "PAP";
+ /** @hide */
+ public static final String AUTH_METHOD_MSCHAP = "MS-CHAP";
+ /** @hide */
+ public static final String AUTH_METHOD_MSCHAPV2 = "MS-CHAP-V2";
+
+ /**
* Supported Non-EAP inner methods. Refer to
* Credential/UsernamePassword/EAPMethod/InnerEAPType in Hotspot 2.0 Release 2 Technical
* Specification Section 9.1 for more info.
*/
- private static final Set<String> SUPPORTED_AUTH =
- new HashSet<String>(Arrays.asList("PAP", "CHAP", "MS-CHAP", "MS-CHAP-V2"));
+ private static final Set<String> SUPPORTED_AUTH = new HashSet<String>(
+ Arrays.asList(AUTH_METHOD_PAP, AUTH_METHOD_MSCHAP, AUTH_METHOD_MSCHAPV2));
/**
* Username of the credential.
@@ -348,8 +358,9 @@
public static final class CertificateCredential implements Parcelable {
/**
* Supported certificate types.
+ * @hide
*/
- private static final String CERT_TYPE_X509V3 = "x509v3";
+ public static final String CERT_TYPE_X509V3 = "x509v3";
/**
* Certificate SHA-256 fingerprint length.
diff --git a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
index 5f949747..632cfaf 100644
--- a/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiConfigurationTest.java
@@ -20,6 +20,7 @@
import static org.junit.Assert.assertEquals;
import android.os.Parcel;
+import android.net.wifi.WifiConfiguration.NetworkSelectionStatus;
import org.junit.Before;
import org.junit.Test;
@@ -66,4 +67,35 @@
assertArrayEquals(bytes, rebytes);
}
+
+ @Test
+ public void testNetworkSelectionStatusCopy() {
+ NetworkSelectionStatus networkSelectionStatus = new NetworkSelectionStatus();
+ networkSelectionStatus.setNotRecommended(true);
+
+ NetworkSelectionStatus copy = new NetworkSelectionStatus();
+ copy.copy(networkSelectionStatus);
+
+ assertEquals(networkSelectionStatus.isNotRecommended(), copy.isNotRecommended());
+ }
+
+ @Test
+ public void testNetworkSelectionStatusParcel() {
+ NetworkSelectionStatus networkSelectionStatus = new NetworkSelectionStatus();
+ networkSelectionStatus.setNotRecommended(true);
+
+ Parcel parcelW = Parcel.obtain();
+ networkSelectionStatus.writeToParcel(parcelW);
+ byte[] bytes = parcelW.marshall();
+ parcelW.recycle();
+
+ Parcel parcelR = Parcel.obtain();
+ parcelR.unmarshall(bytes, 0, bytes.length);
+ parcelR.setDataPosition(0);
+
+ NetworkSelectionStatus copy = new NetworkSelectionStatus();
+ copy.readFromParcel(parcelR);
+
+ assertEquals(networkSelectionStatus.isNotRecommended(), copy.isNotRecommended());
+ }
}
diff --git a/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java b/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java
index c4d2d32..d0aedba 100644
--- a/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java
@@ -89,11 +89,29 @@
@Test
public void testSetClientKeyEntryWithNull() {
mEnterpriseConfig.setClientKeyEntry(null, null);
- assertEquals(null, mEnterpriseConfig.getClientCertificateChain());
- assertEquals(null, mEnterpriseConfig.getClientCertificate());
+ assertNull(mEnterpriseConfig.getClientCertificateChain());
+ assertNull(mEnterpriseConfig.getClientCertificate());
mEnterpriseConfig.setClientKeyEntryWithCertificateChain(null, null);
- assertEquals(null, mEnterpriseConfig.getClientCertificateChain());
- assertEquals(null, mEnterpriseConfig.getClientCertificate());
+ assertNull(mEnterpriseConfig.getClientCertificateChain());
+ assertNull(mEnterpriseConfig.getClientCertificate());
+
+ // Setting the client certificate to null should clear the existing chain.
+ PrivateKey clientKey = FakeKeys.RSA_KEY1;
+ X509Certificate clientCert0 = FakeKeys.CLIENT_CERT;
+ X509Certificate clientCert1 = FakeKeys.CA_CERT1;
+ mEnterpriseConfig.setClientKeyEntry(clientKey, clientCert0);
+ assertNotNull(mEnterpriseConfig.getClientCertificate());
+ mEnterpriseConfig.setClientKeyEntry(null, null);
+ assertNull(mEnterpriseConfig.getClientCertificate());
+ assertNull(mEnterpriseConfig.getClientCertificateChain());
+
+ // Setting the chain to null should clear the existing chain.
+ X509Certificate[] clientChain = new X509Certificate[] {clientCert0, clientCert1};
+ mEnterpriseConfig.setClientKeyEntryWithCertificateChain(clientKey, clientChain);
+ assertNotNull(mEnterpriseConfig.getClientCertificateChain());
+ mEnterpriseConfig.setClientKeyEntryWithCertificateChain(null, null);
+ assertNull(mEnterpriseConfig.getClientCertificate());
+ assertNull(mEnterpriseConfig.getClientCertificateChain());
}
@Test
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
index a396d87..eceb365 100644
--- a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
@@ -100,26 +100,6 @@
*/
/**
- * Validate pass-through of enableUsage() API.
- */
- @Test
- public void testEnableUsage() throws Exception {
- mDut.enableUsage();
-
- verify(mockAwareService).enableUsage();
- }
-
- /**
- * Validate pass-through of disableUsage() API.
- */
- @Test
- public void testDisableUsage() throws Exception {
- mDut.disableUsage();
-
- verify(mockAwareService).disableUsage();
- }
-
- /**
* Validate pass-through of isUsageEnabled() API.
*/
@Test
@@ -566,6 +546,12 @@
collector.checkThat("mMasterPreference", 0,
equalTo(configRequest.mMasterPreference));
collector.checkThat("mSupport5gBand", false, equalTo(configRequest.mSupport5gBand));
+ collector.checkThat("mDiscoveryWindowInterval.length", 2,
+ equalTo(configRequest.mDiscoveryWindowInterval.length));
+ collector.checkThat("mDiscoveryWindowInterval[2.4GHz]", ConfigRequest.DW_INTERVAL_NOT_INIT,
+ equalTo(configRequest.mDiscoveryWindowInterval[ConfigRequest.NAN_BAND_24GHZ]));
+ collector.checkThat("mDiscoveryWindowInterval[5Hz]", ConfigRequest.DW_INTERVAL_NOT_INIT,
+ equalTo(configRequest.mDiscoveryWindowInterval[ConfigRequest.NAN_BAND_5GHZ]));
}
@Test
@@ -574,10 +560,12 @@
final int clusterLow = 5;
final int masterPreference = 55;
final boolean supportBand5g = true;
+ final int dwWindow5GHz = 3;
ConfigRequest configRequest = new ConfigRequest.Builder().setClusterHigh(clusterHigh)
.setClusterLow(clusterLow).setMasterPreference(masterPreference)
.setSupport5gBand(supportBand5g)
+ .setDiscoveryWindowInterval(ConfigRequest.NAN_BAND_5GHZ, dwWindow5GHz)
.build();
collector.checkThat("mClusterHigh", clusterHigh, equalTo(configRequest.mClusterHigh));
@@ -585,6 +573,12 @@
collector.checkThat("mMasterPreference", masterPreference,
equalTo(configRequest.mMasterPreference));
collector.checkThat("mSupport5gBand", supportBand5g, equalTo(configRequest.mSupport5gBand));
+ collector.checkThat("mDiscoveryWindowInterval.length", 2,
+ equalTo(configRequest.mDiscoveryWindowInterval.length));
+ collector.checkThat("mDiscoveryWindowInterval[2.4GHz]", ConfigRequest.DW_INTERVAL_NOT_INIT,
+ equalTo(configRequest.mDiscoveryWindowInterval[ConfigRequest.NAN_BAND_24GHZ]));
+ collector.checkThat("mDiscoveryWindowInterval[5GHz]", dwWindow5GHz,
+ equalTo(configRequest.mDiscoveryWindowInterval[ConfigRequest.NAN_BAND_5GHZ]));
}
@Test(expected = IllegalArgumentException.class)
@@ -633,16 +627,44 @@
new ConfigRequest.Builder().setClusterLow(100).setClusterHigh(5).build();
}
+ @Test(expected = IllegalArgumentException.class)
+ public void testConfigRequestBuilderDwIntervalInvalidBand() {
+ new ConfigRequest.Builder().setDiscoveryWindowInterval(5, 1).build();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testConfigRequestBuilderDwIntervalInvalidValueZero() {
+ new ConfigRequest.Builder().setDiscoveryWindowInterval(ConfigRequest.NAN_BAND_24GHZ,
+ 0).build();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testConfigRequestBuilderDwIntervalInvalidValueLarge() {
+ new ConfigRequest.Builder().setDiscoveryWindowInterval(ConfigRequest.NAN_BAND_5GHZ,
+ 6).build();
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testConfigRequestBuilderDwIntervalInvalidValueLargeValidate() {
+ ConfigRequest cr = new ConfigRequest.Builder().build();
+ cr.mDiscoveryWindowInterval[ConfigRequest.NAN_BAND_5GHZ] = 6;
+ cr.validate();
+ }
+
@Test
public void testConfigRequestParcel() {
final int clusterHigh = 189;
final int clusterLow = 25;
final int masterPreference = 177;
final boolean supportBand5g = true;
+ final int dwWindow24GHz = 1;
+ final int dwWindow5GHz = 5;
ConfigRequest configRequest = new ConfigRequest.Builder().setClusterHigh(clusterHigh)
.setClusterLow(clusterLow).setMasterPreference(masterPreference)
.setSupport5gBand(supportBand5g)
+ .setDiscoveryWindowInterval(ConfigRequest.NAN_BAND_24GHZ, dwWindow24GHz)
+ .setDiscoveryWindowInterval(ConfigRequest.NAN_BAND_5GHZ, dwWindow5GHz)
.build();
Parcel parcelW = Parcel.obtain();
@@ -951,11 +973,12 @@
final int sessionId = 123;
final PeerHandle peerHandle = new PeerHandle(123412);
final int role = WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER;
- final String token = "Some arbitrary token string - can really be anything";
+ final byte[] pmk = "Some arbitrary byte array".getBytes();
+ final String passphrase = "A really bad password";
final ConfigRequest configRequest = new ConfigRequest.Builder().build();
final PublishConfig publishConfig = new PublishConfig.Builder().build();
- String tokenB64 = Base64.encodeToString(token.getBytes(), Base64.DEFAULT);
+ String pmkB64 = Base64.encodeToString(pmk, Base64.DEFAULT);
ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
WifiAwareSession.class);
@@ -986,9 +1009,8 @@
mMockLooper.dispatchAll();
inOrder.verify(mockSessionCallback).onPublishStarted(publishSession.capture());
- // (3) request a network specifier from the session
- String networkSpecifier = publishSession.getValue().createNetworkSpecifier(peerHandle,
- token.getBytes());
+ // (3) request an open (unencrypted) network specifier from the session
+ String networkSpecifier = publishSession.getValue().createNetworkSpecifierOpen(peerHandle);
// validate format
JSONObject jsonObject = new JSONObject(networkSpecifier);
@@ -1000,8 +1022,39 @@
equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_SESSION_ID)));
collector.checkThat("peer_id", peerHandle.peerId,
equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_ID)));
- collector.checkThat("token", tokenB64,
- equalTo(jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_TOKEN)));
+
+ // (4) request an encrypted (PMK) network specifier from the session
+ networkSpecifier = publishSession.getValue().createNetworkSpecifierPmk(peerHandle, pmk);
+
+ // validate format
+ jsonObject = new JSONObject(networkSpecifier);
+ collector.checkThat("role", role,
+ equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE)));
+ collector.checkThat("client_id", clientId,
+ equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_CLIENT_ID)));
+ collector.checkThat("session_id", sessionId,
+ equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_SESSION_ID)));
+ collector.checkThat("peer_id", peerHandle.peerId,
+ equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_ID)));
+ collector.checkThat("pmk", pmkB64 ,
+ equalTo(jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PMK)));
+
+ // (5) request an encrypted (Passphrase) network specifier from the session
+ networkSpecifier = publishSession.getValue().createNetworkSpecifierPassphrase(peerHandle,
+ passphrase);
+
+ // validate format
+ jsonObject = new JSONObject(networkSpecifier);
+ collector.checkThat("role", role,
+ equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE)));
+ collector.checkThat("client_id", clientId,
+ equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_CLIENT_ID)));
+ collector.checkThat("session_id", sessionId,
+ equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_SESSION_ID)));
+ collector.checkThat("peer_id", peerHandle.peerId,
+ equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_ID)));
+ collector.checkThat("passphrase", passphrase,
+ equalTo(jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PASSPHRASE)));
verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService,
mockPublishSession, mockRttListener);
@@ -1017,9 +1070,10 @@
final ConfigRequest configRequest = new ConfigRequest.Builder().build();
final byte[] someMac = HexEncoding.decode("000102030405".toCharArray(), false);
final int role = WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR;
- final String token = "Some arbitrary token string - can really be anything";
+ final byte[] pmk = "Some arbitrary pmk data".getBytes();
+ final String passphrase = "A really bad password";
- String tokenB64 = Base64.encodeToString(token.getBytes(), Base64.DEFAULT);
+ String pmkB64 = Base64.encodeToString(pmk, Base64.DEFAULT);
ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
WifiAwareSession.class);
@@ -1038,10 +1092,10 @@
inOrder.verify(mockCallback).onAttached(sessionCaptor.capture());
WifiAwareSession session = sessionCaptor.getValue();
- /* (2) request a direct network specifier*/
- String networkSpecifier = session.createNetworkSpecifier(role, someMac, token.getBytes());
+ // (2) request an open (unencrypted) direct network specifier
+ String networkSpecifier = session.createNetworkSpecifierOpen(role, someMac);
- /* validate format*/
+ // validate format
JSONObject jsonObject = new JSONObject(networkSpecifier);
collector.checkThat("role", role,
equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE)));
@@ -1050,8 +1104,36 @@
collector.checkThat("peer_mac", someMac, equalTo(HexEncoding.decode(
jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_MAC).toCharArray(),
false)));
- collector.checkThat("token", tokenB64,
- equalTo(jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_TOKEN)));
+
+ // (3) request an encrypted (PMK) direct network specifier
+ networkSpecifier = session.createNetworkSpecifierPmk(role, someMac, pmk);
+
+ // validate format
+ jsonObject = new JSONObject(networkSpecifier);
+ collector.checkThat("role", role,
+ equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE)));
+ collector.checkThat("client_id", clientId,
+ equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_CLIENT_ID)));
+ collector.checkThat("peer_mac", someMac, equalTo(HexEncoding.decode(
+ jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_MAC).toCharArray(),
+ false)));
+ collector.checkThat("pmk", pmkB64,
+ equalTo(jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PMK)));
+
+ // (4) request an encrypted (Passphrase) direct network specifier
+ networkSpecifier = session.createNetworkSpecifierPassphrase(role, someMac, passphrase);
+
+ // validate format
+ jsonObject = new JSONObject(networkSpecifier);
+ collector.checkThat("role", role,
+ equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE)));
+ collector.checkThat("client_id", clientId,
+ equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_CLIENT_ID)));
+ collector.checkThat("peer_mac", someMac, equalTo(HexEncoding.decode(
+ jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_MAC).toCharArray(),
+ false)));
+ collector.checkThat("passphrase", passphrase,
+ equalTo(jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PASSPHRASE)));
verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService,
mockPublishSession, mockRttListener);