diff options
64 files changed, 3791 insertions, 159 deletions
diff --git a/Android.bp b/Android.bp index b0493aa53f31..2c1fce3fc725 100644 --- a/Android.bp +++ b/Android.bp @@ -453,6 +453,17 @@ java_library { "telecomm/java/com/android/internal/telecom/IInCallService.aidl", "telecomm/java/com/android/internal/telecom/ITelecomService.aidl", "telecomm/java/com/android/internal/telecom/RemoteServiceCallback.aidl", + "telephony/java/android/telephony/ims/internal/aidl/IImsCallSessionListener.aidl", + "telephony/java/android/telephony/ims/internal/aidl/IImsCapabilityCallback.aidl", + "telephony/java/android/telephony/ims/internal/aidl/IImsConfig.aidl", + "telephony/java/android/telephony/ims/internal/aidl/IImsConfigCallback.aidl", + "telephony/java/android/telephony/ims/internal/aidl/IImsMmTelFeature.aidl", + "telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl", + "telephony/java/android/telephony/ims/internal/aidl/IImsRcsFeature.aidl", + "telephony/java/android/telephony/ims/internal/aidl/IImsRegistration.aidl", + "telephony/java/android/telephony/ims/internal/aidl/IImsRegistrationCallback.aidl", + "telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl", + "telephony/java/android/telephony/ims/internal/aidl/IImsServiceControllerListener.aidl", "telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl", "telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl", "telephony/java/android/telephony/mbms/IDownloadStateCallback.aidl", @@ -534,7 +545,7 @@ java_library { ], aidl: { - local_include_dirs: [ + export_include_dirs: [ // From build/make/core/pathmap.mk FRAMEWORK_BASE_SUBDIRS "core/java", "graphics/java", diff --git a/api/current.txt b/api/current.txt index 82c51a314c15..d543ae9133a6 100644 --- a/api/current.txt +++ b/api/current.txt @@ -7909,6 +7909,94 @@ package android.bluetooth { method public void onHealthChannelStateChange(android.bluetooth.BluetoothHealthAppConfiguration, android.bluetooth.BluetoothDevice, int, int, android.os.ParcelFileDescriptor, int); } + public final class BluetoothHidDevice implements android.bluetooth.BluetoothProfile { + method public boolean connect(android.bluetooth.BluetoothDevice); + method public boolean disconnect(android.bluetooth.BluetoothDevice); + method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(); + method public int getConnectionState(android.bluetooth.BluetoothDevice); + method public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]); + method public boolean registerApp(android.bluetooth.BluetoothHidDeviceAppSdpSettings, android.bluetooth.BluetoothHidDeviceAppQosSettings, android.bluetooth.BluetoothHidDeviceAppQosSettings, android.bluetooth.BluetoothHidDeviceCallback); + method public boolean replyReport(android.bluetooth.BluetoothDevice, byte, byte, byte[]); + method public boolean reportError(android.bluetooth.BluetoothDevice, byte); + method public boolean sendReport(android.bluetooth.BluetoothDevice, int, byte[]); + method public boolean unregisterApp(); + field public static final java.lang.String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.hiddevice.profile.action.CONNECTION_STATE_CHANGED"; + field public static final byte ERROR_RSP_INVALID_PARAM = 4; // 0x4 + field public static final byte ERROR_RSP_INVALID_RPT_ID = 2; // 0x2 + field public static final byte ERROR_RSP_NOT_READY = 1; // 0x1 + field public static final byte ERROR_RSP_SUCCESS = 0; // 0x0 + field public static final byte ERROR_RSP_UNKNOWN = 14; // 0xe + field public static final byte ERROR_RSP_UNSUPPORTED_REQ = 3; // 0x3 + field public static final byte PROTOCOL_BOOT_MODE = 0; // 0x0 + field public static final byte PROTOCOL_REPORT_MODE = 1; // 0x1 + field public static final byte REPORT_TYPE_FEATURE = 3; // 0x3 + field public static final byte REPORT_TYPE_INPUT = 1; // 0x1 + field public static final byte REPORT_TYPE_OUTPUT = 2; // 0x2 + field public static final byte SUBCLASS1_COMBO = -64; // 0xffffffc0 + field public static final byte SUBCLASS1_KEYBOARD = 64; // 0x40 + field public static final byte SUBCLASS1_MOUSE = -128; // 0xffffff80 + field public static final byte SUBCLASS1_NONE = 0; // 0x0 + field public static final byte SUBCLASS2_CARD_READER = 6; // 0x6 + field public static final byte SUBCLASS2_DIGITIZER_TABLET = 5; // 0x5 + field public static final byte SUBCLASS2_GAMEPAD = 2; // 0x2 + field public static final byte SUBCLASS2_JOYSTICK = 1; // 0x1 + field public static final byte SUBCLASS2_REMOTE_CONTROL = 3; // 0x3 + field public static final byte SUBCLASS2_SENSING_DEVICE = 4; // 0x4 + field public static final byte SUBCLASS2_UNCATEGORIZED = 0; // 0x0 + } + + public final class BluetoothHidDeviceAppQosSettings implements android.os.Parcelable { + ctor public BluetoothHidDeviceAppQosSettings(int, int, int, int, int, int); + method public int describeContents(); + method public int[] toArray(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothHidDeviceAppQosSettings> CREATOR; + field public static final int MAX = -1; // 0xffffffff + field public static final int SERVICE_BEST_EFFORT = 1; // 0x1 + field public static final int SERVICE_GUARANTEED = 2; // 0x2 + field public static final int SERVICE_NO_TRAFFIC = 0; // 0x0 + field public final int delayVariation; + field public final int latency; + field public final int peakBandwidth; + field public final int serviceType; + field public final int tokenBucketSize; + field public final int tokenRate; + } + + public static class BluetoothHidDeviceAppQosSettings.Builder { + ctor public BluetoothHidDeviceAppQosSettings.Builder(); + method public android.bluetooth.BluetoothHidDeviceAppQosSettings build(); + method public android.bluetooth.BluetoothHidDeviceAppQosSettings.Builder delayVariation(int); + method public android.bluetooth.BluetoothHidDeviceAppQosSettings.Builder latency(int); + method public android.bluetooth.BluetoothHidDeviceAppQosSettings.Builder peakBandwidth(int); + method public android.bluetooth.BluetoothHidDeviceAppQosSettings.Builder serviceType(int); + method public android.bluetooth.BluetoothHidDeviceAppQosSettings.Builder tokenBucketSize(int); + method public android.bluetooth.BluetoothHidDeviceAppQosSettings.Builder tokenRate(int); + } + + public final class BluetoothHidDeviceAppSdpSettings implements android.os.Parcelable { + ctor public BluetoothHidDeviceAppSdpSettings(java.lang.String, java.lang.String, java.lang.String, byte, byte[]); + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothHidDeviceAppSdpSettings> CREATOR; + field public final java.lang.String description; + field public final byte[] descriptors; + field public final java.lang.String name; + field public final java.lang.String provider; + field public final byte subclass; + } + + public abstract class BluetoothHidDeviceCallback { + ctor public BluetoothHidDeviceCallback(); + method public void onAppStatusChanged(android.bluetooth.BluetoothDevice, boolean); + method public void onConnectionStateChanged(android.bluetooth.BluetoothDevice, int); + method public void onGetReport(android.bluetooth.BluetoothDevice, byte, byte, int); + method public void onIntrData(android.bluetooth.BluetoothDevice, byte, byte[]); + method public void onSetProtocol(android.bluetooth.BluetoothDevice, byte); + method public void onSetReport(android.bluetooth.BluetoothDevice, byte, byte, byte[]); + method public void onVirtualCableUnplug(android.bluetooth.BluetoothDevice); + } + public final class BluetoothManager { method public android.bluetooth.BluetoothAdapter getAdapter(); method public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices(int); @@ -7928,6 +8016,7 @@ package android.bluetooth { field public static final int GATT_SERVER = 8; // 0x8 field public static final int HEADSET = 1; // 0x1 field public static final int HEALTH = 3; // 0x3 + field public static final int HID_DEVICE = 19; // 0x13 field public static final int SAP = 10; // 0xa field public static final int STATE_CONNECTED = 2; // 0x2 field public static final int STATE_CONNECTING = 1; // 0x1 @@ -16280,6 +16369,17 @@ package android.icu.lang { field public static final int KNOTTED_HEH = 21; // 0x15 field public static final int LAM = 22; // 0x16 field public static final int LAMADH = 23; // 0x17 + field public static final int MALAYALAM_BHA = 89; // 0x59 + field public static final int MALAYALAM_JA = 90; // 0x5a + field public static final int MALAYALAM_LLA = 91; // 0x5b + field public static final int MALAYALAM_LLLA = 92; // 0x5c + field public static final int MALAYALAM_NGA = 93; // 0x5d + field public static final int MALAYALAM_NNA = 94; // 0x5e + field public static final int MALAYALAM_NNNA = 95; // 0x5f + field public static final int MALAYALAM_NYA = 96; // 0x60 + field public static final int MALAYALAM_RA = 97; // 0x61 + field public static final int MALAYALAM_SSA = 98; // 0x62 + field public static final int MALAYALAM_TTA = 99; // 0x63 field public static final int MANICHAEAN_ALEPH = 58; // 0x3a field public static final int MANICHAEAN_AYIN = 59; // 0x3b field public static final int MANICHAEAN_BETH = 60; // 0x3c @@ -16535,6 +16635,8 @@ package android.icu.lang { field public static final int CJK_UNIFIED_IDEOGRAPHS_EXTENSION_D_ID = 209; // 0xd1 field public static final android.icu.lang.UCharacter.UnicodeBlock CJK_UNIFIED_IDEOGRAPHS_EXTENSION_E; field public static final int CJK_UNIFIED_IDEOGRAPHS_EXTENSION_E_ID = 256; // 0x100 + field public static final android.icu.lang.UCharacter.UnicodeBlock CJK_UNIFIED_IDEOGRAPHS_EXTENSION_F; + field public static final int CJK_UNIFIED_IDEOGRAPHS_EXTENSION_F_ID = 274; // 0x112 field public static final int CJK_UNIFIED_IDEOGRAPHS_ID = 71; // 0x47 field public static final android.icu.lang.UCharacter.UnicodeBlock COMBINING_DIACRITICAL_MARKS; field public static final android.icu.lang.UCharacter.UnicodeBlock COMBINING_DIACRITICAL_MARKS_EXTENDED; @@ -16680,6 +16782,8 @@ package android.icu.lang { field public static final int JAVANESE_ID = 181; // 0xb5 field public static final android.icu.lang.UCharacter.UnicodeBlock KAITHI; field public static final int KAITHI_ID = 193; // 0xc1 + field public static final android.icu.lang.UCharacter.UnicodeBlock KANA_EXTENDED_A; + field public static final int KANA_EXTENDED_A_ID = 275; // 0x113 field public static final android.icu.lang.UCharacter.UnicodeBlock KANA_SUPPLEMENT; field public static final int KANA_SUPPLEMENT_ID = 203; // 0xcb field public static final android.icu.lang.UCharacter.UnicodeBlock KANBUN; @@ -16752,6 +16856,8 @@ package android.icu.lang { field public static final int MANICHAEAN_ID = 234; // 0xea field public static final android.icu.lang.UCharacter.UnicodeBlock MARCHEN; field public static final int MARCHEN_ID = 268; // 0x10c + field public static final android.icu.lang.UCharacter.UnicodeBlock MASARAM_GONDI; + field public static final int MASARAM_GONDI_ID = 276; // 0x114 field public static final android.icu.lang.UCharacter.UnicodeBlock MATHEMATICAL_ALPHANUMERIC_SYMBOLS; field public static final int MATHEMATICAL_ALPHANUMERIC_SYMBOLS_ID = 93; // 0x5d field public static final android.icu.lang.UCharacter.UnicodeBlock MATHEMATICAL_OPERATORS; @@ -16811,6 +16917,8 @@ package android.icu.lang { field public static final android.icu.lang.UCharacter.UnicodeBlock NO_BLOCK; field public static final android.icu.lang.UCharacter.UnicodeBlock NUMBER_FORMS; field public static final int NUMBER_FORMS_ID = 45; // 0x2d + field public static final android.icu.lang.UCharacter.UnicodeBlock NUSHU; + field public static final int NUSHU_ID = 277; // 0x115 field public static final android.icu.lang.UCharacter.UnicodeBlock OGHAM; field public static final int OGHAM_ID = 34; // 0x22 field public static final android.icu.lang.UCharacter.UnicodeBlock OLD_HUNGARIAN; @@ -16889,6 +16997,8 @@ package android.icu.lang { field public static final int SMALL_FORM_VARIANTS_ID = 84; // 0x54 field public static final android.icu.lang.UCharacter.UnicodeBlock SORA_SOMPENG; field public static final int SORA_SOMPENG_ID = 218; // 0xda + field public static final android.icu.lang.UCharacter.UnicodeBlock SOYOMBO; + field public static final int SOYOMBO_ID = 278; // 0x116 field public static final android.icu.lang.UCharacter.UnicodeBlock SPACING_MODIFIER_LETTERS; field public static final int SPACING_MODIFIER_LETTERS_ID = 6; // 0x6 field public static final android.icu.lang.UCharacter.UnicodeBlock SPECIALS; @@ -16921,6 +17031,8 @@ package android.icu.lang { field public static final int SYLOTI_NAGRI_ID = 143; // 0x8f field public static final android.icu.lang.UCharacter.UnicodeBlock SYRIAC; field public static final int SYRIAC_ID = 13; // 0xd + field public static final android.icu.lang.UCharacter.UnicodeBlock SYRIAC_SUPPLEMENT; + field public static final int SYRIAC_SUPPLEMENT_ID = 279; // 0x117 field public static final android.icu.lang.UCharacter.UnicodeBlock TAGALOG; field public static final int TAGALOG_ID = 98; // 0x62 field public static final android.icu.lang.UCharacter.UnicodeBlock TAGBANWA; @@ -16981,6 +17093,8 @@ package android.icu.lang { field public static final int YI_RADICALS_ID = 73; // 0x49 field public static final android.icu.lang.UCharacter.UnicodeBlock YI_SYLLABLES; field public static final int YI_SYLLABLES_ID = 72; // 0x48 + field public static final android.icu.lang.UCharacter.UnicodeBlock ZANABAZAR_SQUARE; + field public static final int ZANABAZAR_SQUARE_ID = 280; // 0x118 } public static abstract interface UCharacter.WordBreak { @@ -17131,6 +17245,11 @@ package android.icu.lang { field public static final int DIACRITIC = 7; // 0x7 field public static final int DOUBLE_START = 12288; // 0x3000 field public static final int EAST_ASIAN_WIDTH = 4100; // 0x1004 + field public static final int EMOJI = 57; // 0x39 + field public static final int EMOJI_COMPONENT = 61; // 0x3d + field public static final int EMOJI_MODIFIER = 59; // 0x3b + field public static final int EMOJI_MODIFIER_BASE = 60; // 0x3c + field public static final int EMOJI_PRESENTATION = 58; // 0x3a field public static final int EXTENDER = 8; // 0x8 field public static final int FULL_COMPOSITION_EXCLUSION = 9; // 0x9 field public static final int GENERAL_CATEGORY = 4101; // 0x1005 @@ -17178,8 +17297,10 @@ package android.icu.lang { field public static final int POSIX_GRAPH = 46; // 0x2e field public static final int POSIX_PRINT = 47; // 0x2f field public static final int POSIX_XDIGIT = 48; // 0x30 + field public static final int PREPENDED_CONCATENATION_MARK = 63; // 0x3f field public static final int QUOTATION_MARK = 25; // 0x19 field public static final int RADICAL = 26; // 0x1a + field public static final int REGIONAL_INDICATOR = 62; // 0x3e field public static final int SCRIPT = 4106; // 0x100a field public static final int SCRIPT_EXTENSIONS = 28672; // 0x7000 field public static final int SEGMENT_STARTER = 41; // 0x29 @@ -17321,6 +17442,7 @@ package android.icu.lang { field public static final int MANDAIC = 84; // 0x54 field public static final int MANICHAEAN = 121; // 0x79 field public static final int MARCHEN = 169; // 0xa9 + field public static final int MASARAM_GONDI = 175; // 0xaf field public static final int MATHEMATICAL_NOTATION = 128; // 0x80 field public static final int MAYAN_HIEROGLYPHS = 85; // 0x55 field public static final int MEITEI_MAYEK = 115; // 0x73 @@ -17375,6 +17497,7 @@ package android.icu.lang { field public static final int SINDHI = 145; // 0x91 field public static final int SINHALA = 33; // 0x21 field public static final int SORA_SOMPENG = 152; // 0x98 + field public static final int SOYOMBO = 176; // 0xb0 field public static final int SUNDANESE = 113; // 0x71 field public static final int SYLOTI_NAGRI = 58; // 0x3a field public static final int SYMBOLS = 129; // 0x81 @@ -17405,6 +17528,7 @@ package android.icu.lang { field public static final int WESTERN_SYRIAC = 96; // 0x60 field public static final int WOLEAI = 155; // 0x9b field public static final int YI = 41; // 0x29 + field public static final int ZANABAZAR_SQUARE = 177; // 0xb1 } public static final class UScript.ScriptUsage extends java.lang.Enum { @@ -18199,11 +18323,14 @@ package android.icu.text { method public android.icu.util.Currency getCurrency(); method public java.lang.String getCurrencySymbol(); method public char getDecimalSeparator(); + method public java.lang.String getDecimalSeparatorString(); method public char getDigit(); + method public java.lang.String[] getDigitStrings(); method public char[] getDigits(); method public java.lang.String getExponentMultiplicationSign(); method public java.lang.String getExponentSeparator(); method public char getGroupingSeparator(); + method public java.lang.String getGroupingSeparatorString(); method public java.lang.String getInfinity(); method public static android.icu.text.DecimalFormatSymbols getInstance(); method public static android.icu.text.DecimalFormatSymbols getInstance(java.util.Locale); @@ -18211,37 +18338,52 @@ package android.icu.text { method public java.lang.String getInternationalCurrencySymbol(); method public java.util.Locale getLocale(); method public char getMinusSign(); + method public java.lang.String getMinusSignString(); method public char getMonetaryDecimalSeparator(); + method public java.lang.String getMonetaryDecimalSeparatorString(); method public char getMonetaryGroupingSeparator(); + method public java.lang.String getMonetaryGroupingSeparatorString(); method public java.lang.String getNaN(); method public char getPadEscape(); method public java.lang.String getPatternForCurrencySpacing(int, boolean); method public char getPatternSeparator(); method public char getPerMill(); + method public java.lang.String getPerMillString(); method public char getPercent(); + method public java.lang.String getPercentString(); method public char getPlusSign(); + method public java.lang.String getPlusSignString(); method public char getSignificantDigit(); method public android.icu.util.ULocale getULocale(); method public char getZeroDigit(); method public void setCurrency(android.icu.util.Currency); method public void setCurrencySymbol(java.lang.String); method public void setDecimalSeparator(char); + method public void setDecimalSeparatorString(java.lang.String); method public void setDigit(char); + method public void setDigitStrings(java.lang.String[]); method public void setExponentMultiplicationSign(java.lang.String); method public void setExponentSeparator(java.lang.String); method public void setGroupingSeparator(char); + method public void setGroupingSeparatorString(java.lang.String); method public void setInfinity(java.lang.String); method public void setInternationalCurrencySymbol(java.lang.String); method public void setMinusSign(char); + method public void setMinusSignString(java.lang.String); method public void setMonetaryDecimalSeparator(char); + method public void setMonetaryDecimalSeparatorString(java.lang.String); method public void setMonetaryGroupingSeparator(char); + method public void setMonetaryGroupingSeparatorString(java.lang.String); method public void setNaN(java.lang.String); method public void setPadEscape(char); method public void setPatternForCurrencySpacing(int, boolean, java.lang.String); method public void setPatternSeparator(char); method public void setPerMill(char); + method public void setPerMillString(java.lang.String); method public void setPercent(char); + method public void setPercentString(java.lang.String); method public void setPlusSign(char); + method public void setPlusSignString(java.lang.String); method public void setSignificantDigit(char); method public void setZeroDigit(char); field public static final int CURRENCY_SPC_CURRENCY_MATCH = 0; // 0x0 @@ -18262,7 +18404,9 @@ package android.icu.text { enum_constant public static final android.icu.text.DisplayContext DIALECT_NAMES; enum_constant public static final android.icu.text.DisplayContext LENGTH_FULL; enum_constant public static final android.icu.text.DisplayContext LENGTH_SHORT; + enum_constant public static final android.icu.text.DisplayContext NO_SUBSTITUTE; enum_constant public static final android.icu.text.DisplayContext STANDARD_NAMES; + enum_constant public static final android.icu.text.DisplayContext SUBSTITUTE; } public static final class DisplayContext.Type extends java.lang.Enum { @@ -18271,6 +18415,7 @@ package android.icu.text { enum_constant public static final android.icu.text.DisplayContext.Type CAPITALIZATION; enum_constant public static final android.icu.text.DisplayContext.Type DIALECT_HANDLING; enum_constant public static final android.icu.text.DisplayContext.Type DISPLAY_LENGTH; + enum_constant public static final android.icu.text.DisplayContext.Type SUBSTITUTE_HANDLING; } public abstract class IDNA { @@ -18378,6 +18523,7 @@ package android.icu.text { method public static android.icu.text.MeasureFormat getInstance(java.util.Locale, android.icu.text.MeasureFormat.FormatWidth, android.icu.text.NumberFormat); method public final android.icu.util.ULocale getLocale(); method public android.icu.text.NumberFormat getNumberFormat(); + method public java.lang.String getUnitDisplayName(android.icu.util.MeasureUnit); method public android.icu.text.MeasureFormat.FormatWidth getWidth(); method public final int hashCode(); method public android.icu.util.Measure parseObject(java.lang.String, java.text.ParsePosition); @@ -19916,6 +20062,7 @@ package android.icu.util { field public static final android.icu.util.MeasureUnit MILLILITER; field public static final android.icu.util.MeasureUnit MILLIMETER; field public static final android.icu.util.MeasureUnit MILLIMETER_OF_MERCURY; + field public static final android.icu.util.MeasureUnit MILLIMOLE_PER_LITER; field public static final android.icu.util.MeasureUnit MILLISECOND; field public static final android.icu.util.MeasureUnit MILLIWATT; field public static final android.icu.util.TimeUnit MINUTE; @@ -19927,6 +20074,7 @@ package android.icu.util { field public static final android.icu.util.MeasureUnit OUNCE; field public static final android.icu.util.MeasureUnit OUNCE_TROY; field public static final android.icu.util.MeasureUnit PARSEC; + field public static final android.icu.util.MeasureUnit PART_PER_MILLION; field public static final android.icu.util.MeasureUnit PICOMETER; field public static final android.icu.util.MeasureUnit PINT; field public static final android.icu.util.MeasureUnit PINT_METRIC; @@ -20050,6 +20198,9 @@ package android.icu.util { public static final class TimeZone.SystemTimeZoneType extends java.lang.Enum { method public static android.icu.util.TimeZone.SystemTimeZoneType valueOf(java.lang.String); method public static final android.icu.util.TimeZone.SystemTimeZoneType[] values(); + enum_constant public static final android.icu.util.TimeZone.SystemTimeZoneType ANY; + enum_constant public static final android.icu.util.TimeZone.SystemTimeZoneType CANONICAL; + enum_constant public static final android.icu.util.TimeZone.SystemTimeZoneType CANONICAL_LOCATION; } public final class ULocale implements java.lang.Comparable java.io.Serializable { @@ -20251,6 +20402,7 @@ package android.icu.util { field public static final android.icu.util.VersionInfo ICU_VERSION; field public static final android.icu.util.VersionInfo UCOL_BUILDER_VERSION; field public static final android.icu.util.VersionInfo UCOL_RUNTIME_VERSION; + field public static final android.icu.util.VersionInfo UNICODE_10_0; field public static final android.icu.util.VersionInfo UNICODE_1_0; field public static final android.icu.util.VersionInfo UNICODE_1_0_1; field public static final android.icu.util.VersionInfo UNICODE_1_1_0; @@ -25749,6 +25901,23 @@ package android.net { enum_constant public static final android.net.LocalSocketAddress.Namespace RESERVED; } + public final class MacAddress implements android.os.Parcelable { + method public int addressType(); + method public int describeContents(); + method public static android.net.MacAddress fromBytes(byte[]); + method public static android.net.MacAddress fromString(java.lang.String); + method public boolean isLocallyAssigned(); + method public byte[] toByteArray(); + method public java.lang.String toOuiString(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.net.MacAddress BROADCAST_ADDRESS; + field public static final android.os.Parcelable.Creator<android.net.MacAddress> CREATOR; + field public static final int TYPE_BROADCAST = 3; // 0x3 + field public static final int TYPE_MULTICAST = 2; // 0x2 + field public static final int TYPE_UNICAST = 1; // 0x1 + field public static final int TYPE_UNKNOWN = 0; // 0x0 + } + public class MailTo { method public java.lang.String getBody(); method public java.lang.String getCc(); @@ -25946,6 +26115,7 @@ package android.net { public class TrafficStats { ctor public TrafficStats(); method public static void clearThreadStatsTag(); + method public static void clearThreadStatsUid(); method public static int getAndSetThreadStatsTag(int); method public static long getMobileRxBytes(); method public static long getMobileRxPackets(); @@ -25971,9 +26141,12 @@ package android.net { method public static void incrementOperationCount(int); method public static void incrementOperationCount(int, int); method public static void setThreadStatsTag(int); + method public static void setThreadStatsUidSelf(); method public static void tagDatagramSocket(java.net.DatagramSocket) throws java.net.SocketException; + method public static void tagFileDescriptor(java.io.FileDescriptor) throws java.io.IOException; method public static void tagSocket(java.net.Socket) throws java.net.SocketException; method public static void untagDatagramSocket(java.net.DatagramSocket) throws java.net.SocketException; + method public static void untagFileDescriptor(java.io.FileDescriptor) throws java.io.IOException; method public static void untagSocket(java.net.Socket) throws java.net.SocketException; field public static final int UNSUPPORTED = -1; // 0xffffffff } @@ -38266,7 +38439,6 @@ package android.system { method public static void remove(java.lang.String) throws android.system.ErrnoException; method public static void removexattr(java.lang.String, java.lang.String) throws android.system.ErrnoException; method public static void rename(java.lang.String, java.lang.String) throws android.system.ErrnoException; - method public static deprecated long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.util.MutableLong, long) throws android.system.ErrnoException; method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.system.Int64Ref, long) throws android.system.ErrnoException; method public static int sendto(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException; method public static int sendto(java.io.FileDescriptor, byte[], int, int, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException; @@ -38292,7 +38464,6 @@ package android.system { method public static int umask(int); method public static android.system.StructUtsname uname(); method public static void unsetenv(java.lang.String) throws android.system.ErrnoException; - method public static deprecated int waitpid(int, android.util.MutableInt, int) throws android.system.ErrnoException; method public static int waitpid(int, android.system.Int32Ref, int) throws android.system.ErrnoException; method public static int write(java.io.FileDescriptor, java.nio.ByteBuffer) throws android.system.ErrnoException, java.io.InterruptedIOException; method public static int write(java.io.FileDescriptor, byte[], int, int) throws android.system.ErrnoException, java.io.InterruptedIOException; @@ -38928,6 +39099,7 @@ package android.telecom { field public static final int HANDOVER_FAILURE_DEST_INVALID_PERM = 3; // 0x3 field public static final int HANDOVER_FAILURE_DEST_NOT_SUPPORTED = 2; // 0x2 field public static final int HANDOVER_FAILURE_DEST_USER_REJECTED = 4; // 0x4 + field public static final int HANDOVER_FAILURE_ONGOING_EMERG_CALL = 5; // 0x5 } public static class Call.Details { @@ -39662,6 +39834,7 @@ package android.telephony { public class CarrierConfigManager { method public android.os.PersistableBundle getConfig(); method public android.os.PersistableBundle getConfigForSubId(int); + method public static boolean isConfigForIdentifiedCarrier(android.os.PersistableBundle); method public void notifyConfigChangedForSubId(int); field public static final java.lang.String ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED"; field public static final int DATA_CYCLE_THRESHOLD_DISABLED = -2; // 0xfffffffe @@ -54409,7 +54582,6 @@ package java.lang { method public java.lang.Object[] getSigners(); method public java.lang.String getSimpleName(); method public java.lang.Class<? super T> getSuperclass(); - method public java.lang.String getTypeName(); method public synchronized java.lang.reflect.TypeVariable<java.lang.Class<T>>[] getTypeParameters(); method public boolean isAnnotation(); method public boolean isAnonymousClass(); @@ -56070,6 +56242,11 @@ package java.lang.reflect { ctor public MalformedParameterizedTypeException(); } + public class MalformedParametersException extends java.lang.RuntimeException { + ctor public MalformedParametersException(); + ctor public MalformedParametersException(java.lang.String); + } + public abstract interface Member { method public abstract java.lang.Class<?> getDeclaringClass(); method public abstract int getModifiers(); @@ -56167,6 +56344,7 @@ package java.lang.reflect { } public abstract interface Type { + method public default java.lang.String getTypeName(); } public abstract interface TypeVariable<D extends java.lang.reflect.GenericDeclaration> implements java.lang.reflect.Type { diff --git a/api/test-current.txt b/api/test-current.txt index d4beaca2e7dd..75ff43731b0d 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -253,6 +253,13 @@ package android.net { field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0 } + public class TrafficStats { + method public static long getLoopbackRxBytes(); + method public static long getLoopbackRxPackets(); + method public static long getLoopbackTxBytes(); + method public static long getLoopbackTxPackets(); + } + } package android.os { diff --git a/core/java/android/app/LauncherActivity.java b/core/java/android/app/LauncherActivity.java index 9ec7f4134a30..88e235660c09 100644 --- a/core/java/android/app/LauncherActivity.java +++ b/core/java/android/app/LauncherActivity.java @@ -166,7 +166,7 @@ public abstract class LauncherActivity extends ListActivity { if (item.icon == null) { item.icon = mIconResizer.createIconThumbnail(item.resolveInfo.loadIcon(getPackageManager())); } - text.setCompoundDrawablesWithIntrinsicBounds(item.icon, null, null, null); + text.setCompoundDrawablesRelativeWithIntrinsicBounds(item.icon, null, null, null); } } diff --git a/core/java/android/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java index 330a0bfad329..f38e462eab4f 100644 --- a/core/java/android/bluetooth/BluetoothHidDevice.java +++ b/core/java/android/bluetooth/BluetoothHidDevice.java @@ -35,8 +35,6 @@ import java.util.List; * * <p>BluetoothHidDevice is a proxy object for controlling the Bluetooth HID Device Service via IPC. * Use {@link BluetoothAdapter#getProfileProxy} to get the BluetoothHidDevice proxy object. - * - * <p>{@hide} */ public final class BluetoothHidDevice implements BluetoothProfile { @@ -79,7 +77,7 @@ public final class BluetoothHidDevice implements BluetoothProfile { public static final byte SUBCLASS2_GAMEPAD = (byte) 0x02; public static final byte SUBCLASS2_REMOTE_CONTROL = (byte) 0x03; public static final byte SUBCLASS2_SENSING_DEVICE = (byte) 0x04; - public static final byte SUBCLASS2_DIGITIZER_TABLED = (byte) 0x05; + public static final byte SUBCLASS2_DIGITIZER_TABLET = (byte) 0x05; public static final byte SUBCLASS2_CARD_READER = (byte) 0x06; /** diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java b/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java index 4609d52df0c0..c05df2d23e45 100644 --- a/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java +++ b/core/java/android/bluetooth/BluetoothHidDeviceAppQosSettings.java @@ -26,8 +26,6 @@ import android.os.Parcelable; * registration. * * <p>{@see BluetoothHidDevice} - * - * <p>{@hide} */ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { @@ -46,10 +44,8 @@ public final class BluetoothHidDeviceAppQosSettings implements Parcelable { /** * Create a BluetoothHidDeviceAppQosSettings object for the Bluetooth L2CAP channel. The QoS - * Settings is optional. Recommended to use BluetoothHidDeviceAppQosSettings.Builder. {@see <a - * href="https://www.bluetooth.com/specifications/profiles-overview"> - * https://www.bluetooth.com/specifications/profiles-overview </a> Bluetooth HID Specfication - * v1.1.1 Section 5.2 and Appendix D } + * Settings is optional. Recommended to use BluetoothHidDeviceAppQosSettings.Builder. + * Please refer to Bluetooth HID Specfication v1.1.1 Section 5.2 and Appendix D for parameters. * * @param serviceType L2CAP service type * @param tokenRate L2CAP token rate diff --git a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java index 2da64e5a5023..562c559eddc4 100644 --- a/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java +++ b/core/java/android/bluetooth/BluetoothHidDeviceAppSdpSettings.java @@ -28,8 +28,6 @@ import java.util.Arrays; * Android device can be discovered as a Bluetooth HID Device. * * <p>{@see BluetoothHidDevice} - * - * <p>{@hide} */ public final class BluetoothHidDeviceAppSdpSettings implements Parcelable { diff --git a/core/java/android/bluetooth/BluetoothHidDeviceCallback.java b/core/java/android/bluetooth/BluetoothHidDeviceCallback.java index 6ed19654b4c5..bd19955b4e62 100644 --- a/core/java/android/bluetooth/BluetoothHidDeviceCallback.java +++ b/core/java/android/bluetooth/BluetoothHidDeviceCallback.java @@ -24,8 +24,6 @@ import android.util.Log; * registration. * * <p>{@see BluetoothHidDevice} - * - * <p>{@hide} */ public abstract class BluetoothHidDeviceCallback { diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java index ebbc710922c2..df2028a55351 100644 --- a/core/java/android/bluetooth/BluetoothProfile.java +++ b/core/java/android/bluetooth/BluetoothProfile.java @@ -153,8 +153,6 @@ public interface BluetoothProfile { /** * HID Device - * - * @hide */ public static final int HID_DEVICE = 19; diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl index 91801127fd4a..95e7f6031c72 100644 --- a/core/java/android/net/INetworkStatsService.aidl +++ b/core/java/android/net/INetworkStatsService.aidl @@ -67,4 +67,13 @@ interface INetworkStatsService { /** Unregisters a callback on data usage. */ void unregisterUsageRequest(in DataUsageRequest request); + /** Get the uid stats information since boot */ + long getUidStats(int uid, int type); + + /** Get the iface stats information since boot */ + long getIfaceStats(String iface, int type); + + /** Get the total network stats information since boot */ + long getTotalStats(int type); + } diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java index 5620a627df7f..d6992aaede5f 100644 --- a/core/java/android/net/MacAddress.java +++ b/core/java/android/net/MacAddress.java @@ -33,8 +33,6 @@ import java.util.Random; * * This class only supports 48 bits long addresses and does not support 64 bits long addresses. * Instances of this class are immutable. - * - * @hide */ public final class MacAddress implements Parcelable { @@ -132,11 +130,12 @@ public final class MacAddress implements Parcelable { } /** - * @return a String representation of the OUI part of this MacAddres, - * with the lower 3 bytes constituting the NIC part replaced with 0. + * @return a String representation of the OUI part of this MacAddress made of 3 hexadecimal + * numbers in [0,ff] joined by ':' characters. */ - public String toSafeString() { - return stringAddrFromLongAddr(mAddr & OUI_MASK); + public String toOuiString() { + return String.format( + "%02x:%02x:%02x", (mAddr >> 40) & 0xff, (mAddr >> 32) & 0xff, (mAddr >> 24) & 0xff); } @Override diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java index c339856f4388..196a3bc9c8d7 100644 --- a/core/java/android/net/TrafficStats.java +++ b/core/java/android/net/TrafficStats.java @@ -17,7 +17,9 @@ package android.net; import android.annotation.RequiresPermission; +import android.annotation.SuppressLint; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.app.DownloadManager; import android.app.backup.BackupManager; import android.app.usage.NetworkStatsManager; @@ -30,6 +32,8 @@ import com.android.server.NetworkManagementSocketTagger; import dalvik.system.SocketTagger; +import java.io.FileDescriptor; +import java.io.IOException; import java.net.DatagramSocket; import java.net.Socket; import java.net.SocketException; @@ -151,6 +155,8 @@ public class TrafficStats { private static Object sProfilingLock = new Object(); + private static final String LOOPBACK_IFACE = "lo"; + /** * Set active tag to use when accounting {@link Socket} traffic originating * from the current thread. Only one active tag per thread is supported. @@ -264,14 +270,25 @@ public class TrafficStats { } /** + * Set specific UID to use when accounting {@link Socket} traffic + * originating from the current thread as the calling UID. Designed for use + * when another application is performing operations on your behalf. + * <p> + * Changes only take effect during subsequent calls to + * {@link #tagSocket(Socket)}. + */ + public static void setThreadStatsUidSelf() { + setThreadStatsUid(android.os.Process.myUid()); + } + + /** * Clear any active UID set to account {@link Socket} traffic originating * from the current thread. * * @see #setThreadStatsUid(int) - * @hide */ @SystemApi - @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) + @SuppressLint("Doclava125") public static void clearThreadStatsUid() { NetworkManagementSocketTagger.setThreadSocketStatsUid(-1); } @@ -316,6 +333,27 @@ public class TrafficStats { } /** + * Tag the given {@link FileDescriptor} socket with any statistics + * parameters active for the current thread. Subsequent calls always replace + * any existing parameters. When finished, call + * {@link #untagFileDescriptor(FileDescriptor)} to remove statistics + * parameters. + * + * @see #setThreadStatsTag(int) + */ + public static void tagFileDescriptor(FileDescriptor fd) throws IOException { + SocketTagger.get().tag(fd); + } + + /** + * Remove any statistics parameters from the given {@link FileDescriptor} + * socket. + */ + public static void untagFileDescriptor(FileDescriptor fd) throws IOException { + SocketTagger.get().untag(fd); + } + + /** * Start profiling data usage for current UID. Only one profiling session * can be active at a time. * @@ -467,7 +505,12 @@ public class TrafficStats { public static long getMobileTcpRxPackets() { long total = 0; for (String iface : getMobileIfaces()) { - final long stat = nativeGetIfaceStat(iface, TYPE_TCP_RX_PACKETS); + long stat = UNSUPPORTED; + try { + stat = getStatsService().getIfaceStats(iface, TYPE_TCP_RX_PACKETS); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } if (stat != UNSUPPORTED) { total += stat; } @@ -479,7 +522,12 @@ public class TrafficStats { public static long getMobileTcpTxPackets() { long total = 0; for (String iface : getMobileIfaces()) { - final long stat = nativeGetIfaceStat(iface, TYPE_TCP_TX_PACKETS); + long stat = UNSUPPORTED; + try { + stat = getStatsService().getIfaceStats(iface, TYPE_TCP_TX_PACKETS); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } if (stat != UNSUPPORTED) { total += stat; } @@ -489,22 +537,78 @@ public class TrafficStats { /** {@hide} */ public static long getTxPackets(String iface) { - return nativeGetIfaceStat(iface, TYPE_TX_PACKETS); + try { + return getStatsService().getIfaceStats(iface, TYPE_TX_PACKETS); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } /** {@hide} */ public static long getRxPackets(String iface) { - return nativeGetIfaceStat(iface, TYPE_RX_PACKETS); + try { + return getStatsService().getIfaceStats(iface, TYPE_RX_PACKETS); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } /** {@hide} */ public static long getTxBytes(String iface) { - return nativeGetIfaceStat(iface, TYPE_TX_BYTES); + try { + return getStatsService().getIfaceStats(iface, TYPE_TX_BYTES); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } /** {@hide} */ public static long getRxBytes(String iface) { - return nativeGetIfaceStat(iface, TYPE_RX_BYTES); + try { + return getStatsService().getIfaceStats(iface, TYPE_RX_BYTES); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** {@hide} */ + @TestApi + public static long getLoopbackTxPackets() { + try { + return getStatsService().getIfaceStats(LOOPBACK_IFACE, TYPE_TX_PACKETS); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** {@hide} */ + @TestApi + public static long getLoopbackRxPackets() { + try { + return getStatsService().getIfaceStats(LOOPBACK_IFACE, TYPE_RX_PACKETS); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** {@hide} */ + @TestApi + public static long getLoopbackTxBytes() { + try { + return getStatsService().getIfaceStats(LOOPBACK_IFACE, TYPE_TX_BYTES); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + /** {@hide} */ + @TestApi + public static long getLoopbackRxBytes() { + try { + return getStatsService().getIfaceStats(LOOPBACK_IFACE, TYPE_RX_BYTES); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } /** @@ -517,7 +621,11 @@ public class TrafficStats { * return {@link #UNSUPPORTED} on devices where statistics aren't available. */ public static long getTotalTxPackets() { - return nativeGetTotalStat(TYPE_TX_PACKETS); + try { + return getStatsService().getTotalStats(TYPE_TX_PACKETS); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } /** @@ -530,7 +638,11 @@ public class TrafficStats { * return {@link #UNSUPPORTED} on devices where statistics aren't available. */ public static long getTotalRxPackets() { - return nativeGetTotalStat(TYPE_RX_PACKETS); + try { + return getStatsService().getTotalStats(TYPE_RX_PACKETS); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } /** @@ -543,7 +655,11 @@ public class TrafficStats { * return {@link #UNSUPPORTED} on devices where statistics aren't available. */ public static long getTotalTxBytes() { - return nativeGetTotalStat(TYPE_TX_BYTES); + try { + return getStatsService().getTotalStats(TYPE_TX_BYTES); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } /** @@ -556,7 +672,11 @@ public class TrafficStats { * return {@link #UNSUPPORTED} on devices where statistics aren't available. */ public static long getTotalRxBytes() { - return nativeGetTotalStat(TYPE_RX_BYTES); + try { + return getStatsService().getTotalStats(TYPE_RX_BYTES); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } /** @@ -582,7 +702,11 @@ public class TrafficStats { // unsupported value. The real filtering is done at the kernel level. final int callingUid = android.os.Process.myUid(); if (callingUid == android.os.Process.SYSTEM_UID || callingUid == uid) { - return nativeGetUidStat(uid, TYPE_TX_BYTES); + try { + return getStatsService().getUidStats(uid, TYPE_TX_BYTES); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } else { return UNSUPPORTED; } @@ -611,7 +735,11 @@ public class TrafficStats { // unsupported value. The real filtering is done at the kernel level. final int callingUid = android.os.Process.myUid(); if (callingUid == android.os.Process.SYSTEM_UID || callingUid == uid) { - return nativeGetUidStat(uid, TYPE_RX_BYTES); + try { + return getStatsService().getUidStats(uid, TYPE_RX_BYTES); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } else { return UNSUPPORTED; } @@ -640,7 +768,11 @@ public class TrafficStats { // unsupported value. The real filtering is done at the kernel level. final int callingUid = android.os.Process.myUid(); if (callingUid == android.os.Process.SYSTEM_UID || callingUid == uid) { - return nativeGetUidStat(uid, TYPE_TX_PACKETS); + try { + return getStatsService().getUidStats(uid, TYPE_TX_PACKETS); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } else { return UNSUPPORTED; } @@ -669,7 +801,11 @@ public class TrafficStats { // unsupported value. The real filtering is done at the kernel level. final int callingUid = android.os.Process.myUid(); if (callingUid == android.os.Process.SYSTEM_UID || callingUid == uid) { - return nativeGetUidStat(uid, TYPE_RX_PACKETS); + try { + return getStatsService().getUidStats(uid, TYPE_RX_PACKETS); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } else { return UNSUPPORTED; } @@ -797,8 +933,4 @@ public class TrafficStats { private static final int TYPE_TX_PACKETS = 3; private static final int TYPE_TCP_RX_PACKETS = 4; private static final int TYPE_TCP_TX_PACKETS = 5; - - private static native long nativeGetTotalStat(int type); - private static native long nativeGetIfaceStat(String iface, int type); - private static native long nativeGetUidStat(int uid, int type); } diff --git a/core/java/android/util/MutableInt.java b/core/java/android/util/MutableInt.java new file mode 100644 index 000000000000..a3d8606d916a --- /dev/null +++ b/core/java/android/util/MutableInt.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2011 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.util; + +/** + */ +public final class MutableInt { + public int value; + + public MutableInt(int value) { + this.value = value; + } +} diff --git a/core/java/android/util/MutableLong.java b/core/java/android/util/MutableLong.java new file mode 100644 index 000000000000..575068ea9364 --- /dev/null +++ b/core/java/android/util/MutableLong.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2011 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.util; + +/** + */ +public final class MutableLong { + public long value; + + public MutableLong(long value) { + this.value = value; + } +} diff --git a/core/jni/Android.bp b/core/jni/Android.bp index 336fee12a5af..551d54ab9053 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -96,7 +96,6 @@ cc_library_shared { "android_os_VintfRuntimeInfo.cpp", "android_net_LocalSocketImpl.cpp", "android_net_NetUtils.cpp", - "android_net_TrafficStats.cpp", "android_nio_utils.cpp", "android_util_AssetManager.cpp", "android_util_Binder.cpp", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 90598626cbe8..047fa8489453 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -173,7 +173,6 @@ extern int register_android_os_MemoryFile(JNIEnv* env); extern int register_android_os_SharedMemory(JNIEnv* env); extern int register_android_net_LocalSocketImpl(JNIEnv* env); extern int register_android_net_NetworkUtils(JNIEnv* env); -extern int register_android_net_TrafficStats(JNIEnv* env); extern int register_android_text_AndroidCharacter(JNIEnv *env); extern int register_android_text_StaticLayout(JNIEnv *env); extern int register_android_text_AndroidBidi(JNIEnv *env); @@ -643,6 +642,7 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote) char methodTraceFileBuf[sizeof("-Xmethod-trace-file:") + PROPERTY_VALUE_MAX]; char methodTraceFileSizeBuf[sizeof("-Xmethod-trace-file-size:") + PROPERTY_VALUE_MAX]; std::string fingerprintBuf; + char jdwpProviderBuf[sizeof("-XjdwpProvider:") - 1 + PROPERTY_VALUE_MAX]; bool checkJni = false; property_get("dalvik.vm.checkjni", propBuf, ""); @@ -765,9 +765,15 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote) * Set suspend=y to pause during VM init and use android ADB transport. */ if (zygote) { - addOption("-agentlib:jdwp=transport=dt_android_adb,suspend=n,server=y"); + addOption("-XjdwpOptions:suspend=n,server=y"); } + // Set the JDWP provider. By default let the runtime choose. + parseRuntimeOption("dalvik.vm.jdwp-provider", + jdwpProviderBuf, + "-XjdwpProvider:", + "default"); + parseRuntimeOption("dalvik.vm.lockprof.threshold", lockProfThresholdBuf, "-Xlockprofthreshold:"); @@ -1419,7 +1425,6 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_os_UEventObserver), REG_JNI(register_android_net_LocalSocketImpl), REG_JNI(register_android_net_NetworkUtils), - REG_JNI(register_android_net_TrafficStats), REG_JNI(register_android_os_MemoryFile), REG_JNI(register_android_os_SharedMemory), REG_JNI(register_com_android_internal_os_ClassLoaderFactory), diff --git a/core/res/res/layout/activity_list_item_2.xml b/core/res/res/layout/activity_list_item_2.xml index 608e9861dfbe..af1963c7c39a 100644 --- a/core/res/res/layout/activity_list_item_2.xml +++ b/core/res/res/layout/activity_list_item_2.xml @@ -21,5 +21,6 @@ android:textAppearance="?attr/textAppearanceListItemSmall" android:gravity="center_vertical" android:drawablePadding="14dip" + android:textAlignment="viewStart" android:paddingStart="?attr/listPreferredItemPaddingStart" android:paddingEnd="?attr/listPreferredItemPaddingEnd" /> diff --git a/core/tests/packagemanagertests/Android.mk b/core/tests/packagemanagertests/Android.mk index c1e8c981579c..5bfde78c424a 100644 --- a/core/tests/packagemanagertests/Android.mk +++ b/core/tests/packagemanagertests/Android.mk @@ -10,7 +10,8 @@ LOCAL_SRC_FILES := \ LOCAL_STATIC_JAVA_LIBRARIES := \ android-support-test \ - frameworks-base-testutils + frameworks-base-testutils \ + mockito-target-minus-junit4 LOCAL_JAVA_LIBRARIES := android.test.runner LOCAL_PACKAGE_NAME := FrameworksCorePackageManagerTests diff --git a/libs/hwui/tests/macrobench/main.cpp b/libs/hwui/tests/macrobench/main.cpp index 1f5622252f6a..616558f29676 100644 --- a/libs/hwui/tests/macrobench/main.cpp +++ b/libs/hwui/tests/macrobench/main.cpp @@ -17,24 +17,23 @@ #include "tests/common/LeakChecker.h" #include "tests/common/TestScene.h" +#include "Properties.h" #include "hwui/Typeface.h" #include "protos/hwui.pb.h" -#include "Properties.h" #include <benchmark/benchmark.h> -#include <../src/sysinfo.h> #include <getopt.h> +#include <pthread.h> #include <stdio.h> -#include <string> #include <unistd.h> +#include <string> #include <unordered_map> #include <vector> -#include <pthread.h> -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> #include <errno.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> using namespace android; using namespace android::uirenderer; @@ -320,9 +319,6 @@ int main(int argc, char* argv[]) { name_field_width += 5; benchmark::BenchmarkReporter::Context context; - context.num_cpus = benchmark::NumCPUs(); - context.mhz_per_cpu = benchmark::CyclesPerSecond() / 1000000.0f; - context.cpu_scaling_enabled = benchmark::CpuScalingEnabled(); context.name_field_width = name_field_width; gBenchmarkReporter->ReportContext(context); } 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 e628b6825208..fadb76d80fe5 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java @@ -16,10 +16,18 @@ package com.android.mediaframeworktest.integration; +import static android.hardware.camera2.CameraDevice.TEMPLATE_PREVIEW; + +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyLong; +import static org.mockito.Mockito.argThat; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.verify; + import android.graphics.ImageFormat; import android.graphics.SurfaceTexture; import android.hardware.ICameraService; -import android.hardware.camera2.CameraMetadata; import android.hardware.camera2.CameraCaptureSession; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CaptureRequest; @@ -41,13 +49,10 @@ import android.test.suitebuilder.annotation.SmallTest; import android.util.Log; import android.view.Surface; -import static android.hardware.camera2.CameraDevice.TEMPLATE_PREVIEW; - import com.android.mediaframeworktest.MediaFrameworkIntegrationTestRunner; import org.mockito.ArgumentCaptor; -import org.mockito.compat.ArgumentMatcher; -import static org.mockito.Mockito.*; +import org.mockito.ArgumentMatcher; public class CameraDeviceBinderTest extends AndroidTestCase { private static String TAG = "CameraDeviceBinderTest"; @@ -166,10 +171,10 @@ public class CameraDeviceBinderTest extends AndroidTestCase { } } - class IsMetadataNotEmpty extends ArgumentMatcher<CameraMetadataNative> { + class IsMetadataNotEmpty implements ArgumentMatcher<CameraMetadataNative> { @Override - public boolean matchesObject(Object obj) { - return !((CameraMetadataNative) obj).isEmpty(); + public boolean matches(CameraMetadataNative obj) { + return !obj.isEmpty(); } } diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java index 712039decc83..74bf1a20304a 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/MediaInserterTest.java @@ -16,13 +16,13 @@ package com.android.mediaframeworktest.unit; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.argThat; +import static org.mockito.Matchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import static org.mockito.Matchers.argThat; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; import android.content.ContentProviderClient; import android.content.ContentValues; @@ -36,10 +36,9 @@ import android.provider.MediaStore.Video; import android.test.InstrumentationTestCase; import android.test.suitebuilder.annotation.SmallTest; -import org.hamcrest.Description; +import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.mockito.compat.ArgumentMatcher; public class MediaInserterTest extends InstrumentationTestCase { @@ -59,7 +58,7 @@ public class MediaInserterTest extends InstrumentationTestCase { private static final Uri sImagesUri = Images.Media.getContentUri(sVolumeName); private static final Uri sFilesUri = Files.getContentUri(sVolumeName); - private static class MediaUriMatcher extends ArgumentMatcher<Uri> { + private static class MediaUriMatcher implements ArgumentMatcher<Uri> { private final Uri mUri; private MediaUriMatcher(Uri uri) { @@ -67,15 +66,8 @@ public class MediaInserterTest extends InstrumentationTestCase { } @Override - public boolean matchesObject(Object argument) { - if (!(argument instanceof Uri)) { - return false; - } - - Uri actualUri = (Uri) argument; - if (actualUri == mUri) - return true; - return false; + public boolean matches(Uri actualUri) { + return actualUri == mUri; } @Override diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java index 820231ef76c4..b011c6738ee9 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/AppRestrictionsHelperTest.java @@ -45,9 +45,9 @@ import android.test.suitebuilder.annotation.SmallTest; import android.view.inputmethod.InputMethodInfo; import com.android.settingslib.BaseTest; +import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.mockito.compat.ArgumentMatcher; import java.util.ArrayList; import java.util.List; @@ -241,7 +241,7 @@ public class AppRestrictionsHelperTest extends BaseTest { return ri; } - private class IntentMatcher extends ArgumentMatcher<Intent> { + private class IntentMatcher implements ArgumentMatcher<Intent> { private final Intent mIntent; IntentMatcher(Intent intent) { @@ -249,11 +249,8 @@ public class AppRestrictionsHelperTest extends BaseTest { } @Override - public boolean matchesObject(Object argument) { - if (argument instanceof Intent) { - return ((Intent) argument).filterEquals(mIntent); - } - return false; + public boolean matches(Intent argument) { + return argument != null && argument.filterEquals(mIntent); } @Override diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java index e9eb3b3c469d..d3ab1259c9ed 100644 --- a/services/core/java/com/android/server/IpSecService.java +++ b/services/core/java/com/android/server/IpSecService.java @@ -34,6 +34,7 @@ import android.net.IpSecTransform; import android.net.IpSecTransformResponse; import android.net.IpSecUdpEncapResponse; import android.net.NetworkUtils; +import android.net.TrafficStats; import android.net.util.NetdService; import android.os.Binder; import android.os.IBinder; @@ -120,6 +121,7 @@ public class IpSecService extends IIpSecService.Stub { } private final IpSecServiceConfiguration mSrvConfig; + final UidFdTagger mUidFdTagger; /** * Interface for user-reference and kernel-resource cleanup. @@ -762,8 +764,23 @@ public class IpSecService extends IIpSecService.Stub { /** @hide */ @VisibleForTesting public IpSecService(Context context, IpSecServiceConfiguration config) { + this(context, config, (fd, uid) -> { + try{ + TrafficStats.setThreadStatsUid(uid); + TrafficStats.tagFileDescriptor(fd); + } finally { + TrafficStats.clearThreadStatsUid(); + } + }); + } + + /** @hide */ + @VisibleForTesting + public IpSecService( + Context context, IpSecServiceConfiguration config, UidFdTagger uidFdTagger) { mContext = context; mSrvConfig = config; + mUidFdTagger = uidFdTagger; } public void systemReady() { @@ -925,6 +942,26 @@ public class IpSecService extends IIpSecService.Stub { } /** + * Functional interface to do traffic tagging of given sockets to UIDs. + * + * <p>Specifically used by openUdpEncapsulationSocket to ensure data usage on the UDP encap + * sockets are billed to the UID that the UDP encap socket was created on behalf of. + * + * <p>Separate class so that the socket tagging logic can be mocked; TrafficStats uses static + * methods that cannot be easily mocked/tested. + */ + @VisibleForTesting + public interface UidFdTagger { + /** + * Sets socket tag to assign all traffic to the provided UID. + * + * <p>Since the socket is created on behalf of an unprivileged application, all traffic + * should be accounted to the UID of the unprivileged application. + */ + public void tag(FileDescriptor fd, int uid) throws IOException; + } + + /** * Open a socket via the system server and bind it to the specified port (random if port=0). * This will return a PFD to the user that represent a bound UDP socket. The system server will * cache the socket and a record of its owner so that it can and must be freed when no longer @@ -939,7 +976,8 @@ public class IpSecService extends IIpSecService.Stub { } checkNotNull(binder, "Null Binder passed to openUdpEncapsulationSocket"); - UserRecord userRecord = mUserResourceTracker.getUserRecord(Binder.getCallingUid()); + int callingUid = Binder.getCallingUid(); + UserRecord userRecord = mUserResourceTracker.getUserRecord(callingUid); int resourceId = mNextResourceId.getAndIncrement(); FileDescriptor sockFd = null; try { @@ -948,13 +986,8 @@ public class IpSecService extends IIpSecService.Stub { } sockFd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + mUidFdTagger.tag(sockFd, callingUid); - if (port != 0) { - Log.v(TAG, "Binding to port " + port); - Os.bind(sockFd, INADDR_ANY, port); - } else { - port = bindToRandomPort(sockFd); - } // This code is common to both the unspecified and specified port cases Os.setsockoptInt( sockFd, @@ -962,6 +995,14 @@ public class IpSecService extends IIpSecService.Stub { OsConstants.UDP_ENCAP, OsConstants.UDP_ENCAP_ESPINUDP); + mSrvConfig.getNetdInstance().ipSecSetEncapSocketOwner(sockFd, callingUid); + if (port != 0) { + Log.v(TAG, "Binding to port " + port); + Os.bind(sockFd, INADDR_ANY, port); + } else { + port = bindToRandomPort(sockFd); + } + userRecord.mEncapSocketRecords.put( resourceId, new RefcountedResource<EncapSocketRecord>( diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java index 3af5265e6fa9..db61ef5cd9b9 100644 --- a/services/core/java/com/android/server/net/NetworkStatsService.java +++ b/services/core/java/com/android/server/net/NetworkStatsService.java @@ -873,6 +873,21 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } } + @Override + public long getUidStats(int uid, int type) { + return nativeGetUidStat(uid, type); + } + + @Override + public long getIfaceStats(String iface, int type) { + return nativeGetIfaceStat(iface, type); + } + + @Override + public long getTotalStats(int type) { + return nativeGetTotalStat(type); + } + /** * Update {@link NetworkStatsRecorder} and {@link #mGlobalAlertBytes} to * reflect current {@link #mPersistThreshold} value. Always defers to @@ -1626,4 +1641,15 @@ public class NetworkStatsService extends INetworkStatsService.Stub { return getGlobalLong(NETSTATS_UID_TAG_PERSIST_BYTES, def); } } + + private static int TYPE_RX_BYTES; + private static int TYPE_RX_PACKETS; + private static int TYPE_TX_BYTES; + private static int TYPE_TX_PACKETS; + private static int TYPE_TCP_RX_PACKETS; + private static int TYPE_TCP_TX_PACKETS; + + private static native long nativeGetTotalStat(int type); + private static native long nativeGetIfaceStat(String iface, int type); + private static native long nativeGetUidStat(int uid, int type); } diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 27acaee58e51..04fd3e35b5e9 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -33,6 +33,7 @@ cc_library_static { "com_android_server_location_ContextHubService.cpp", "com_android_server_location_GnssLocationProvider.cpp", "com_android_server_locksettings_SyntheticPasswordManager.cpp", + "com_android_server_net_NetworkStatsService.cpp", "com_android_server_power_PowerManagerService.cpp", "com_android_server_SerialService.cpp", "com_android_server_storage_AppFuseBridge.cpp", diff --git a/services/core/jni/com_android_server_am_BatteryStatsService.cpp b/services/core/jni/com_android_server_am_BatteryStatsService.cpp index ae7d6daa649b..b044a4e81be7 100644 --- a/services/core/jni/com_android_server_am_BatteryStatsService.cpp +++ b/services/core/jni/com_android_server_am_BatteryStatsService.cpp @@ -100,7 +100,7 @@ static jint nativeWaitWakeup(JNIEnv *env, jobject clazz, jobject outBuf) return -1; } ALOGV("Registering callback..."); - set_wakeup_callback(&wakeup_callback); + autosuspend_set_wakeup_callback(&wakeup_callback); } // Wait for wakeup. diff --git a/core/jni/android_net_TrafficStats.cpp b/services/core/jni/com_android_server_net_NetworkStatsService.cpp index d0c237da0777..8de24e556511 100644 --- a/core/jni/android_net_TrafficStats.cpp +++ b/services/core/jni/com_android_server_net_NetworkStatsService.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#define LOG_TAG "TrafficStats" +#define LOG_TAG "NetworkStatsNative" #include <dirent.h> #include <errno.h> @@ -191,8 +191,24 @@ static const JNINativeMethod gMethods[] = { {"nativeGetUidStat", "(II)J", (void*) getUidStat}, }; -int register_android_net_TrafficStats(JNIEnv* env) { - return RegisterMethodsOrDie(env, "android/net/TrafficStats", gMethods, NELEM(gMethods)); +int register_android_server_net_NetworkStatsService(JNIEnv* env) { + jclass netStatsService = env->FindClass("com/android/server/net/NetworkStatsService"); + jfieldID rxBytesId = env->GetStaticFieldID(netStatsService, "TYPE_RX_BYTES", "I"); + jfieldID rxPacketsId = env->GetStaticFieldID(netStatsService, "TYPE_RX_PACKETS", "I"); + jfieldID txBytesId = env->GetStaticFieldID(netStatsService, "TYPE_TX_BYTES", "I"); + jfieldID txPacketsId = env->GetStaticFieldID(netStatsService, "TYPE_TX_PACKETS", "I"); + jfieldID tcpRxPacketsId = env->GetStaticFieldID(netStatsService, "TYPE_TCP_RX_PACKETS", "I"); + jfieldID tcpTxPacketsId = env->GetStaticFieldID(netStatsService, "TYPE_TCP_TX_PACKETS", "I"); + + env->SetStaticIntField(netStatsService, rxBytesId, RX_BYTES); + env->SetStaticIntField(netStatsService, rxPacketsId, RX_PACKETS); + env->SetStaticIntField(netStatsService, txBytesId, TX_BYTES); + env->SetStaticIntField(netStatsService, txPacketsId, TX_PACKETS); + env->SetStaticIntField(netStatsService, tcpRxPacketsId, TCP_RX_PACKETS); + env->SetStaticIntField(netStatsService, tcpTxPacketsId, TCP_TX_PACKETS); + + return jniRegisterNativeMethods(env, "com/android/server/net/NetworkStatsService", gMethods, + NELEM(gMethods)); } } diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index e8ef1686e1b4..8dfbbf35adec 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -52,6 +52,7 @@ int register_android_server_HardwarePropertiesManagerService(JNIEnv* env); int register_android_server_SyntheticPasswordManager(JNIEnv* env); int register_android_server_GraphicsStatsService(JNIEnv* env); int register_android_hardware_display_DisplayViewport(JNIEnv* env); +int register_android_server_net_NetworkStatsService(JNIEnv* env); }; using namespace android; @@ -98,6 +99,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) register_android_server_SyntheticPasswordManager(env); register_android_server_GraphicsStatsService(env); register_android_hardware_display_DisplayViewport(env); + register_android_server_net_NetworkStatsService(env); return JNI_VERSION_1_4; } diff --git a/services/tests/servicestests/src/com/android/server/NetworkScorerAppManagerTest.java b/services/tests/servicestests/src/com/android/server/NetworkScorerAppManagerTest.java index 82ff0d835f33..6874624ae193 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkScorerAppManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkScorerAppManagerTest.java @@ -52,10 +52,10 @@ import com.android.internal.R; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; -import org.mockito.compat.ArgumentMatcher; import java.util.ArrayList; import java.util.List; @@ -82,13 +82,9 @@ public class NetworkScorerAppManagerTest { MockitoAnnotations.initMocks(this); mAvailableServices = new ArrayList<>(); when(mMockContext.getPackageManager()).thenReturn(mMockPm); - when(mMockPm.queryIntentServices(Mockito.argThat(new ArgumentMatcher<Intent>() { - @Override - public boolean matchesObject(Object object) { - Intent intent = (Intent) object; - return NetworkScoreManager.ACTION_RECOMMEND_NETWORKS.equals(intent.getAction()); - } - }), eq(PackageManager.GET_META_DATA))).thenReturn(mAvailableServices); + when(mMockPm.queryIntentServices(Mockito.argThat( + intent -> NetworkScoreManager.ACTION_RECOMMEND_NETWORKS.equals(intent.getAction())), + eq(PackageManager.GET_META_DATA))).thenReturn(mAvailableServices); when(mMockContext.getResources()).thenReturn(mResources); when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager); @@ -664,15 +660,10 @@ public class NetworkScorerAppManagerTest { final int flags = PackageManager.GET_META_DATA; when(mMockPm.resolveService( - Mockito.argThat(new ArgumentMatcher<Intent>() { - @Override - public boolean matchesObject(Object object) { - Intent intent = (Intent) object; - return NetworkScoreManager.ACTION_RECOMMEND_NETWORKS - .equals(intent.getAction()) - && compName.getPackageName().equals(intent.getPackage()); - } - }), Mockito.eq(flags))).thenReturn(serviceInfo); + Mockito.argThat(intent -> NetworkScoreManager.ACTION_RECOMMEND_NETWORKS + .equals(intent.getAction()) + && compName.getPackageName().equals(intent.getPackage())), + Mockito.eq(flags))).thenReturn(serviceInfo); mAvailableServices.add(serviceInfo); } @@ -685,13 +676,9 @@ public class NetworkScorerAppManagerTest { final int flags = 0; when(mMockPm.resolveActivity( - Mockito.argThat(new ArgumentMatcher<Intent>() { - @Override - public boolean matchesObject(Object object) { - Intent intent = (Intent) object; - return NetworkScoreManager.ACTION_CUSTOM_ENABLE.equals(intent.getAction()) - && useOpenWifiComp.getPackageName().equals(intent.getPackage()); - } - }), Mockito.eq(flags))).thenReturn(resolveActivityInfo); + Mockito.argThat(intent -> + NetworkScoreManager.ACTION_CUSTOM_ENABLE.equals(intent.getAction()) + && useOpenWifiComp.getPackageName().equals(intent.getPackage())), + Mockito.eq(flags))).thenReturn(resolveActivityInfo); } } diff --git a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java index ec99a9a2eb95..fce5e3f89ca4 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/MotionEventInjectorTest.java @@ -65,7 +65,6 @@ import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; -import org.mockito.compat.ArgumentMatcher; /** * Tests for MotionEventInjector 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 05c4853612a3..30e84490a97c 100644 --- a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java @@ -38,9 +38,9 @@ import android.webkit.WebViewProviderResponse; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentMatcher; import org.mockito.Mockito; import org.mockito.Matchers; -import org.mockito.compat.ArgumentMatcher; import java.lang.Integer; import java.util.concurrent.CountDownLatch; @@ -140,7 +140,7 @@ public class WebViewUpdateServiceTest { } // For matching the package name of a PackageInfo - private class IsPackageInfoWithName extends ArgumentMatcher<PackageInfo> { + private class IsPackageInfoWithName implements ArgumentMatcher<PackageInfo> { private final String mPackageName; IsPackageInfoWithName(String name) { @@ -148,8 +148,8 @@ public class WebViewUpdateServiceTest { } @Override - public boolean matchesObject(Object p) { - return ((PackageInfo) p).packageName.equals(mPackageName); + public boolean matches(PackageInfo p) { + return p.packageName.equals(mPackageName); } @Override diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java index 5cd2044f8318..20911012e6ba 100644 --- a/telecomm/java/android/telecom/Call.java +++ b/telecomm/java/android/telecom/Call.java @@ -868,7 +868,8 @@ public final class Call { * @hide */ @IntDef({HANDOVER_FAILURE_DEST_APP_REJECTED, HANDOVER_FAILURE_DEST_NOT_SUPPORTED, - HANDOVER_FAILURE_DEST_INVALID_PERM, HANDOVER_FAILURE_DEST_USER_REJECTED}) + HANDOVER_FAILURE_DEST_INVALID_PERM, HANDOVER_FAILURE_DEST_USER_REJECTED, + HANDOVER_FAILURE_ONGOING_EMERG_CALL}) @Retention(RetentionPolicy.SOURCE) public @interface HandoverFailureErrors {} @@ -896,6 +897,12 @@ public final class Call { */ public static final int HANDOVER_FAILURE_DEST_USER_REJECTED = 4; + /** + * Handover failure reason returned via {@link #onHandoverFailed(Call, int)} when there + * is ongoing emergency call. + */ + public static final int HANDOVER_FAILURE_ONGOING_EMERG_CALL = 5; + /** * Invoked when the state of this {@code Call} has changed. See {@link #getState()}. @@ -1945,6 +1952,15 @@ public final class Call { } } + /** {@hide} */ + final void internalOnHandoverFailed(int error) { + for (CallbackRecord<Callback> record : mCallbackRecords) { + final Call call = this; + final Callback callback = record.getCallback(); + record.getHandler().post(() -> callback.onHandoverFailed(call, error)); + } + } + private void fireStateChanged(final int newState) { for (CallbackRecord<Callback> record : mCallbackRecords) { final Call call = this; diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java index da8ac5ec00d7..6fcb1156a45d 100644 --- a/telecomm/java/android/telecom/ConnectionService.java +++ b/telecomm/java/android/telecom/ConnectionService.java @@ -21,6 +21,7 @@ import android.app.Service; import android.content.ComponentName; import android.content.Intent; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -143,6 +144,7 @@ public abstract class ConnectionService extends Service { 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 String SESSION_HANDOVER_FAILED = "CS.haF"; private static final int MSG_ADD_CONNECTION_SERVICE_ADAPTER = 1; private static final int MSG_CREATE_CONNECTION = 2; @@ -172,6 +174,7 @@ public abstract class ConnectionService extends Service { private static final int MSG_ON_STOP_RTT = 27; private static final int MSG_RTT_UPGRADE_RESPONSE = 28; private static final int MSG_CREATE_CONNECTION_COMPLETE = 29; + private static final int MSG_HANDOVER_FAILED = 32; private static Connection sNullConnection; @@ -275,6 +278,22 @@ public abstract class ConnectionService extends Service { } @Override + public void handoverFailed(String callId, ConnectionRequest request, int reason, + Session.Info sessionInfo) { + Log.startSession(sessionInfo, SESSION_HANDOVER_FAILED); + try { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = callId; + args.arg2 = request; + args.arg3 = Log.createSubsession(); + args.arg4 = reason; + mHandler.obtainMessage(MSG_HANDOVER_FAILED, args).sendToTarget(); + } finally { + Log.endSession(); + } + } + + @Override public void abort(String callId, Session.Info sessionInfo) { Log.startSession(sessionInfo, SESSION_ABORT); try { @@ -723,6 +742,36 @@ public abstract class ConnectionService extends Service { } break; } + case MSG_HANDOVER_FAILED: { + SomeArgs args = (SomeArgs) msg.obj; + Log.continueSession((Session) args.arg3, SESSION_HANDLER + + SESSION_HANDOVER_FAILED); + try { + final String id = (String) args.arg1; + final ConnectionRequest request = (ConnectionRequest) args.arg2; + final int reason = (int) args.arg4; + if (!mAreAccountsInitialized) { + Log.d(this, "Enqueueing pre-init request %s", id); + mPreInitializationConnectionRequests.add( + new android.telecom.Logging.Runnable( + SESSION_HANDLER + + SESSION_HANDOVER_FAILED + ".pICR", + null /*lock*/) { + @Override + public void loggedRun() { + handoverFailed(id, request, reason); + } + }.prepare()); + } else { + Log.i(this, "createConnectionFailed %s", id); + handoverFailed(id, request, reason); + } + } finally { + args.recycle(); + Log.endSession(); + } + break; + } case MSG_ABORT: { SomeArgs args = (SomeArgs) msg.obj; Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_ABORT); @@ -1371,9 +1420,20 @@ public abstract class ConnectionService extends Service { isIncoming, isUnknown); - Connection connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request) - : isIncoming ? onCreateIncomingConnection(callManagerAccount, request) - : onCreateOutgoingConnection(callManagerAccount, request); + Connection connection = null; + if (getApplicationContext().getApplicationInfo().targetSdkVersion > + Build.VERSION_CODES.O_MR1 && request.getExtras() != null && + request.getExtras().getBoolean(TelecomManager.EXTRA_IS_HANDOVER,false)) { + if (!isIncoming) { + connection = onCreateOutgoingHandoverConnection(callManagerAccount, request); + } else { + connection = onCreateIncomingHandoverConnection(callManagerAccount, request); + } + } else { + connection = isUnknown ? onCreateUnknownConnection(callManagerAccount, request) + : isIncoming ? onCreateIncomingConnection(callManagerAccount, request) + : onCreateOutgoingConnection(callManagerAccount, request); + } Log.d(this, "createConnection, connection: %s", connection); if (connection == null) { connection = Connection.createFailedConnection( @@ -1442,6 +1502,13 @@ public abstract class ConnectionService extends Service { } } + private void handoverFailed(final String callId, final ConnectionRequest request, + int reason) { + + Log.i(this, "handoverFailed %s", callId); + onHandoverFailed(request, reason); + } + /** * Called by Telecom when the creation of a new Connection has completed and it is now added * to Telecom. diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java index d558bbaea6d3..74fa62d62ccf 100644 --- a/telecomm/java/android/telecom/InCallService.java +++ b/telecomm/java/android/telecom/InCallService.java @@ -80,6 +80,7 @@ public abstract class InCallService extends Service { 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; + private static final int MSG_ON_HANDOVER_FAILED = 12; /** Default Handler used to consolidate binder method calls onto a single thread. */ private final Handler mHandler = new Handler(Looper.getMainLooper()) { @@ -150,6 +151,12 @@ public abstract class InCallService extends Service { mPhone.internalOnRttInitiationFailure(callId, reason); break; } + case MSG_ON_HANDOVER_FAILED: { + String callId = (String) msg.obj; + int error = msg.arg1; + mPhone.internalOnHandoverFailed(callId, error); + break; + } default: break; } @@ -225,6 +232,11 @@ public abstract class InCallService extends Service { public void onRttInitiationFailure(String callId, int reason) { mHandler.obtainMessage(MSG_ON_RTT_INITIATION_FAILURE, reason, 0, callId).sendToTarget(); } + + @Override + public void onHandoverFailed(String callId, int error) { + mHandler.obtainMessage(MSG_ON_HANDOVER_FAILED, error, 0, callId).sendToTarget(); + } } private Phone.Listener mPhoneListener = new Phone.Listener() { diff --git a/telecomm/java/android/telecom/Phone.java b/telecomm/java/android/telecom/Phone.java index 421b1a4b22a5..b5394b9b0290 100644 --- a/telecomm/java/android/telecom/Phone.java +++ b/telecomm/java/android/telecom/Phone.java @@ -223,6 +223,13 @@ public final class Phone { } } + final void internalOnHandoverFailed(String callId, int error) { + Call call = mCallByTelecomCallId.get(callId); + if (call != null) { + call.internalOnHandoverFailed(error); + } + } + /** * Called to destroy the phone and cleanup any lingering calls. */ diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl index e428286ad1e3..732d00d9eb85 100644 --- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl +++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl @@ -100,4 +100,7 @@ oneway interface IConnectionService { void respondToRttUpgradeRequest(String callId, in ParcelFileDescriptor fromInCall, in ParcelFileDescriptor toInCall, in Session.Info sessionInfo); + + void handoverFailed(String callId, in ConnectionRequest request, + int error, in Session.Info sessionInfo); } diff --git a/telecomm/java/com/android/internal/telecom/IInCallService.aidl b/telecomm/java/com/android/internal/telecom/IInCallService.aidl index e8cf8e975444..110109e6e276 100644 --- a/telecomm/java/com/android/internal/telecom/IInCallService.aidl +++ b/telecomm/java/com/android/internal/telecom/IInCallService.aidl @@ -54,4 +54,6 @@ oneway interface IInCallService { void onRttUpgradeRequest(String callId, int id); void onRttInitiationFailure(String callId, int reason); + + void onHandoverFailed(String callId, int error); } diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index d80ad3654389..514222dcdbab 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -1712,6 +1712,13 @@ public class CarrierConfigManager { public static final String KEY_SPN_DISPLAY_RULE_USE_ROAMING_FROM_SERVICE_STATE_BOOL = "spn_display_rule_use_roaming_from_service_state_bool"; + /** + * Determines whether any carrier has been identified and its specific config has been applied, + * default to false. + * @hide + */ + public static final String KEY_CARRIER_CONFIG_APPLIED_BOOL = "carrier_config_applied_bool"; + /** The default value for every variable. */ private final static PersistableBundle sDefaults; @@ -1995,6 +2002,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_IDENTIFY_HIGH_DEFINITION_CALLS_IN_CALL_LOG_BOOL, false); sDefaults.putBoolean(KEY_SPN_DISPLAY_RULE_USE_ROAMING_FROM_SERVICE_STATE_BOOL, false); sDefaults.putBoolean(KEY_ALWAYS_SHOW_DATA_RAT_ICON_BOOL, false); + sDefaults.putBoolean(KEY_CARRIER_CONFIG_APPLIED_BOOL, false); } /** @@ -2040,6 +2048,33 @@ public class CarrierConfigManager { } /** + * Determines whether a configuration {@link PersistableBundle} obtained from + * {@link #getConfig()} or {@link #getConfigForSubId(int)} corresponds to an identified carrier. + * <p> + * When an app receives the {@link CarrierConfigManager#ACTION_CARRIER_CONFIG_CHANGED} + * broadcast which informs it that the carrier configuration has changed, it is possible + * that another reload of the carrier configuration has begun since the intent was sent. + * In this case, the carrier configuration the app fetches (e.g. via {@link #getConfig()}) + * may not represent the configuration for the current carrier. It should be noted that it + * does not necessarily mean the configuration belongs to current carrier when this function + * return true because it may belong to another previous identified carrier. Users should + * always call {@link #getConfig()} or {@link #getConfigForSubId(int)} after receiving the + * broadcast {@link #ACTION_CARRIER_CONFIG_CHANGED}. + * </p> + * <p> + * After using {@link #getConfig()} or {@link #getConfigForSubId(int)} an app should always + * use this method to confirm whether any carrier specific configuration has been applied. + * </p> + * + * @param bundle the configuration bundle to be checked. + * @return boolean true if any carrier specific configuration bundle has been applied, false + * otherwise or the bundle is null. + */ + public static boolean isConfigForIdentifiedCarrier(PersistableBundle bundle) { + return bundle != null && bundle.getBoolean(KEY_CARRIER_CONFIG_APPLIED_BOOL); + } + + /** * Calling this method triggers telephony services to fetch the current carrier configuration. * <p> * Normally this does not need to be called because the platform reloads config on its own. diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java index a13af5f4b90b..84d0087b3790 100644 --- a/telephony/java/android/telephony/euicc/EuiccManager.java +++ b/telephony/java/android/telephony/euicc/EuiccManager.java @@ -168,12 +168,10 @@ public class EuiccManager { public static final String META_DATA_CARRIER_ICON = "android.telephony.euicc.carriericon"; private final Context mContext; - private final IEuiccController mController; /** @hide */ public EuiccManager(Context context) { mContext = context; - mController = IEuiccController.Stub.asInterface(ServiceManager.getService("econtroller")); } /** @@ -189,7 +187,7 @@ public class EuiccManager { public boolean isEnabled() { // In the future, this may reach out to IEuiccController (if non-null) to check any dynamic // restrictions. - return mController != null; + return getIEuiccController() != null; } /** @@ -206,7 +204,7 @@ public class EuiccManager { return null; } try { - return mController.getEid(); + return getIEuiccController().getEid(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -232,7 +230,7 @@ public class EuiccManager { return; } try { - mController.downloadSubscription(subscription, switchAfterDownload, + getIEuiccController().downloadSubscription(subscription, switchAfterDownload, mContext.getOpPackageName(), callbackIntent); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -296,7 +294,7 @@ public class EuiccManager { return; } try { - mController.continueOperation(resolutionIntent, resolutionExtras); + getIEuiccController().continueOperation(resolutionIntent, resolutionExtras); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -328,7 +326,7 @@ public class EuiccManager { return; } try { - mController.getDownloadableSubscriptionMetadata( + getIEuiccController().getDownloadableSubscriptionMetadata( subscription, mContext.getOpPackageName(), callbackIntent); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -358,7 +356,7 @@ public class EuiccManager { return; } try { - mController.getDefaultDownloadableSubscriptionList( + getIEuiccController().getDefaultDownloadableSubscriptionList( mContext.getOpPackageName(), callbackIntent); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -377,7 +375,7 @@ public class EuiccManager { return null; } try { - return mController.getEuiccInfo(); + return getIEuiccController().getEuiccInfo(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -402,7 +400,7 @@ public class EuiccManager { return; } try { - mController.deleteSubscription( + getIEuiccController().deleteSubscription( subscriptionId, mContext.getOpPackageName(), callbackIntent); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -429,7 +427,7 @@ public class EuiccManager { return; } try { - mController.switchToSubscription( + getIEuiccController().switchToSubscription( subscriptionId, mContext.getOpPackageName(), callbackIntent); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -455,7 +453,8 @@ public class EuiccManager { return; } try { - mController.updateSubscriptionNickname(subscriptionId, nickname, callbackIntent); + getIEuiccController().updateSubscriptionNickname( + subscriptionId, nickname, callbackIntent); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -477,7 +476,7 @@ public class EuiccManager { return; } try { - mController.eraseSubscriptions(callbackIntent); + getIEuiccController().eraseSubscriptions(callbackIntent); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -507,7 +506,7 @@ public class EuiccManager { return; } try { - mController.retainSubscriptionsForFactoryReset(callbackIntent); + getIEuiccController().retainSubscriptionsForFactoryReset(callbackIntent); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -520,4 +519,8 @@ public class EuiccManager { // Caller canceled the callback; do nothing. } } + + private static IEuiccController getIEuiccController() { + return IEuiccController.Stub.asInterface(ServiceManager.getService("econtroller")); + } } diff --git a/telephony/java/android/telephony/ims/internal/ImsCallSessionListener.java b/telephony/java/android/telephony/ims/internal/ImsCallSessionListener.java new file mode 100644 index 000000000000..5d16dd5b30ee --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/ImsCallSessionListener.java @@ -0,0 +1,364 @@ +/* + * 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.internal; + +import android.os.RemoteException; +import android.telephony.ims.internal.aidl.IImsCallSessionListener; + +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.ImsCallSession; + +/** + * Proxy class for interfacing with the framework's Call session for an ongoing IMS call. + * + * 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 ImsCallSessionListener { + + private final IImsCallSessionListener mListener; + + public ImsCallSessionListener(IImsCallSessionListener l) { + mListener = l; + } + + /** + * Called when a request is sent out to initiate a new session + * and 1xx response is received from the network. + */ + public void callSessionProgressing(ImsStreamMediaProfile profile) + throws RemoteException { + mListener.callSessionProgressing(profile); + } + + /** + * Called when the session is initiated. + * + * @param profile the associated {@link ImsCallSession}. + */ + public void callSessionInitiated(ImsCallProfile profile) throws RemoteException { + mListener.callSessionInitiated(profile); + } + + /** + * Called when the session establishment has failed. + * + * @param reasonInfo detailed reason of the session establishment failure + */ + public void callSessionInitiatedFailed(ImsReasonInfo reasonInfo) throws RemoteException { + mListener.callSessionInitiatedFailed(reasonInfo); + } + + /** + * Called when the session is terminated. + * + * @param reasonInfo detailed reason of the session termination + */ + public void callSessionTerminated(ImsReasonInfo reasonInfo) throws RemoteException { + mListener.callSessionTerminated(reasonInfo); + } + + /** + * Called when the session is on hold. + */ + public void callSessionHeld(ImsCallProfile profile) throws RemoteException { + mListener.callSessionHeld(profile); + } + + /** + * Called when the session hold has failed. + * + * @param reasonInfo detailed reason of the session hold failure + */ + public void callSessionHoldFailed(ImsReasonInfo reasonInfo) throws RemoteException { + mListener.callSessionHoldFailed(reasonInfo); + } + + /** + * Called when the session hold is received from the remote user. + */ + public void callSessionHoldReceived(ImsCallProfile profile) throws RemoteException { + mListener.callSessionHoldReceived(profile); + } + + /** + * Called when the session resume is done. + */ + public void callSessionResumed(ImsCallProfile profile) throws RemoteException { + mListener.callSessionResumed(profile); + } + + /** + * Called when the session resume has failed. + * + * @param reasonInfo detailed reason of the session resume failure + */ + public void callSessionResumeFailed(ImsReasonInfo reasonInfo) throws RemoteException { + mListener.callSessionResumeFailed(reasonInfo); + } + + /** + * Called when the session resume is received from the remote user. + */ + public void callSessionResumeReceived(ImsCallProfile profile) throws RemoteException { + mListener.callSessionResumeReceived(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 newSession the session object that is merged with an active & hold session + */ + public void callSessionMergeStarted(ImsCallSession newSession, ImsCallProfile profile) + throws RemoteException { + mListener.callSessionMergeStarted(newSession != null ? newSession.getSession() : null, + profile); + } + + /** + * Called when the session merge is successful and the merged session is active. + * + * @param newSession the new session object that is used for the conference + */ + public void callSessionMergeComplete(ImsCallSession newSession) throws RemoteException { + mListener.callSessionMergeComplete(newSession != null ? newSession.getSession() : null); + } + + /** + * Called when the session merge has failed. + * + * @param reasonInfo detailed reason of the call merge failure + */ + public void callSessionMergeFailed(ImsReasonInfo reasonInfo) throws RemoteException { + mListener.callSessionMergeFailed(reasonInfo); + } + + /** + * Called when the session is updated (except for hold/unhold). + */ + public void callSessionUpdated(ImsCallProfile profile) throws RemoteException { + mListener.callSessionUpdated(profile); + } + + /** + * Called when the session update has failed. + * + * @param reasonInfo detailed reason of the session update failure + */ + public void callSessionUpdateFailed(ImsReasonInfo reasonInfo) throws RemoteException { + mListener.callSessionUpdateFailed(reasonInfo); + } + + /** + * Called when the session update is received from the remote user. + */ + public void callSessionUpdateReceived(ImsCallProfile profile) throws RemoteException { + mListener.callSessionUpdateReceived(profile); + } + + /** + * Called when the session has been extended to a conference session. + * + * @param newSession the session object that is extended to the conference + * from the active session + */ + public void callSessionConferenceExtended(ImsCallSession newSession, ImsCallProfile profile) + throws RemoteException { + mListener.callSessionConferenceExtended(newSession != null ? newSession.getSession() : null, + profile); + } + + /** + * Called when the conference extension has failed. + * + * @param reasonInfo detailed reason of the conference extension failure + */ + public void callSessionConferenceExtendFailed(ImsReasonInfo reasonInfo) throws RemoteException { + mListener.callSessionConferenceExtendFailed(reasonInfo); + } + + /** + * Called when the conference extension is received from the remote user. + */ + public void callSessionConferenceExtendReceived(ImsCallSession newSession, + ImsCallProfile profile) throws RemoteException { + mListener.callSessionConferenceExtendReceived(newSession != null + ? newSession.getSession() : null, profile); + } + + /** + * Called when the invitation request of the participants is delivered to the conference + * server. + */ + public void callSessionInviteParticipantsRequestDelivered() throws RemoteException { + mListener.callSessionInviteParticipantsRequestDelivered(); + } + + /** + * Called when the invitation request of the participants has failed. + * + * @param reasonInfo detailed reason of the conference invitation failure + */ + public void callSessionInviteParticipantsRequestFailed(ImsReasonInfo reasonInfo) + throws RemoteException { + mListener.callSessionInviteParticipantsRequestFailed(reasonInfo); + } + + /** + * Called when the removal request of the participants is delivered to the conference + * server. + */ + public void callSessionRemoveParticipantsRequestDelivered() throws RemoteException { + mListener.callSessionRemoveParticipantsRequestDelivered(); + } + + /** + * Called when the removal request of the participants has failed. + * + * @param reasonInfo detailed reason of the conference removal failure + */ + public void callSessionRemoveParticipantsRequestFailed(ImsReasonInfo reasonInfo) + throws RemoteException { + mListener.callSessionInviteParticipantsRequestFailed(reasonInfo); + } + + /** + * Notifies the framework of the updated Call session conference state. + * + * @param state the new {@link ImsConferenceState} associated with the conference. + */ + public void callSessionConferenceStateUpdated(ImsConferenceState state) throws RemoteException { + mListener.callSessionConferenceStateUpdated(state); + } + + /** + * Notifies the incoming USSD message. + */ + public void callSessionUssdMessageReceived(int mode, String ussdMessage) + throws RemoteException { + mListener.callSessionUssdMessageReceived(mode, ussdMessage); + } + + /** + * Notifies of a case where a {@link com.android.ims.internal.ImsCallSession} may potentially + * handover from one radio technology to another. + * + * @param srcAccessTech The source radio access technology; one of the access technology + * constants defined in {@link android.telephony.ServiceState}. For + * example + * {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}. + * @param targetAccessTech The target radio access technology; one of the access technology + * constants defined in {@link android.telephony.ServiceState}. For + * example + * {@link android.telephony.ServiceState#RIL_RADIO_TECHNOLOGY_LTE}. + */ + public void callSessionMayHandover(int srcAccessTech, int targetAccessTech) + throws RemoteException { + mListener.callSessionMayHandover(srcAccessTech, targetAccessTech); + } + + /** + * Called when session access technology changes. + * + * @param srcAccessTech original access technology + * @param targetAccessTech new access technology + * @param reasonInfo + */ + public void callSessionHandover(int srcAccessTech, int targetAccessTech, + ImsReasonInfo reasonInfo) throws RemoteException { + mListener.callSessionHandover(srcAccessTech, targetAccessTech, reasonInfo); + } + + /** + * Called when session access technology change fails. + * + * @param srcAccessTech original access technology + * @param targetAccessTech new access technology + * @param reasonInfo handover failure reason + */ + public void callSessionHandoverFailed(int srcAccessTech, int targetAccessTech, + ImsReasonInfo reasonInfo) throws RemoteException { + mListener.callSessionHandoverFailed(srcAccessTech, targetAccessTech, reasonInfo); + } + + /** + * Called when the TTY mode is changed by the 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} + */ + public void callSessionTtyModeReceived(int mode) throws RemoteException { + mListener.callSessionTtyModeReceived(mode); + } + + /** + * Called when the multiparty state is changed for this {@code ImsCallSession}. + * + * @param isMultiParty {@code true} if the session became multiparty, + * {@code false} otherwise. + */ + + public void callSessionMultipartyStateChanged(boolean isMultiParty) throws RemoteException { + mListener.callSessionMultipartyStateChanged(isMultiParty); + } + + /** + * Called when the supplementary service information is received for the current session. + */ + public void callSessionSuppServiceReceived(ImsSuppServiceNotification suppSrvNotification) + throws RemoteException { + mListener.callSessionSuppServiceReceived(suppSrvNotification); + } + + /** + * Received RTT modify request from the remote party. + * + * @param callProfile ImsCallProfile with updated attributes + */ + public void callSessionRttModifyRequestReceived(ImsCallProfile callProfile) + throws RemoteException { + mListener.callSessionRttModifyRequestReceived(callProfile); + } + + /** + * @param status the received response for RTT modify request. + */ + public void callSessionRttModifyResponseReceived(int status) throws RemoteException { + mListener.callSessionRttModifyResponseReceived(status); + } + + /** + * Device received RTT message from Remote UE. + * + * @param rttMessage RTT message received + */ + public void callSessionRttMessageReceived(String rttMessage) throws RemoteException { + mListener.callSessionRttMessageReceived(rttMessage); + } +} + diff --git a/telephony/java/android/telephony/ims/internal/ImsService.java b/telephony/java/android/telephony/ims/internal/ImsService.java new file mode 100644 index 000000000000..b7c8ca0f9799 --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/ImsService.java @@ -0,0 +1,339 @@ +/* + * 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.internal; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; +import android.os.RemoteException; +import android.telephony.CarrierConfigManager; +import android.telephony.ims.internal.aidl.IImsConfig; +import android.telephony.ims.internal.aidl.IImsMmTelFeature; +import android.telephony.ims.internal.aidl.IImsRcsFeature; +import android.telephony.ims.internal.aidl.IImsRegistration; +import android.telephony.ims.internal.aidl.IImsServiceController; +import android.telephony.ims.internal.aidl.IImsServiceControllerListener; +import android.telephony.ims.internal.feature.ImsFeature; +import android.telephony.ims.internal.feature.MmTelFeature; +import android.telephony.ims.internal.feature.RcsFeature; +import android.telephony.ims.internal.stub.ImsConfigImplBase; +import android.telephony.ims.internal.stub.ImsFeatureConfiguration; +import android.telephony.ims.internal.stub.ImsRegistrationImplBase; +import android.util.Log; +import android.util.SparseArray; + +import com.android.ims.internal.IImsFeatureStatusCallback; +import com.android.internal.annotations.VisibleForTesting; + +/** + * 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 RcsFeature class. + * - MMTEL_FEATURE: This ImsService implements the MmTelFeature class. + * @hide + */ +public class ImsService extends Service { + + private static final String LOG_TAG = "ImsService"; + + /** + * The intent that must be defined as an intent-filter in the AndroidManifest of the ImsService. + * @hide + */ + public static final String SERVICE_INTERFACE = "android.telephony.ims.ImsService"; + + // A map of slot Id -> map of features (indexed by ImsFeature feature id) corresponding to that + // slot. + // We keep track of this to facilitate cleanup of the IImsFeatureStatusCallback and + // call ImsFeature#onFeatureRemoved. + private final SparseArray<SparseArray<ImsFeature>> mFeaturesBySlot = new SparseArray<>(); + + private IImsServiceControllerListener mListener; + + + /** + * Listener that notifies the framework of ImsService changes. + */ + public static class Listener extends IImsServiceControllerListener.Stub { + /** + * The IMS features that this ImsService supports has changed. + * @param c a new {@link ImsFeatureConfiguration} containing {@link ImsFeature.FeatureType}s + * that this ImsService supports. This may trigger the addition/removal of feature + * in this service. + */ + public void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c) { + } + } + + /** + * @hide + */ + protected final IBinder mImsServiceController = new IImsServiceController.Stub() { + @Override + public void setListener(IImsServiceControllerListener l) { + mListener = l; + } + + @Override + public IImsMmTelFeature createMmTelFeature(int slotId, IImsFeatureStatusCallback c) { + return createMmTelFeatureInternal(slotId, c); + } + + @Override + public IImsRcsFeature createRcsFeature(int slotId, IImsFeatureStatusCallback c) { + return createRcsFeatureInternal(slotId, c); + } + + @Override + public void removeImsFeature(int slotId, int featureType, IImsFeatureStatusCallback c) + throws RemoteException { + ImsService.this.removeImsFeature(slotId, featureType, c); + } + + @Override + public ImsFeatureConfiguration querySupportedImsFeatures() { + return ImsService.this.querySupportedImsFeatures(); + } + + @Override + public void notifyImsServiceReadyForFeatureCreation() { + ImsService.this.readyForFeatureCreation(); + } + + @Override + public void notifyImsFeatureReady(int slotId, int featureType) + throws RemoteException { + ImsService.this.notifyImsFeatureReady(slotId, featureType); + } + + @Override + public IImsConfig getConfig(int slotId) throws RemoteException { + ImsConfigImplBase c = ImsService.this.getConfig(slotId); + return c != null ? c.getBinder() : null; + } + + @Override + public IImsRegistration getRegistration(int slotId) throws RemoteException { + ImsRegistrationImplBase r = ImsService.this.getRegistration(slotId); + return r != null ? r.getBinder() : null; + } + }; + + /** + * @hide + */ + @Override + public IBinder onBind(Intent intent) { + if(SERVICE_INTERFACE.equals(intent.getAction())) { + Log.i(LOG_TAG, "ImsService Bound."); + return mImsServiceController; + } + return null; + } + + /** + * @hide + */ + @VisibleForTesting + public SparseArray<ImsFeature> getFeatures(int slotId) { + return mFeaturesBySlot.get(slotId); + } + + private IImsMmTelFeature createMmTelFeatureInternal(int slotId, + IImsFeatureStatusCallback c) { + MmTelFeature f = createMmTelFeature(slotId); + if (f != null) { + setupFeature(f, slotId, ImsFeature.FEATURE_MMTEL, c); + return f.getBinder(); + } else { + Log.e(LOG_TAG, "createMmTelFeatureInternal: null feature returned."); + return null; + } + } + + private IImsRcsFeature createRcsFeatureInternal(int slotId, + IImsFeatureStatusCallback c) { + RcsFeature f = createRcsFeature(slotId); + if (f != null) { + setupFeature(f, slotId, ImsFeature.FEATURE_RCS, c); + return f.getBinder(); + } else { + Log.e(LOG_TAG, "createRcsFeatureInternal: null feature returned."); + return null; + } + } + + private void setupFeature(ImsFeature f, int slotId, int featureType, + IImsFeatureStatusCallback c) { + f.addImsFeatureStatusCallback(c); + f.initialize(this, slotId); + addImsFeature(slotId, featureType, f); + } + + private void addImsFeature(int slotId, int featureType, ImsFeature f) { + synchronized (mFeaturesBySlot) { + // Get SparseArray for Features, by querying slot Id + SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId); + if (features == null) { + // Populate new SparseArray of features if it doesn't exist for this slot yet. + features = new SparseArray<>(); + mFeaturesBySlot.put(slotId, features); + } + features.put(featureType, f); + } + } + + private void removeImsFeature(int slotId, int featureType, + IImsFeatureStatusCallback c) { + synchronized (mFeaturesBySlot) { + // get ImsFeature associated with the slot/feature + SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId); + if (features == null) { + Log.w(LOG_TAG, "Can not remove ImsFeature. No ImsFeatures exist on slot " + + slotId); + return; + } + ImsFeature f = features.get(featureType); + if (f == null) { + Log.w(LOG_TAG, "Can not remove ImsFeature. No feature with type " + + featureType + " exists on slot " + slotId); + return; + } + f.removeImsFeatureStatusCallback(c); + f.onFeatureRemoved(); + features.remove(featureType); + } + } + + private void notifyImsFeatureReady(int slotId, int featureType) { + synchronized (mFeaturesBySlot) { + // get ImsFeature associated with the slot/feature + SparseArray<ImsFeature> features = mFeaturesBySlot.get(slotId); + if (features == null) { + Log.w(LOG_TAG, "Can not notify ImsFeature ready. No ImsFeatures exist on " + + "slot " + slotId); + return; + } + ImsFeature f = features.get(featureType); + if (f == null) { + Log.w(LOG_TAG, "Can not notify ImsFeature ready. No feature with type " + + featureType + " exists on slot " + slotId); + return; + } + f.onFeatureReady(); + } + } + + /** + * When called, provide the {@link ImsFeatureConfiguration} that this ImsService currently + * supports. This will trigger the framework to set up the {@link ImsFeature}s that correspond + * to the {@link ImsFeature.FeatureType}s configured here. + * @return an {@link ImsFeatureConfiguration} containing Features this ImsService supports, + * defined in {@link ImsFeature.FeatureType}. + */ + public ImsFeatureConfiguration querySupportedImsFeatures() { + // Return empty for base implementation + return new ImsFeatureConfiguration(); + } + + /** + * Updates the framework with a new {@link ImsFeatureConfiguration} containing the updated + * features, defined in {@link ImsFeature.FeatureType} that this ImsService supports. This may + * trigger the framework to add/remove new ImsFeatures, depending on the configuration. + */ + public final void onUpdateSupportedImsFeatures(ImsFeatureConfiguration c) + throws RemoteException { + if (mListener == null) { + throw new IllegalStateException("Framework is not ready"); + } + mListener.onUpdateSupportedImsFeatures(c); + } + + /** + * The ImsService has been bound and is ready for ImsFeature creation based on the Features that + * the ImsService has registered for with the framework, either in the manifest or via + * The ImsService should use this signal instead of onCreate/onBind or similar to perform + * feature initialization because the framework may bind to this service multiple times to + * query the ImsService's {@link ImsFeatureConfiguration} via + * {@link #querySupportedImsFeatures()}before creating features. + */ + public void readyForFeatureCreation() { + } + + /** + * When called, the framework is requesting that a new MmTelFeature is created for the specified + * slot. + * + * @param slotId The slot ID that the MMTel Feature is being created for. + * @return The newly created MmTelFeature associated with the slot or null if the feature is not + * supported. + */ + public MmTelFeature createMmTelFeature(int slotId) { + return null; + } + + /** + * When called, the framework is requesting that a new RcsFeature is created for the specified + * slot + * + * @param slotId The slot ID that the RCS Feature is being created for. + * @return The newly created RcsFeature associated with the slot or null if the feature is not + * supported. + */ + public RcsFeature createRcsFeature(int slotId) { + return null; + } + + /** + * @param slotId The slot that the IMS configuration is associated with. + * @return ImsConfig implementation that is associated with the specified slot. + */ + public ImsConfigImplBase getConfig(int slotId) { + return new ImsConfigImplBase(); + } + + /** + * @param slotId The slot that is associated with the IMS Registration. + * @return the ImsRegistration implementation associated with the slot. + */ + public ImsRegistrationImplBase getRegistration(int slotId) { + return new ImsRegistrationImplBase(); + } +} diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsCallSessionListener.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsCallSessionListener.aidl new file mode 100644 index 000000000000..2fb67442fa34 --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/aidl/IImsCallSessionListener.aidl @@ -0,0 +1,141 @@ +/* + * 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.telephony.ims.internal.aidl; + +import com.android.ims.ImsStreamMediaProfile; +import com.android.ims.ImsCallProfile; +import com.android.ims.ImsReasonInfo; +import com.android.ims.ImsConferenceState; +import com.android.ims.internal.IImsCallSession; +import com.android.ims.ImsSuppServiceNotification; + +/** + * 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}. + * {@hide} + */ +oneway interface IImsCallSessionListener { + /** + * Notifies the result of the basic session operation (setup / terminate). + */ + void callSessionProgressing(in ImsStreamMediaProfile profile); + void callSessionInitiated(in ImsCallProfile profile); + void callSessionInitiatedFailed(in ImsReasonInfo reasonInfo); + void callSessionTerminated(in ImsReasonInfo reasonInfo); + + /** + * Notifies the result of the call hold/resume operation. + */ + void callSessionHeld(in ImsCallProfile profile); + void callSessionHoldFailed(in ImsReasonInfo reasonInfo); + void callSessionHoldReceived(in ImsCallProfile profile); + void callSessionResumed(in ImsCallProfile profile); + void callSessionResumeFailed(in ImsReasonInfo reasonInfo); + void callSessionResumeReceived(in ImsCallProfile profile); + + /** + * Notifies the result of call merge operation. + */ + void callSessionMergeStarted(IImsCallSession newSession, in ImsCallProfile profile); + void callSessionMergeComplete(IImsCallSession session); + void callSessionMergeFailed(in ImsReasonInfo reasonInfo); + + /** + * Notifies the result of call upgrade / downgrade or any other call updates. + */ + void callSessionUpdated(in ImsCallProfile profile); + void callSessionUpdateFailed(in ImsReasonInfo reasonInfo); + void callSessionUpdateReceived(in ImsCallProfile profile); + + /** + * Notifies the result of conference extension. + */ + void callSessionConferenceExtended(IImsCallSession newSession, in ImsCallProfile profile); + void callSessionConferenceExtendFailed(in ImsReasonInfo reasonInfo); + void callSessionConferenceExtendReceived(IImsCallSession newSession, + in ImsCallProfile profile); + + /** + * Notifies the result of the participant invitation / removal to/from the conference session. + */ + void callSessionInviteParticipantsRequestDelivered(); + void callSessionInviteParticipantsRequestFailed(in ImsReasonInfo reasonInfo); + void callSessionRemoveParticipantsRequestDelivered(); + void callSessionRemoveParticipantsRequestFailed(in ImsReasonInfo reasonInfo); + + /** + * Notifies the changes of the conference info. in the conference session. + */ + void callSessionConferenceStateUpdated(in ImsConferenceState state); + + /** + * Notifies the incoming USSD message. + */ + void callSessionUssdMessageReceived(int mode, String ussdMessage); + + /** + * Notifies of handover information for this call + */ + void callSessionHandover(int srcAccessTech, int targetAccessTech, + in ImsReasonInfo reasonInfo); + void callSessionHandoverFailed(int srcAccessTech, int targetAccessTech, + in ImsReasonInfo reasonInfo); + void callSessionMayHandover(int srcAccessTech, int targetAccessTech); + + /** + * 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} + */ + void callSessionTtyModeReceived(int 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. + */ + void callSessionMultipartyStateChanged(boolean isMultiParty); + + /** + * Notifies the supplementary service information for the current session. + */ + void callSessionSuppServiceReceived(in ImsSuppServiceNotification suppSrvNotification); + + /** + * Device received RTT modify request from Remote UE + * @param session ImsCallProfile with updated attribute + */ + void callSessionRttModifyRequestReceived(in ImsCallProfile callProfile); + + /* Device issued RTT modify request and inturn received response + * from Remote UE + * @param status Will be one of the following values from: + * - {@link Connection.RttModifyStatus} + */ + void callSessionRttModifyResponseReceived(int status); + + /* + * While in call, device received RTT message from Remote UE + * @param rttMessage Received RTT message + */ + void callSessionRttMessageReceived(in String rttMessage); +} diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsCapabilityCallback.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsCapabilityCallback.aidl new file mode 100644 index 000000000000..fd2eb24610ec --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/aidl/IImsCapabilityCallback.aidl @@ -0,0 +1,27 @@ +/* + * 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.internal.aidl; + +/** + * See ImsFeature#CapabilityCallback for more information. + * {@hide} + */ +oneway interface IImsCapabilityCallback { + void onQueryCapabilityConfiguration(int capability, int radioTech, boolean enabled); + void onChangeCapabilityConfigurationError(int capability, int radioTech, int reason); + void onCapabilitiesStatusChanged(int config); +} diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsConfig.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsConfig.aidl new file mode 100644 index 000000000000..3d424a33012d --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/aidl/IImsConfig.aidl @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package android.telephony.ims.internal.aidl; + +import android.telephony.ims.internal.aidl.IImsConfigCallback; + +import com.android.ims.ImsConfigListener; + +/** + * Provides APIs to get/set the IMS service feature/capability/parameters. + * The config items include items provisioned by the operator. + * + * {@hide} + */ +interface IImsConfig { + + void addImsConfigCallback(IImsConfigCallback c); + void removeImsConfigCallback(IImsConfigCallback c); + int getConfigInt(int item); + String getConfigString(int item); + // Return result code defined in ImsConfig#OperationStatusConstants + int setConfigInt(int item, int value); + // Return result code defined in ImsConfig#OperationStatusConstants + int setConfigString(int item, String value); +} diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsConfigCallback.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsConfigCallback.aidl new file mode 100644 index 000000000000..52efd2322c17 --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/aidl/IImsConfigCallback.aidl @@ -0,0 +1,28 @@ +/* + * 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.telephony.ims.internal.aidl; + +/** + * Provides callback interface for ImsConfig when a value has changed. + * + * {@hide} + */ +oneway interface IImsConfigCallback { + void onIntConfigChanged(int item, int value); + void onStringConfigChanged(int item, String value); +} diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelFeature.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelFeature.aidl new file mode 100644 index 000000000000..712578117e44 --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelFeature.aidl @@ -0,0 +1,52 @@ +/* + * 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.internal.aidl; + +import android.os.Message; +import android.telephony.ims.internal.aidl.IImsMmTelListener; +import android.telephony.ims.internal.aidl.IImsCapabilityCallback; +import android.telephony.ims.internal.aidl.IImsCallSessionListener; +import android.telephony.ims.internal.feature.CapabilityChangeRequest; + +import com.android.ims.ImsCallProfile; +import com.android.ims.internal.IImsCallSession; +import com.android.ims.internal.IImsEcbm; +import com.android.ims.internal.IImsMultiEndpoint; +import com.android.ims.internal.IImsRegistrationListener; +import com.android.ims.internal.IImsUt; + +/** + * See MmTelFeature for more information. + * {@hide} + */ +interface IImsMmTelFeature { + void setListener(IImsMmTelListener l); + int getFeatureState(); + ImsCallProfile createCallProfile(int callSessionType, int callType); + IImsCallSession createCallSession(in ImsCallProfile profile, IImsCallSessionListener listener); + IImsUt getUtInterface(); + IImsEcbm getEcbmInterface(); + void setUiTtyMode(int uiTtyMode, in Message onCompleteMessage); + IImsMultiEndpoint getMultiEndpointInterface(); + int queryCapabilityStatus(); + oneway void addCapabilityCallback(IImsCapabilityCallback c); + oneway void removeCapabilityCallback(IImsCapabilityCallback c); + oneway void changeCapabilitiesConfiguration(in CapabilityChangeRequest request, + IImsCapabilityCallback c); + oneway void queryCapabilityConfiguration(int capability, int radioTech, + IImsCapabilityCallback c); +} diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl new file mode 100644 index 000000000000..8332bc024e37 --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/aidl/IImsMmTelListener.aidl @@ -0,0 +1,27 @@ +/* + * 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.internal.aidl; + +import com.android.ims.internal.IImsCallSession; + +/** + * See MmTelFeature#Listener for more information. + * {@hide} + */ +oneway interface IImsMmTelListener { + void onIncomingCall(IImsCallSession c); +}
\ No newline at end of file diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsRcsFeature.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsRcsFeature.aidl new file mode 100644 index 000000000000..f6005b66bd3c --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/aidl/IImsRcsFeature.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 android.telephony.ims.internal.aidl; + +/** + * See RcsFeature for more information. + * {@hide} + */ +interface IImsRcsFeature { + //Empty Default Implementation +}
\ No newline at end of file diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsRegistration.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsRegistration.aidl new file mode 100644 index 000000000000..687b7ca408d5 --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/aidl/IImsRegistration.aidl @@ -0,0 +1,32 @@ +/* + * 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.telephony.ims.internal.aidl; + +import android.telephony.ims.internal.aidl.IImsRegistrationCallback; +import android.telephony.ims.internal.stub.ImsFeatureConfiguration; + +/** + * See ImsRegistration for more information. + * + * {@hide} + */ +interface IImsRegistration { + int getRegistrationTechnology(); + oneway void addRegistrationCallback(IImsRegistrationCallback c); + oneway void removeRegistrationCallback(IImsRegistrationCallback c); +}
\ No newline at end of file diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsRegistrationCallback.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsRegistrationCallback.aidl new file mode 100644 index 000000000000..a50575b96865 --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/aidl/IImsRegistrationCallback.aidl @@ -0,0 +1,34 @@ +/* + * 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.telephony.ims.internal.aidl; + +import android.telephony.ims.internal.stub.ImsFeatureConfiguration; + +import com.android.ims.ImsReasonInfo; + +/** + * See ImsRegistrationImplBase.Callback for more information. + * + * {@hide} + */ +oneway interface IImsRegistrationCallback { + void onRegistered(int imsRadioTech); + void onRegistering(int imsRadioTech); + void onDeregistered(in ImsReasonInfo info); + void onTechnologyChangeFailed(int imsRadioTech, in ImsReasonInfo info); +}
\ No newline at end of file diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl new file mode 100644 index 000000000000..8afb95588b01 --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/aidl/IImsServiceController.aidl @@ -0,0 +1,44 @@ +/* + * 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.internal.aidl; + +import android.telephony.ims.internal.aidl.IImsMmTelFeature; +import android.telephony.ims.internal.aidl.IImsRcsFeature; +import android.telephony.ims.internal.aidl.IImsRegistration; +import android.telephony.ims.internal.aidl.IImsConfig; +import android.telephony.ims.internal.aidl.IImsServiceControllerListener; +import android.telephony.ims.internal.stub.ImsFeatureConfiguration; + +import com.android.ims.internal.IImsFeatureStatusCallback; + +/** + * See ImsService and MmTelFeature for more information. + * {@hide} + */ +interface IImsServiceController { + void setListener(IImsServiceControllerListener l); + IImsMmTelFeature createMmTelFeature(int slotId, in IImsFeatureStatusCallback c); + IImsRcsFeature createRcsFeature(int slotId, in IImsFeatureStatusCallback c); + ImsFeatureConfiguration querySupportedImsFeatures(); + // Synchronous call to ensure the ImsService is ready before continuing with feature creation. + void notifyImsServiceReadyForFeatureCreation(); + // Synchronous call to ensure the new ImsFeature is ready before using the Feature. + void notifyImsFeatureReady(int slotId, int featureType); + void removeImsFeature(int slotId, int featureType, in IImsFeatureStatusCallback c); + IImsConfig getConfig(int slotId); + IImsRegistration getRegistration(int slotId); +} diff --git a/telephony/java/android/telephony/ims/internal/aidl/IImsServiceControllerListener.aidl b/telephony/java/android/telephony/ims/internal/aidl/IImsServiceControllerListener.aidl new file mode 100644 index 000000000000..01cca2db0978 --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/aidl/IImsServiceControllerListener.aidl @@ -0,0 +1,27 @@ +/* + * 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.internal.aidl; + +import android.telephony.ims.internal.stub.ImsFeatureConfiguration; + +/** + * See ImsService#Listener for more information. + * {@hide} + */ +oneway interface IImsServiceControllerListener { + void onUpdateSupportedImsFeatures(in ImsFeatureConfiguration c); +} diff --git a/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.aidl b/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.aidl new file mode 100644 index 000000000000..f4ec0eb38f34 --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.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.telephony.ims.internal.feature; + +parcelable CapabilityChangeRequest; diff --git a/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.java b/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.java new file mode 100644 index 000000000000..4d188734c10e --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/feature/CapabilityChangeRequest.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.telephony.ims.internal.feature; + +import android.os.Parcel; +import android.os.Parcelable; +import android.telephony.ims.internal.stub.ImsRegistrationImplBase; +import android.util.ArraySet; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +/** + * Request to send to IMS provider, which will try to enable/disable capabilities that are added to + * the request. + * {@hide} + */ +public class CapabilityChangeRequest implements Parcelable { + + public static class CapabilityPair { + private final int mCapability; + private final int radioTech; + + public CapabilityPair(int capability, int radioTech) { + this.mCapability = capability; + this.radioTech = radioTech; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof CapabilityPair)) return false; + + CapabilityPair that = (CapabilityPair) o; + + if (getCapability() != that.getCapability()) return false; + return getRadioTech() == that.getRadioTech(); + } + + @Override + public int hashCode() { + int result = getCapability(); + result = 31 * result + getRadioTech(); + return result; + } + + public @MmTelFeature.MmTelCapabilities.MmTelCapability int getCapability() { + return mCapability; + } + + public @ImsRegistrationImplBase.ImsRegistrationTech int getRadioTech() { + return radioTech; + } + } + + // Pair contains <radio tech, mCapability> + private final Set<CapabilityPair> mCapabilitiesToEnable; + // Pair contains <radio tech, mCapability> + private final Set<CapabilityPair> mCapabilitiesToDisable; + + public CapabilityChangeRequest() { + mCapabilitiesToEnable = new ArraySet<>(); + mCapabilitiesToDisable = new ArraySet<>(); + } + + /** + * Add one or many capabilities to the request to be enabled. + * + * @param capabilities A bitfield of capabilities to enable, valid values are defined in + * {@link MmTelFeature.MmTelCapabilities.MmTelCapability}. + * @param radioTech the radio tech that these capabilities should be enabled for, valid + * values are in {@link ImsRegistrationImplBase.ImsRegistrationTech}. + */ + public void addCapabilitiesToEnableForTech( + @MmTelFeature.MmTelCapabilities.MmTelCapability int capabilities, + @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) { + addAllCapabilities(mCapabilitiesToEnable, capabilities, radioTech); + } + + /** + * Add one or many capabilities to the request to be disabled. + * @param capabilities A bitfield of capabilities to diable, valid values are defined in + * {@link MmTelFeature.MmTelCapabilities.MmTelCapability}. + * @param radioTech the radio tech that these capabilities should be disabled for, valid + * values are in {@link ImsRegistrationImplBase.ImsRegistrationTech}. + */ + public void addCapabilitiesToDisableForTech( + @MmTelFeature.MmTelCapabilities.MmTelCapability int capabilities, + @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) { + addAllCapabilities(mCapabilitiesToDisable, capabilities, radioTech); + } + + /** + * @return a {@link List} of {@link CapabilityPair}s that are requesting to be enabled. + */ + public List<CapabilityPair> getCapabilitiesToEnable() { + return new ArrayList<>(mCapabilitiesToEnable); + } + + /** + * @return a {@link List} of {@link CapabilityPair}s that are requesting to be disabled. + */ + public List<CapabilityPair> getCapabilitiesToDisable() { + return new ArrayList<>(mCapabilitiesToDisable); + } + + // Iterate through capabilities bitfield and add each one as a pair associated with the radio + // technology + private void addAllCapabilities(Set<CapabilityPair> set, int capabilities, int tech) { + long highestCapability = Long.highestOneBit(capabilities); + for (int i = 1; i <= highestCapability; i *= 2) { + if ((i & capabilities) > 0) { + set.add(new CapabilityPair(/*capability*/ i, /*radioTech*/ tech)); + } + } + } + + protected CapabilityChangeRequest(Parcel in) { + int enableSize = in.readInt(); + mCapabilitiesToEnable = new ArraySet<>(enableSize); + for (int i = 0; i < enableSize; i++) { + mCapabilitiesToEnable.add(new CapabilityPair(/*capability*/ in.readInt(), + /*radioTech*/ in.readInt())); + } + int disableSize = in.readInt(); + mCapabilitiesToDisable = new ArraySet<>(disableSize); + for (int i = 0; i < disableSize; i++) { + mCapabilitiesToDisable.add(new CapabilityPair(/*capability*/ in.readInt(), + /*radioTech*/ in.readInt())); + } + } + + public static final Creator<CapabilityChangeRequest> CREATOR = + new Creator<CapabilityChangeRequest>() { + @Override + public CapabilityChangeRequest createFromParcel(Parcel in) { + return new CapabilityChangeRequest(in); + } + + @Override + public CapabilityChangeRequest[] newArray(int size) { + return new CapabilityChangeRequest[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mCapabilitiesToEnable.size()); + for (CapabilityPair pair : mCapabilitiesToEnable) { + dest.writeInt(pair.getCapability()); + dest.writeInt(pair.getRadioTech()); + } + dest.writeInt(mCapabilitiesToDisable.size()); + for (CapabilityPair pair : mCapabilitiesToDisable) { + dest.writeInt(pair.getCapability()); + dest.writeInt(pair.getRadioTech()); + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof CapabilityChangeRequest)) return false; + + CapabilityChangeRequest that = (CapabilityChangeRequest) o; + + if (!mCapabilitiesToEnable.equals(that.mCapabilitiesToEnable)) return false; + return mCapabilitiesToDisable.equals(that.mCapabilitiesToDisable); + } + + @Override + public int hashCode() { + int result = mCapabilitiesToEnable.hashCode(); + result = 31 * result + mCapabilitiesToDisable.hashCode(); + return result; + } +} diff --git a/telephony/java/android/telephony/ims/internal/feature/ImsFeature.java b/telephony/java/android/telephony/ims/internal/feature/ImsFeature.java new file mode 100644 index 000000000000..9f82ad241eaf --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/feature/ImsFeature.java @@ -0,0 +1,462 @@ +/* + * 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.internal.feature; + +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.content.Context; +import android.content.Intent; +import android.os.IInterface; +import android.os.RemoteCallbackList; +import android.os.RemoteException; +import android.telephony.SubscriptionManager; +import android.telephony.ims.internal.aidl.IImsCapabilityCallback; +import android.util.Log; + +import com.android.ims.internal.IImsFeatureStatusCallback; +import com.android.internal.annotations.VisibleForTesting; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Collections; +import java.util.Iterator; +import java.util.Set; +import java.util.WeakHashMap; + +/** + * Base class for all IMS features that are supported by the framework. + * + * @hide + */ +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 for 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 FEATURE_INVALID = -1; + // 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 FEATURE_EMERGENCY_MMTEL = 0; + public static final int FEATURE_MMTEL = 1; + public static final int FEATURE_RCS = 2; + // Total number of features defined + public static final int FEATURE_MAX = 3; + + // Integer values defining IMS features that are supported in ImsFeature. + @IntDef(flag = true, + value = { + FEATURE_EMERGENCY_MMTEL, + FEATURE_MMTEL, + FEATURE_RCS + }) + @Retention(RetentionPolicy.SOURCE) + public @interface FeatureType {} + + // Integer values defining the state of the ImsFeature at any time. + @IntDef(flag = true, + value = { + STATE_UNAVAILABLE, + STATE_INITIALIZING, + STATE_READY, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ImsState {} + + public static final int STATE_UNAVAILABLE = 0; + public static final int STATE_INITIALIZING = 1; + public static final int STATE_READY = 2; + + // Integer values defining the result codes that should be returned from + // {@link changeEnabledCapabilities} when the framework tries to set a feature's capability. + @IntDef(flag = true, + value = { + CAPABILITY_ERROR_GENERIC, + CAPABILITY_SUCCESS + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ImsCapabilityError {} + + public static final int CAPABILITY_ERROR_GENERIC = -1; + public static final int CAPABILITY_SUCCESS = 0; + + + /** + * The framework implements this callback in order to register for Feature Capability status + * updates, via {@link #onCapabilitiesStatusChanged(Capabilities)}, query Capability + * configurations, via {@link #onQueryCapabilityConfiguration}, as well as to receive error + * callbacks when the ImsService can not change the capability as requested, via + * {@link #onChangeCapabilityConfigurationError}. + */ + public static class CapabilityCallback extends IImsCapabilityCallback.Stub { + + @Override + public final void onCapabilitiesStatusChanged(int config) throws RemoteException { + onCapabilitiesStatusChanged(new Capabilities(config)); + } + + /** + * Returns the result of a query for the capability configuration of a requested capability. + * + * @param capability The capability that was requested. + * @param radioTech The IMS radio technology associated with the capability. + * @param isEnabled true if the capability is enabled, false otherwise. + */ + @Override + public void onQueryCapabilityConfiguration(int capability, int radioTech, + boolean isEnabled) { + + } + + /** + * Called when a change to the capability configuration has returned an error. + * + * @param capability The capability that was requested to be changed. + * @param radioTech The IMS radio technology associated with the capability. + * @param reason error associated with the failure to change configuration. + */ + @Override + public void onChangeCapabilityConfigurationError(int capability, int radioTech, + int reason) { + } + + /** + * The status of the feature's capabilities has changed to either available or unavailable. + * If unavailable, the feature is not able to support the unavailable capability at this + * time. + * + * @param config The new availability of the capabilities. + */ + public void onCapabilitiesStatusChanged(Capabilities config) { + } + } + + /** + * Used by the ImsFeature to call back to the CapabilityCallback that the framework has + * provided. + */ + protected static class CapabilityCallbackProxy { + private final IImsCapabilityCallback mCallback; + + public CapabilityCallbackProxy(IImsCapabilityCallback c) { + mCallback = c; + } + + /** + * This method notifies the provided framework callback that the request to change the + * indicated capability has failed and has not changed. + * + * @param capability The Capability that will be notified to the framework. + * @param radioTech The radio tech that this capability failed for. + * @param reason The reason this capability was unable to be changed. + */ + public void onChangeCapabilityConfigurationError(int capability, int radioTech, + @ImsCapabilityError int reason) { + try { + mCallback.onChangeCapabilityConfigurationError(capability, radioTech, reason); + } catch (RemoteException e) { + Log.e(LOG_TAG, "onChangeCapabilityConfigurationError called on dead binder."); + } + } + + public void onQueryCapabilityConfiguration(int capability, int radioTech, + boolean isEnabled) { + try { + mCallback.onQueryCapabilityConfiguration(capability, radioTech, isEnabled); + } catch (RemoteException e) { + Log.e(LOG_TAG, "onQueryCapabilityConfiguration called on dead binder."); + } + } + } + + /** + * Contains the capabilities defined and supported by an ImsFeature in the form of a bit mask. + */ + public static class Capabilities { + protected int mCapabilities = 0; + + public Capabilities() { + } + + protected Capabilities(int capabilities) { + mCapabilities = capabilities; + } + + /** + * @param capabilities Capabilities to be added to the configuration in the form of a + * bit mask. + */ + public void addCapabilities(int capabilities) { + mCapabilities |= capabilities; + } + + /** + * @param capabilities Capabilities to be removed to the configuration in the form of a + * bit mask. + */ + public void removeCapabilities(int capabilities) { + mCapabilities &= ~capabilities; + } + + /** + * @return true if all of the capabilities specified are capable. + */ + public boolean isCapable(int capabilities) { + return (mCapabilities & capabilities) == capabilities; + } + + public Capabilities copy() { + return new Capabilities(mCapabilities); + } + + /** + * @return a bitmask containing the capability flags directly. + */ + public int getMask() { + return mCapabilities; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Capabilities)) return false; + + Capabilities that = (Capabilities) o; + + return mCapabilities == that.mCapabilities; + } + + @Override + public int hashCode() { + return mCapabilities; + } + } + + private final Set<IImsFeatureStatusCallback> mStatusCallbacks = Collections.newSetFromMap( + new WeakHashMap<IImsFeatureStatusCallback, Boolean>()); + private @ImsState int mState = STATE_UNAVAILABLE; + private int mSlotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX; + private Context mContext; + private final Object mLock = new Object(); + private final RemoteCallbackList<IImsCapabilityCallback> mCapabilityCallbacks + = new RemoteCallbackList<>(); + private Capabilities mCapabilityStatus = new Capabilities(); + + public final void initialize(Context context, int slotId) { + mContext = context; + mSlotId = slotId; + } + + public final int getFeatureState() { + synchronized (mLock) { + return mState; + } + } + + protected final void setFeatureState(@ImsState int state) { + synchronized (mLock) { + if (mState != state) { + mState = state; + notifyFeatureState(state); + } + } + } + + // Not final for testing, but shouldn't be extended! + @VisibleForTesting + public void addImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) { + try { + // If we have just connected, send queued status. + c.notifyImsFeatureStatus(getFeatureState()); + // Add the callback if the callback completes successfully without a RemoteException. + synchronized (mLock) { + mStatusCallbacks.add(c); + } + } catch (RemoteException e) { + Log.w(LOG_TAG, "Couldn't notify feature state: " + e.getMessage()); + } + } + + @VisibleForTesting + // Not final for testing, but should not be extended! + public void removeImsFeatureStatusCallback(@NonNull IImsFeatureStatusCallback c) { + synchronized (mLock) { + mStatusCallbacks.remove(c); + } + } + + /** + * Internal method called by ImsFeature when setFeatureState has changed. + */ + private void notifyFeatureState(@ImsState int state) { + synchronized (mLock) { + for (Iterator<IImsFeatureStatusCallback> iter = mStatusCallbacks.iterator(); + iter.hasNext(); ) { + IImsFeatureStatusCallback callback = iter.next(); + try { + Log.i(LOG_TAG, "notifying ImsFeatureState=" + state); + callback.notifyImsFeatureStatus(state); + } catch (RemoteException e) { + // remove if the callback is no longer alive. + iter.remove(); + 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_UNAVAILABLE: + 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); + } + + public final void addCapabilityCallback(IImsCapabilityCallback c) { + mCapabilityCallbacks.register(c); + } + + public final void removeCapabilityCallback(IImsCapabilityCallback c) { + mCapabilityCallbacks.unregister(c); + } + + /** + * @return the cached capabilities status for this feature. + */ + @VisibleForTesting + public Capabilities queryCapabilityStatus() { + synchronized (mLock) { + return mCapabilityStatus.copy(); + } + } + + // Called internally to request the change of enabled capabilities. + @VisibleForTesting + public final void requestChangeEnabledCapabilities(CapabilityChangeRequest request, + IImsCapabilityCallback c) throws RemoteException { + if (request == null) { + throw new IllegalArgumentException( + "ImsFeature#requestChangeEnabledCapabilities called with invalid params."); + } + changeEnabledCapabilities(request, new CapabilityCallbackProxy(c)); + } + + /** + * Called by the ImsFeature when the capabilities status has changed. + * + * @param c A {@link Capabilities} containing the new Capabilities status. + */ + protected final void notifyCapabilitiesStatusChanged(Capabilities c) { + synchronized (mLock) { + mCapabilityStatus = c.copy(); + } + int count = mCapabilityCallbacks.beginBroadcast(); + try { + for (int i = 0; i < count; i++) { + try { + mCapabilityCallbacks.getBroadcastItem(i).onCapabilitiesStatusChanged( + c.mCapabilities); + } catch (RemoteException e) { + Log.w(LOG_TAG, e + " " + "notifyCapabilitiesStatusChanged() - Skipping " + + "callback."); + } + } + } finally { + mCapabilityCallbacks.finishBroadcast(); + } + } + + /** + * Features should override this method to receive Capability preference change requests from + * the framework using the provided {@link CapabilityChangeRequest}. If any of the capabilities + * in the {@link CapabilityChangeRequest} are not able to be completed due to an error, + * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError} should be called for + * each failed capability. + * + * @param request A {@link CapabilityChangeRequest} containing requested capabilities to + * enable/disable. + * @param c A {@link CapabilityCallbackProxy}, which will be used to call back to the framework + * setting a subset of these capabilities fail, using + * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError}. + */ + public abstract void changeEnabledCapabilities(CapabilityChangeRequest request, + CapabilityCallbackProxy c); + + /** + * Called when the framework is removing this feature and it needs to be cleaned up. + */ + public abstract void onFeatureRemoved(); + + /** + * Called when the feature has been initialized and communication with the framework is set up. + * Any attempt by this feature to access the framework before this method is called will return + * with an {@link IllegalStateException}. + * The IMS provider should use this method to trigger registration for this feature on the IMS + * network, if needed. + */ + public abstract void onFeatureReady(); + + /** + * @return Binder instance that the framework will use to communicate with this feature. + */ + protected abstract IInterface getBinder(); +} diff --git a/telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java b/telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java new file mode 100644 index 000000000000..f183a57e77fc --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/feature/MmTelFeature.java @@ -0,0 +1,422 @@ +/* + * 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.internal.feature; + +import android.annotation.IntDef; +import android.os.Message; +import android.os.RemoteException; +import android.telecom.TelecomManager; +import android.telephony.ims.internal.ImsCallSessionListener; +import android.telephony.ims.internal.aidl.IImsCallSessionListener; +import android.telephony.ims.internal.aidl.IImsCapabilityCallback; +import android.telephony.ims.internal.aidl.IImsMmTelFeature; +import android.telephony.ims.internal.aidl.IImsMmTelListener; +import android.telephony.ims.internal.stub.ImsRegistrationImplBase; +import android.telephony.ims.stub.ImsEcbmImplBase; +import android.telephony.ims.stub.ImsMultiEndpointImplBase; +import android.telephony.ims.stub.ImsUtImplBase; +import android.util.Log; + +import com.android.ims.ImsCallProfile; +import com.android.ims.internal.IImsCallSession; +import com.android.ims.internal.IImsEcbm; +import com.android.ims.internal.IImsMultiEndpoint; +import com.android.ims.internal.IImsUt; +import com.android.ims.internal.ImsCallSession; +import com.android.internal.annotations.VisibleForTesting; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Base implementation for Voice and SMS (IR-92) and Video (IR-94) IMS support. + * + * Any class wishing to use MmTelFeature should extend this class and implement all methods that the + * service supports. + * @hide + */ + +public class MmTelFeature extends ImsFeature { + + private static final String LOG_TAG = "MmTelFeature"; + + private final IImsMmTelFeature mImsMMTelBinder = new IImsMmTelFeature.Stub() { + + @Override + public void setListener(IImsMmTelListener l) throws RemoteException { + synchronized (mLock) { + MmTelFeature.this.setListener(l); + } + } + + @Override + public int getFeatureState() throws RemoteException { + synchronized (mLock) { + return MmTelFeature.this.getFeatureState(); + } + } + + + @Override + public ImsCallProfile createCallProfile(int callSessionType, int callType) + throws RemoteException { + synchronized (mLock) { + return MmTelFeature.this.createCallProfile(callSessionType, callType); + } + } + + @Override + public IImsCallSession createCallSession(ImsCallProfile profile, + IImsCallSessionListener listener) throws RemoteException { + synchronized (mLock) { + ImsCallSession s = MmTelFeature.this.createCallSession(profile, + new ImsCallSessionListener(listener)); + return s != null ? s.getSession() : null; + } + } + + @Override + public IImsUt getUtInterface() throws RemoteException { + synchronized (mLock) { + return MmTelFeature.this.getUt(); + } + } + + @Override + public IImsEcbm getEcbmInterface() throws RemoteException { + synchronized (mLock) { + return MmTelFeature.this.getEcbm(); + } + } + + @Override + public void setUiTtyMode(int uiTtyMode, Message onCompleteMessage) throws RemoteException { + synchronized (mLock) { + MmTelFeature.this.setUiTtyMode(uiTtyMode, onCompleteMessage); + } + } + + @Override + public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException { + synchronized (mLock) { + return MmTelFeature.this.getMultiEndpoint(); + } + } + + @Override + public int queryCapabilityStatus() throws RemoteException { + return MmTelFeature.this.queryCapabilityStatus().mCapabilities; + } + + @Override + public void addCapabilityCallback(IImsCapabilityCallback c) { + MmTelFeature.this.addCapabilityCallback(c); + } + + @Override + public void removeCapabilityCallback(IImsCapabilityCallback c) { + MmTelFeature.this.removeCapabilityCallback(c); + } + + @Override + public void changeCapabilitiesConfiguration(CapabilityChangeRequest request, + IImsCapabilityCallback c) throws RemoteException { + MmTelFeature.this.requestChangeEnabledCapabilities(request, c); + } + + @Override + public void queryCapabilityConfiguration(int capability, int radioTech, + IImsCapabilityCallback c) { + queryCapabilityConfigurationInternal(capability, radioTech, c); + } + }; + + /** + * Contains the capabilities defined and supported by a MmTelFeature in the form of a Bitmask. + * The capabilities that are used in MmTelFeature are defined by {@link MmTelCapability}. + * + * The capabilities of this MmTelFeature will be set by the framework and can be queried with + * {@link #queryCapabilityStatus()}. + * + * This MmTelFeature can then return the status of each of these capabilities (enabled or not) + * by sending a {@link #notifyCapabilitiesStatusChanged} callback to the framework. The current + * status can also be queried using {@link #queryCapabilityStatus()}. + */ + public static class MmTelCapabilities extends Capabilities { + + @VisibleForTesting + public MmTelCapabilities() { + super(); + } + + public MmTelCapabilities(Capabilities c) { + mCapabilities = c.mCapabilities; + } + + @IntDef(flag = true, + value = { + CAPABILITY_TYPE_VOICE, + CAPABILITY_TYPE_VIDEO, + CAPABILITY_TYPE_UT + }) + @Retention(RetentionPolicy.SOURCE) + public @interface MmTelCapability {} + + /** + * This MmTelFeature supports Voice calling (IR.92) + */ + public static final int CAPABILITY_TYPE_VOICE = 1 << 0; + + /** + * This MmTelFeature supports Video (IR.94) + */ + public static final int CAPABILITY_TYPE_VIDEO = 1 << 1; + + /** + * This MmTelFeature supports XCAP over Ut for supplementary services. (IR.92) + */ + public static final int CAPABILITY_TYPE_UT = 1 << 2; + + @Override + public final void addCapabilities(@MmTelCapability int capabilities) { + super.addCapabilities(capabilities); + } + + @Override + public final void removeCapabilities(@MmTelCapability int capability) { + super.removeCapabilities(capability); + } + + @Override + public final boolean isCapable(@MmTelCapability int capabilities) { + return super.isCapable(capabilities); + } + } + + /** + * Listener that the framework implements for communication from the MmTelFeature. + */ + public static class Listener extends IImsMmTelListener.Stub { + + @Override + public final void onIncomingCall(IImsCallSession c) { + onIncomingCall(new ImsCallSession(c)); + } + + /** + * Called when the IMS provider receives an incoming call. + * @param c The {@link ImsCallSession} associated with the new call. + */ + public void onIncomingCall(ImsCallSession c) { + } + } + + // Lock for feature synchronization + private final Object mLock = new Object(); + private IImsMmTelListener mListener; + + /** + * @param listener A {@link Listener} used when the MmTelFeature receives an incoming call and + * notifies the framework. + */ + private void setListener(IImsMmTelListener listener) { + synchronized (mLock) { + mListener = listener; + } + } + + private void queryCapabilityConfigurationInternal(int capability, int radioTech, + IImsCapabilityCallback c) { + boolean enabled = queryCapabilityConfiguration(capability, radioTech); + try { + if (c != null) { + c.onQueryCapabilityConfiguration(capability, radioTech, enabled); + } + } catch (RemoteException e) { + Log.e(LOG_TAG, "queryCapabilityConfigurationInternal called on dead binder!"); + } + } + + /** + * The current capability status that this MmTelFeature has defined is available. This + * configuration will be used by the platform to figure out which capabilities are CURRENTLY + * available to be used. + * + * Should be a subset of the capabilities that are enabled by the framework in + * {@link #changeEnabledCapabilities}. + * @return A copy of the current MmTelFeature capability status. + */ + @Override + public final MmTelCapabilities queryCapabilityStatus() { + return new MmTelCapabilities(super.queryCapabilityStatus()); + } + + /** + * Notify the framework that the status of the Capabilities has changed. Even though the + * MmTelFeature capability may be enabled by the framework, the status may be disabled due to + * the feature being unavailable from the network. + * @param c The current capability status of the MmTelFeature. If a capability is disabled, then + * the status of that capability is disabled. This can happen if the network does not currently + * support the capability that is enabled. A capability that is disabled by the framework (via + * {@link #changeEnabledCapabilities}) should also show the status as disabled. + */ + protected final void notifyCapabilitiesStatusChanged(MmTelCapabilities c) { + super.notifyCapabilitiesStatusChanged(c); + } + + /** + * Notify the framework of an incoming call. + * @param c The {@link ImsCallSession} of the new incoming call. + * + * @throws RemoteException if the connection to the framework is not available. If this happens, + * the call should be no longer considered active and should be cleaned up. + * */ + protected final void notifyIncomingCall(ImsCallSession c) throws RemoteException { + synchronized (mLock) { + if (mListener == null) { + throw new IllegalStateException("Session is not available."); + } + mListener.onIncomingCall(c.getSession()); + } + } + + /** + * Provides the MmTelFeature with the ability to return the framework Capability Configuration + * for a provided Capability. If the framework calls {@link #changeEnabledCapabilities} and + * includes a capability A to enable or disable, this method should return the correct enabled + * status for capability A. + * @param capability The capability that we are querying the configuration for. + * @return true if the capability is enabled, false otherwise. + */ + public boolean queryCapabilityConfiguration(@MmTelCapabilities.MmTelCapability int capability, + @ImsRegistrationImplBase.ImsRegistrationTech int radioTech) { + // Base implementation - Override to provide functionality + return false; + } + + /** + * The MmTelFeature should override this method to handle the enabling/disabling of + * MmTel Features, defined in {@link MmTelCapabilities.MmTelCapability}. The framework assumes + * the {@link CapabilityChangeRequest} was processed successfully. If a subset of capabilities + * could not be set to their new values, + * {@link CapabilityCallbackProxy#onChangeCapabilityConfigurationError} must be called + * individually for each capability whose processing resulted in an error. + * + * Enabling/Disabling a capability here indicates that the capability should be registered or + * deregistered (depending on the capability change) and become available or unavailable to + * the framework. + */ + @Override + public void changeEnabledCapabilities(CapabilityChangeRequest request, + CapabilityCallbackProxy c) { + // Base implementation, no-op + } + + /** + * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state. + * + * @param callSessionType 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 + */ + public ImsCallProfile createCallProfile(int callSessionType, int callType) { + // Base Implementation - Should be overridden + return null; + } + + /** + * Creates an {@link ImsCallSession} with the specified call profile. + * Use other methods, if applicable, instead of interacting with + * {@link ImsCallSession} directly. + * + * @param profile a call profile to make the call + * @param listener An implementation of IImsCallSessionListener. + */ + public ImsCallSession createCallSession(ImsCallProfile profile, + ImsCallSessionListener listener) { + // Base Implementation - Should be overridden + return null; + } + + /** + * @return The Ut interface for the supplementary service configuration. + */ + public ImsUtImplBase getUt() { + // Base Implementation - Should be overridden + return null; + } + + /** + * @return The Emergency call-back mode interface for emergency VoLTE calls that support it. + */ + public ImsEcbmImplBase getEcbm() { + // Base Implementation - Should be overridden + return null; + } + + /** + * @return The Emergency call-back mode interface for emergency VoLTE calls that support it. + */ + public ImsMultiEndpointImplBase getMultiEndpoint() { + // Base Implementation - Should be overridden + return null; + } + + /** + * Sets the current UI TTY mode for the MmTelFeature. + * @param mode An integer containing the new UI TTY Mode, can consist of + * {@link TelecomManager#TTY_MODE_OFF}, + * {@link TelecomManager#TTY_MODE_FULL}, + * {@link TelecomManager#TTY_MODE_HCO}, + * {@link TelecomManager#TTY_MODE_VCO} + * @param onCompleteMessage A {@link Message} to be used when the mode has been set. + */ + void setUiTtyMode(int mode, Message onCompleteMessage) { + // Base Implementation - Should be overridden + } + + /**{@inheritDoc}*/ + @Override + public void onFeatureRemoved() { + // Base Implementation - Should be overridden + } + + /**{@inheritDoc}*/ + @Override + public void onFeatureReady() { + // Base Implementation - Should be overridden + } + + /** + * @hide + */ + @Override + public final IImsMmTelFeature getBinder() { + return mImsMMTelBinder; + } +} diff --git a/telephony/java/android/telephony/ims/internal/feature/RcsFeature.java b/telephony/java/android/telephony/ims/internal/feature/RcsFeature.java new file mode 100644 index 000000000000..8d1bd9d27f7c --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/feature/RcsFeature.java @@ -0,0 +1,59 @@ +/* + * 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.internal.feature; + +import android.telephony.ims.internal.aidl.IImsRcsFeature; + +/** + * Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend + * this class and provide implementations of the RcsFeature methods that they support. + * @hide + */ + +public class RcsFeature extends ImsFeature { + + private final IImsRcsFeature mImsRcsBinder = new IImsRcsFeature.Stub() { + // Empty Default Implementation. + }; + + + public RcsFeature() { + super(); + } + + @Override + public void changeEnabledCapabilities(CapabilityChangeRequest request, + CapabilityCallbackProxy c) { + // Do nothing for base implementation. + } + + @Override + public void onFeatureRemoved() { + + } + + /**{@inheritDoc}*/ + @Override + public void onFeatureReady() { + + } + + @Override + public final IImsRcsFeature getBinder() { + return mImsRcsBinder; + } +} diff --git a/telephony/java/android/telephony/ims/internal/stub/ImsConfigImplBase.java b/telephony/java/android/telephony/ims/internal/stub/ImsConfigImplBase.java new file mode 100644 index 000000000000..33aec5dfb8af --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/stub/ImsConfigImplBase.java @@ -0,0 +1,173 @@ +/* + * 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.internal.stub; + +import android.os.RemoteCallbackList; +import android.os.RemoteException; +import android.telephony.ims.internal.aidl.IImsConfig; +import android.telephony.ims.internal.aidl.IImsConfigCallback; + +import com.android.ims.ImsConfig; + +/** + * Controls the modification of IMS specific configurations. For more information on the supported + * IMS configuration constants, see {@link ImsConfig}. + * + * @hide + */ + +public class ImsConfigImplBase { + + //TODO: Implement the Binder logic to call base APIs. Need to finish other ImsService Config + // work first. + private final IImsConfig mBinder = new IImsConfig.Stub() { + + @Override + public void addImsConfigCallback(IImsConfigCallback c) throws RemoteException { + ImsConfigImplBase.this.addImsConfigCallback(c); + } + + @Override + public void removeImsConfigCallback(IImsConfigCallback c) throws RemoteException { + ImsConfigImplBase.this.removeImsConfigCallback(c); + } + + @Override + public int getConfigInt(int item) throws RemoteException { + return Integer.MIN_VALUE; + } + + @Override + public String getConfigString(int item) throws RemoteException { + return null; + } + + @Override + public int setConfigInt(int item, int value) throws RemoteException { + return Integer.MIN_VALUE; + } + + @Override + public int setConfigString(int item, String value) throws RemoteException { + return Integer.MIN_VALUE; + } + }; + + public class Callback extends IImsConfigCallback.Stub { + + @Override + public final void onIntConfigChanged(int item, int value) throws RemoteException { + onConfigChanged(item, value); + } + + @Override + public final void onStringConfigChanged(int item, String value) throws RemoteException { + onConfigChanged(item, value); + } + + /** + * Called when the IMS configuration has changed. + * @param item the IMS configuration key constant, as defined in ImsConfig. + * @param value the new integer value of the IMS configuration constant. + */ + public void onConfigChanged(int item, int value) { + // Base Implementation + } + + /** + * Called when the IMS configuration has changed. + * @param item the IMS configuration key constant, as defined in ImsConfig. + * @param value the new String value of the IMS configuration constant. + */ + public void onConfigChanged(int item, String value) { + // Base Implementation + } + } + + private final RemoteCallbackList<IImsConfigCallback> mCallbacks = new RemoteCallbackList<>(); + + /** + * Adds a {@link Callback} to the list of callbacks notified when a value in the configuration + * changes. + * @param c callback to add. + */ + private void addImsConfigCallback(IImsConfigCallback c) { + mCallbacks.register(c); + } + /** + * Removes a {@link Callback} to the list of callbacks notified when a value in the + * configuration changes. + * + * @param c callback to remove. + */ + private void removeImsConfigCallback(IImsConfigCallback c) { + mCallbacks.unregister(c); + } + + public final IImsConfig getBinder() { + return mBinder; + } + + /** + * 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. + * + * @param item as defined in com.android.ims.ImsConfig#ConfigConstants. + * @param value in Integer format. + * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants. + */ + public int setConfig(int item, int value) { + // Base Implementation - To be overridden. + 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. + * + * @param item as defined in com.android.ims.ImsConfig#ConfigConstants. + * @param value in String format. + * @return as defined in com.android.ims.ImsConfig#OperationStatusConstants. + */ + public int setConfig(int item, String value) { + return ImsConfig.OperationStatusConstants.FAILED; + } + + /** + * Gets the value for ims service/capabilities parameters from the provisioned + * value storage. + * + * @param item as defined in com.android.ims.ImsConfig#ConfigConstants. + * @return value in Integer format. + */ + public int getConfigInt(int item) { + return ImsConfig.OperationStatusConstants.FAILED; + } + + /** + * Gets the value for ims service/capabilities parameters from the provisioned + * value storage. + * + * @param item as defined in com.android.ims.ImsConfig#ConfigConstants. + * @return value in String format. + */ + public String getConfigString(int item) { + return null; + } +} diff --git a/telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.aidl b/telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.aidl new file mode 100644 index 000000000000..e890cf8756f3 --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.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.telephony.ims.internal.stub; + +parcelable ImsFeatureConfiguration; diff --git a/telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.java b/telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.java new file mode 100644 index 000000000000..244c9578f6b4 --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/stub/ImsFeatureConfiguration.java @@ -0,0 +1,147 @@ +/* + * 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.internal.stub; + +import android.os.Parcel; +import android.os.Parcelable; +import android.telephony.ims.internal.feature.ImsFeature; +import android.util.ArraySet; + +import java.util.Arrays; +import java.util.Set; + +/** + * Container class for IMS Feature configuration. This class contains the features that the + * ImsService supports, which are defined in {@link ImsFeature.FeatureType}. + * @hide + */ +public class ImsFeatureConfiguration implements Parcelable { + /** + * Features that this ImsService supports. + */ + private final Set<Integer> mFeatures; + + /** + * Creates an ImsFeatureConfiguration with the features + */ + public static class Builder { + ImsFeatureConfiguration mConfig; + public Builder() { + mConfig = new ImsFeatureConfiguration(); + } + + /** + * @param feature A feature defined in {@link ImsFeature.FeatureType} that this service + * supports. + * @return a {@link Builder} to continue constructing the ImsFeatureConfiguration. + */ + public Builder addFeature(@ImsFeature.FeatureType int feature) { + mConfig.addFeature(feature); + return this; + } + + public ImsFeatureConfiguration build() { + return mConfig; + } + } + + /** + * Creates with all registration features empty. + * + * Consider using the provided {@link Builder} to create this configuration instead. + */ + public ImsFeatureConfiguration() { + mFeatures = new ArraySet<>(); + } + + /** + * Configuration of the ImsService, which describes which features the ImsService supports + * (for registration). + * @param features an array of feature integers defined in {@link ImsFeature} that describe + * which features this ImsService supports. + */ + public ImsFeatureConfiguration(int[] features) { + mFeatures = new ArraySet<>(); + + if (features != null) { + for (int i : features) { + mFeatures.add(i); + } + } + } + + /** + * @return an int[] containing the features that this ImsService supports. + */ + public int[] getServiceFeatures() { + return mFeatures.stream().mapToInt(i->i).toArray(); + } + + void addFeature(int feature) { + mFeatures.add(feature); + } + + protected ImsFeatureConfiguration(Parcel in) { + int[] features = in.createIntArray(); + if (features != null) { + mFeatures = new ArraySet<>(features.length); + for(Integer i : features) { + mFeatures.add(i); + } + } else { + mFeatures = new ArraySet<>(); + } + } + + public static final Creator<ImsFeatureConfiguration> CREATOR + = new Creator<ImsFeatureConfiguration>() { + @Override + public ImsFeatureConfiguration createFromParcel(Parcel in) { + return new ImsFeatureConfiguration(in); + } + + @Override + public ImsFeatureConfiguration[] newArray(int size) { + return new ImsFeatureConfiguration[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeIntArray(mFeatures.stream().mapToInt(i->i).toArray()); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ImsFeatureConfiguration)) return false; + + ImsFeatureConfiguration that = (ImsFeatureConfiguration) o; + + return mFeatures.equals(that.mFeatures); + } + + @Override + public int hashCode() { + return mFeatures.hashCode(); + } +} diff --git a/telephony/java/android/telephony/ims/internal/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/internal/stub/ImsRegistrationImplBase.java new file mode 100644 index 000000000000..558b009ab4c2 --- /dev/null +++ b/telephony/java/android/telephony/ims/internal/stub/ImsRegistrationImplBase.java @@ -0,0 +1,276 @@ +/* + * 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.internal.stub; + +import android.annotation.IntDef; +import android.os.RemoteCallbackList; +import android.os.RemoteException; +import android.telephony.ims.internal.aidl.IImsRegistration; +import android.telephony.ims.internal.aidl.IImsRegistrationCallback; +import android.util.Log; + +import com.android.ims.ImsReasonInfo; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Controls IMS registration for this ImsService and notifies the framework when the IMS + * registration for this ImsService has changed status. + * @hide + */ + +public class ImsRegistrationImplBase { + + private static final String LOG_TAG = "ImsRegistrationImplBase"; + + // Defines the underlying radio technology type that we have registered for IMS over. + @IntDef(flag = true, + value = { + REGISTRATION_TECH_NONE, + REGISTRATION_TECH_LTE, + REGISTRATION_TECH_IWLAN + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ImsRegistrationTech {} + /** + * No registration technology specified, used when we are not registered. + */ + public static final int REGISTRATION_TECH_NONE = -1; + /** + * IMS is registered to IMS via LTE. + */ + public static final int REGISTRATION_TECH_LTE = 0; + /** + * IMS is registered to IMS via IWLAN. + */ + public static final int REGISTRATION_TECH_IWLAN = 1; + + // Registration states, used to notify new ImsRegistrationImplBase#Callbacks of the current + // state. + private static final int REGISTRATION_STATE_NOT_REGISTERED = 0; + private static final int REGISTRATION_STATE_REGISTERING = 1; + private static final int REGISTRATION_STATE_REGISTERED = 2; + + + /** + * Callback class for receiving Registration callback events. + */ + public static class Callback extends IImsRegistrationCallback.Stub { + + /** + * Notifies the framework when the IMS Provider is connected to the IMS network. + * + * @param imsRadioTech the radio access technology. Valid values are defined in + * {@link ImsRegistrationTech}. + */ + @Override + public void onRegistered(@ImsRegistrationTech int imsRadioTech) { + } + + /** + * Notifies the framework when the IMS Provider is trying to connect the IMS network. + * + * @param imsRadioTech the radio access technology. Valid values are defined in + * {@link ImsRegistrationTech}. + */ + @Override + public void onRegistering(@ImsRegistrationTech int imsRadioTech) { + } + + /** + * Notifies the framework when the IMS Provider is disconnected from the IMS network. + * + * @param info the {@link ImsReasonInfo} associated with why registration was disconnected. + */ + @Override + public void onDeregistered(ImsReasonInfo info) { + } + + /** + * A failure has occurred when trying to handover registration to another technology type, + * defined in {@link ImsRegistrationTech} + * + * @param imsRadioTech The {@link ImsRegistrationTech} type that has failed + * @param info A {@link ImsReasonInfo} that identifies the reason for failure. + */ + @Override + public void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech, + ImsReasonInfo info) { + } + } + + private final IImsRegistration mBinder = new IImsRegistration.Stub() { + + @Override + public @ImsRegistrationTech int getRegistrationTechnology() throws RemoteException { + return getConnectionType(); + } + + @Override + public void addRegistrationCallback(IImsRegistrationCallback c) throws RemoteException { + ImsRegistrationImplBase.this.addRegistrationCallback(c); + } + + @Override + public void removeRegistrationCallback(IImsRegistrationCallback c) throws RemoteException { + ImsRegistrationImplBase.this.removeRegistrationCallback(c); + } + }; + + private final RemoteCallbackList<IImsRegistrationCallback> mCallbacks + = new RemoteCallbackList<>(); + private final Object mLock = new Object(); + // Locked on mLock + private @ImsRegistrationTech + int mConnectionType = REGISTRATION_TECH_NONE; + // Locked on mLock + private int mRegistrationState = REGISTRATION_STATE_NOT_REGISTERED; + // Locked on mLock + private ImsReasonInfo mLastDisconnectCause; + + public final IImsRegistration getBinder() { + return mBinder; + } + + private void addRegistrationCallback(IImsRegistrationCallback c) throws RemoteException { + mCallbacks.register(c); + updateNewCallbackWithState(c); + } + + private void removeRegistrationCallback(IImsRegistrationCallback c) { + mCallbacks.unregister(c); + } + + /** + * Notify the framework that the device is connected to the IMS network. + * + * @param imsRadioTech the radio access technology. Valid values are defined in + * {@link ImsRegistrationTech}. + */ + public final void onRegistered(@ImsRegistrationTech int imsRadioTech) { + updateToState(imsRadioTech, REGISTRATION_STATE_REGISTERED); + mCallbacks.broadcast((c) -> { + try { + c.onRegistered(imsRadioTech); + } catch (RemoteException e) { + Log.w(LOG_TAG, e + " " + "onRegistrationConnected() - Skipping " + + "callback."); + } + }); + } + + /** + * Notify the framework that the device is trying to connect the IMS network. + * + * @param imsRadioTech the radio access technology. Valid values are defined in + * {@link ImsRegistrationTech}. + */ + public final void onRegistering(@ImsRegistrationTech int imsRadioTech) { + updateToState(imsRadioTech, REGISTRATION_STATE_REGISTERING); + mCallbacks.broadcast((c) -> { + try { + c.onRegistering(imsRadioTech); + } catch (RemoteException e) { + Log.w(LOG_TAG, e + " " + "onRegistrationProcessing() - Skipping " + + "callback."); + } + }); + } + + /** + * Notify the framework that the device is disconnected from the IMS network. + * + * @param info the {@link ImsReasonInfo} associated with why registration was disconnected. + */ + public final void onDeregistered(ImsReasonInfo info) { + updateToDisconnectedState(info); + mCallbacks.broadcast((c) -> { + try { + c.onDeregistered(info); + } catch (RemoteException e) { + Log.w(LOG_TAG, e + " " + "onRegistrationDisconnected() - Skipping " + + "callback."); + } + }); + } + + public final void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech, + ImsReasonInfo info) { + mCallbacks.broadcast((c) -> { + try { + c.onTechnologyChangeFailed(imsRadioTech, info); + } catch (RemoteException e) { + Log.w(LOG_TAG, e + " " + "onRegistrationChangeFailed() - Skipping " + + "callback."); + } + }); + } + + private void updateToState(@ImsRegistrationTech int connType, int newState) { + synchronized (mLock) { + mConnectionType = connType; + mRegistrationState = newState; + mLastDisconnectCause = null; + } + } + + private void updateToDisconnectedState(ImsReasonInfo info) { + synchronized (mLock) { + updateToState(REGISTRATION_TECH_NONE, REGISTRATION_STATE_NOT_REGISTERED); + if (info != null) { + mLastDisconnectCause = info; + } else { + Log.w(LOG_TAG, "updateToDisconnectedState: no ImsReasonInfo provided."); + mLastDisconnectCause = new ImsReasonInfo(); + } + } + } + + private @ImsRegistrationTech int getConnectionType() { + synchronized (mLock) { + return mConnectionType; + } + } + + /** + * @param c the newly registered callback that will be updated with the current registration + * state. + */ + private void updateNewCallbackWithState(IImsRegistrationCallback c) throws RemoteException { + int state; + ImsReasonInfo disconnectInfo; + synchronized (mLock) { + state = mRegistrationState; + disconnectInfo = mLastDisconnectCause; + } + switch (state) { + case REGISTRATION_STATE_NOT_REGISTERED: { + c.onDeregistered(disconnectInfo); + break; + } + case REGISTRATION_STATE_REGISTERING: { + c.onRegistering(getConnectionType()); + break; + } + case REGISTRATION_STATE_REGISTERED: { + c.onRegistered(getConnectionType()); + break; + } + } + } +} diff --git a/tests/net/java/android/net/MacAddressTest.java b/tests/net/java/android/net/MacAddressTest.java index 558dbb69aeb6..473dc538f09d 100644 --- a/tests/net/java/android/net/MacAddressTest.java +++ b/tests/net/java/android/net/MacAddressTest.java @@ -73,18 +73,18 @@ public class MacAddressTest { } @Test - public void testToSafeString() { + public void testToOuiString() { String[][] macs = { - {"07:00:d3:56:8a:c4", "07:00:d3:00:00:00"}, - {"33:33:aa:bb:cc:dd", "33:33:aa:00:00:00"}, - {"06:00:00:00:00:00", "06:00:00:00:00:00"}, - {"07:00:d3:56:8a:c4", "07:00:d3:00:00:00"} + {"07:00:d3:56:8a:c4", "07:00:d3"}, + {"33:33:aa:bb:cc:dd", "33:33:aa"}, + {"06:00:00:00:00:00", "06:00:00"}, + {"07:00:d3:56:8a:c4", "07:00:d3"} }; for (String[] pair : macs) { String mac = pair[0]; String expected = pair[1]; - assertEquals(expected, MacAddress.fromString(mac).toSafeString()); + assertEquals(expected, MacAddress.fromString(mac).toOuiString()); } } diff --git a/tests/net/java/com/android/server/IpSecServiceTest.java b/tests/net/java/com/android/server/IpSecServiceTest.java index 8683c12e192e..5d1e10eab572 100644 --- a/tests/net/java/com/android/server/IpSecServiceTest.java +++ b/tests/net/java/com/android/server/IpSecServiceTest.java @@ -27,6 +27,7 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.argThat; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -40,10 +41,14 @@ import android.net.IpSecTransform; import android.net.IpSecUdpEncapResponse; import android.os.Binder; import android.os.ParcelFileDescriptor; +import android.os.Process; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.system.ErrnoException; import android.system.Os; +import android.system.StructStat; + +import dalvik.system.SocketTagger; import java.io.FileDescriptor; import java.net.InetAddress; @@ -56,6 +61,7 @@ import java.util.List; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentMatcher; /** Unit tests for {@link IpSecService}. */ @SmallTest @@ -411,4 +417,84 @@ public class IpSecServiceTest { mIpSecService.releaseSecurityParameterIndex(spiResp.resourceId); } } + + @Test + public void testUidFdtagger() throws Exception { + SocketTagger actualSocketTagger = SocketTagger.get(); + + try { + FileDescriptor sockFd = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + + // Has to be done after socket creation because BlockGuardOS calls tag on new sockets + SocketTagger mockSocketTagger = mock(SocketTagger.class); + SocketTagger.set(mockSocketTagger); + + mIpSecService.mUidFdTagger.tag(sockFd, Process.LAST_APPLICATION_UID); + verify(mockSocketTagger).tag(eq(sockFd)); + } finally { + SocketTagger.set(actualSocketTagger); + } + } + + /** + * Checks if two file descriptors point to the same file. + * + * <p>According to stat.h documentation, the correct way to check for equivalent or duplicated + * file descriptors is to check their inode and device. These two entries uniquely identify any + * file. + */ + private boolean fileDescriptorsEqual(FileDescriptor fd1, FileDescriptor fd2) { + try { + StructStat fd1Stat = Os.fstat(fd1); + StructStat fd2Stat = Os.fstat(fd2); + + return fd1Stat.st_ino == fd2Stat.st_ino && fd1Stat.st_dev == fd2Stat.st_dev; + } catch (ErrnoException e) { + return false; + } + } + + @Test + public void testOpenUdpEncapSocketTagsSocket() throws Exception { + IpSecService.UidFdTagger mockTagger = mock(IpSecService.UidFdTagger.class); + IpSecService testIpSecService = + new IpSecService(mMockContext, mMockIpSecSrvConfig, mockTagger); + + IpSecUdpEncapResponse udpEncapResp = + testIpSecService.openUdpEncapsulationSocket(0, new Binder()); + assertNotNull(udpEncapResp); + assertEquals(IpSecManager.Status.OK, udpEncapResp.status); + + FileDescriptor sockFd = udpEncapResp.fileDescriptor.getFileDescriptor(); + ArgumentMatcher<FileDescriptor> fdMatcher = + (argFd) -> { + return fileDescriptorsEqual(sockFd, argFd); + }; + verify(mockTagger).tag(argThat(fdMatcher), eq(Os.getuid())); + + testIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId); + udpEncapResp.fileDescriptor.close(); + } + + @Test + public void testOpenUdpEncapsulationSocketCallsSetEncapSocketOwner() throws Exception { + IpSecUdpEncapResponse udpEncapResp = + mIpSecService.openUdpEncapsulationSocket(0, new Binder()); + + FileDescriptor sockFd = udpEncapResp.fileDescriptor.getFileDescriptor(); + ArgumentMatcher<FileDescriptor> fdMatcher = (arg) -> { + try { + StructStat sockStat = Os.fstat(sockFd); + StructStat argStat = Os.fstat(arg); + + return sockStat.st_ino == argStat.st_ino + && sockStat.st_dev == argStat.st_dev; + } catch (ErrnoException e) { + return false; + } + }; + + verify(mMockNetd).ipSecSetEncapSocketOwner(argThat(fdMatcher), eq(Os.getuid())); + mIpSecService.closeUdpEncapsulationSocket(udpEncapResp.resourceId); + } } diff --git a/tests/utils/testutils/Android.mk b/tests/utils/testutils/Android.mk index 43d1e37cafa3..700b7cb90c96 100644 --- a/tests/utils/testutils/Android.mk +++ b/tests/utils/testutils/Android.mk @@ -25,9 +25,10 @@ LOCAL_SRC_FILES := $(call all-java-files-under,java) LOCAL_STATIC_JAVA_LIBRARIES := \ android-support-test \ - legacy-android-test \ - mockito-target-minus-junit4 + legacy-android-test -LOCAL_JAVA_LIBRARIES := android.test.runner +LOCAL_JAVA_LIBRARIES := \ + android.test.runner \ + mockito-target-minus-junit4 include $(BUILD_STATIC_JAVA_LIBRARY) |