Merge "Get the correct operator name"
diff --git a/Android.mk b/Android.mk
index ada4ac5..cdb3834 100644
--- a/Android.mk
+++ b/Android.mk
@@ -116,9 +116,10 @@
core/java/android/bluetooth/IBluetoothPan.aidl \
core/java/android/bluetooth/IBluetoothManager.aidl \
core/java/android/bluetooth/IBluetoothManagerCallback.aidl \
+ core/java/android/bluetooth/IBluetoothMap.aidl \
+ core/java/android/bluetooth/IBluetoothMapClient.aidl \
core/java/android/bluetooth/IBluetoothPbap.aidl \
core/java/android/bluetooth/IBluetoothPbapClient.aidl \
- core/java/android/bluetooth/IBluetoothMap.aidl \
core/java/android/bluetooth/IBluetoothSap.aidl \
core/java/android/bluetooth/IBluetoothStateChangeCallback.aidl \
core/java/android/bluetooth/IBluetoothHeadsetClient.aidl \
@@ -451,9 +452,9 @@
telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl \
telephony/java/com/android/internal/telephony/IWapPushManager.aidl \
wifi/java/android/net/wifi/IWifiManager.aidl \
- wifi/java/android/net/wifi/nan/IWifiNanEventCallback.aidl \
- wifi/java/android/net/wifi/nan/IWifiNanManager.aidl \
- wifi/java/android/net/wifi/nan/IWifiNanDiscoverySessionCallback.aidl \
+ wifi/java/android/net/wifi/aware/IWifiAwareEventCallback.aidl \
+ wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl \
+ wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl \
wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl \
wifi/java/android/net/wifi/IWifiScanner.aidl \
wifi/java/android/net/wifi/IRttManager.aidl \
@@ -493,7 +494,7 @@
LOCAL_ADDITIONAL_DEPENDENCIES := $(framework_res_R_stamp)
LOCAL_NO_STANDARD_LIBRARIES := true
-LOCAL_JAVA_LIBRARIES := core-oj core-libart conscrypt okhttp core-junit bouncycastle ext
+LOCAL_JAVA_LIBRARIES := core-oj core-libart conscrypt okhttp legacy-test bouncycastle ext
LOCAL_STATIC_JAVA_LIBRARIES := framework-protos
LOCAL_MODULE := framework
@@ -536,9 +537,9 @@
frameworks/base/media/java/android/media/tv/TvTrackInfo.aidl \
frameworks/base/media/java/android/media/browse/MediaBrowser.aidl \
frameworks/base/wifi/java/android/net/wifi/ScanSettings.aidl \
- frameworks/base/wifi/java/android/net/wifi/nan/ConfigRequest.aidl \
- frameworks/base/wifi/java/android/net/wifi/nan/PublishConfig.aidl \
- frameworks/base/wifi/java/android/net/wifi/nan/SubscribeConfig.aidl \
+ frameworks/base/wifi/java/android/net/wifi/aware/ConfigRequest.aidl \
+ frameworks/base/wifi/java/android/net/wifi/aware/PublishConfig.aidl \
+ frameworks/base/wifi/java/android/net/wifi/aware/SubscribeConfig.aidl \
frameworks/base/wifi/java/android/net/wifi/p2p/WifiP2pInfo.aidl \
frameworks/base/wifi/java/android/net/wifi/p2p/WifiP2pDeviceList.aidl \
frameworks/base/wifi/java/android/net/wifi/p2p/WifiP2pConfig.aidl \
diff --git a/api/current.txt b/api/current.txt
index 9cfbbc7..d22006d3 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -36363,6 +36363,7 @@
field public static final int CAPABILITY_CONNECTION_MANAGER = 1; // 0x1
field public static final int CAPABILITY_PLACE_EMERGENCY_CALLS = 16; // 0x10
field public static final int CAPABILITY_SIM_SUBSCRIPTION = 4; // 0x4
+ field public static final int CAPABILITY_SUPPORTS_VIDEO_CALLING = 1024; // 0x400
field public static final int CAPABILITY_VIDEO_CALLING = 8; // 0x8
field public static final int CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE = 256; // 0x100
field public static final android.os.Parcelable.Creator<android.telecom.PhoneAccount> CREATOR;
@@ -36703,6 +36704,7 @@
field public static final java.lang.String KEY_MMS_UA_PROF_TAG_NAME_STRING = "uaProfTagName";
field public static final java.lang.String KEY_MMS_UA_PROF_URL_STRING = "uaProfUrl";
field public static final java.lang.String KEY_MMS_USER_AGENT_STRING = "userAgent";
+ field public static final java.lang.String KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY = "only_single_dc_allowed_int_array";
field public static final java.lang.String KEY_OPERATOR_SELECTION_EXPAND_BOOL = "operator_selection_expand_bool";
field public static final java.lang.String KEY_PREFER_2G_BOOL = "prefer_2g_bool";
field public static final java.lang.String KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL = "require_entitlement_checks_bool";
@@ -36855,6 +36857,7 @@
method public int getAsuLevel();
method public int getDbm();
method public int getLevel();
+ method public int getTimingAdvance();
method public int hashCode();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.telephony.CellSignalStrengthGsm> CREATOR;
@@ -36864,8 +36867,12 @@
method public int describeContents();
method public boolean equals(java.lang.Object);
method public int getAsuLevel();
+ method public int getCqi();
method public int getDbm();
method public int getLevel();
+ method public int getRsrp();
+ method public int getRsrq();
+ method public int getRssnr();
method public int getTimingAdvance();
method public int hashCode();
method public void writeToParcel(android.os.Parcel, int);
@@ -37211,11 +37218,13 @@
public class TelephonyManager {
method public boolean canChangeDtmfToneLength();
+ method public android.telephony.TelephonyManager createForPhoneAccountHandle(android.telecom.PhoneAccountHandle);
method public android.telephony.TelephonyManager createForSubscriptionId(int);
method public java.util.List<android.telephony.CellInfo> getAllCellInfo();
method public int getCallState();
method public android.telephony.CellLocation getCellLocation();
method public int getDataActivity();
+ method public boolean getDataEnabled();
method public int getDataNetworkType();
method public int getDataState();
method public java.lang.String getDeviceId();
@@ -37233,6 +37242,7 @@
method public int getNetworkType();
method public int getPhoneCount();
method public int getPhoneType();
+ method public android.telephony.ServiceState getServiceState();
method public java.lang.String getSimCountryIso();
method public java.lang.String getSimOperator();
method public java.lang.String getSimOperatorName();
@@ -37259,6 +37269,7 @@
method public boolean isWorldPhone();
method public void listen(android.telephony.PhoneStateListener, int);
method public java.lang.String sendEnvelopeWithStatus(java.lang.String);
+ method public void setDataEnabled(boolean);
method public boolean setLine1NumberForDisplay(java.lang.String, java.lang.String);
method public boolean setOperatorBrandOverride(java.lang.String);
method public boolean setPreferredNetworkTypeToGlobal();
@@ -37266,6 +37277,7 @@
field public static final java.lang.String ACTION_CONFIGURE_VOICEMAIL = "android.telephony.action.CONFIGURE_VOICEMAIL";
field public static final java.lang.String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE";
field public static final java.lang.String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE";
+ field public static final java.lang.String ACTION_SHOW_VOICEMAIL_NOTIFICATION = "android.telephony.action.SHOW_VOICEMAIL_NOTIFICATION";
field public static final int APPTYPE_CSIM = 4; // 0x4
field public static final int APPTYPE_ISIM = 5; // 0x5
field public static final int APPTYPE_RUIM = 3; // 0x3
@@ -37285,11 +37297,15 @@
field public static final int DATA_CONNECTING = 1; // 0x1
field public static final int DATA_DISCONNECTED = 0; // 0x0
field public static final int DATA_SUSPENDED = 3; // 0x3
+ field public static final java.lang.String EXTRA_CALL_VOICEMAIL_INTENT = "android.telephony.extra.CALL_VOICEMAIL_INTENT";
field public static final java.lang.String EXTRA_INCOMING_NUMBER = "incoming_number";
+ field public static final java.lang.String EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT = "android.telephony.extra.LAUNCH_VOICEMAIL_SETTINGS_INTENT";
+ field public static final java.lang.String EXTRA_NOTIFICATION_COUNT = "android.telephony.extra.NOTIFICATION_COUNT";
field public static final java.lang.String EXTRA_STATE = "state";
field public static final java.lang.String EXTRA_STATE_IDLE;
field public static final java.lang.String EXTRA_STATE_OFFHOOK;
field public static final java.lang.String EXTRA_STATE_RINGING;
+ field public static final java.lang.String EXTRA_VOICEMAIL_NUMBER = "android.telephony.extra.VOICEMAIL_NUMBER";
field public static final int NETWORK_TYPE_1xRTT = 7; // 0x7
field public static final int NETWORK_TYPE_CDMA = 4; // 0x4
field public static final int NETWORK_TYPE_EDGE = 2; // 0x2
@@ -62843,7 +62859,10 @@
method public java.lang.String getComment();
method public long getCompressedSize();
method public long getCrc();
+ method public java.nio.file.attribute.FileTime getCreationTime();
method public byte[] getExtra();
+ method public java.nio.file.attribute.FileTime getLastAccessTime();
+ method public java.nio.file.attribute.FileTime getLastModifiedTime();
method public int getMethod();
method public java.lang.String getName();
method public long getSize();
@@ -62852,7 +62871,10 @@
method public void setComment(java.lang.String);
method public void setCompressedSize(long);
method public void setCrc(long);
+ method public java.util.zip.ZipEntry setCreationTime(java.nio.file.attribute.FileTime);
method public void setExtra(byte[]);
+ method public java.util.zip.ZipEntry setLastAccessTime(java.nio.file.attribute.FileTime);
+ method public java.util.zip.ZipEntry setLastModifiedTime(java.nio.file.attribute.FileTime);
method public void setMethod(int);
method public void setSize(long);
method public void setTime(long);
@@ -62923,6 +62945,7 @@
method public java.io.InputStream getInputStream(java.util.zip.ZipEntry) throws java.io.IOException;
method public java.lang.String getName();
method public int size();
+ method public java.util.stream.Stream<? extends java.util.zip.ZipEntry> stream();
field public static final int CENATT = 36; // 0x24
field public static final int CENATX = 38; // 0x26
field public static final int CENCOM = 32; // 0x20
diff --git a/api/system-current.txt b/api/system-current.txt
index fab5e00..01d4f47 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -35032,6 +35032,7 @@
field public static final java.lang.String ACTION_VR_LISTENER_SETTINGS = "android.settings.VR_LISTENER_SETTINGS";
field public static final java.lang.String ACTION_WEBVIEW_SETTINGS = "android.settings.WEBVIEW_SETTINGS";
field public static final java.lang.String ACTION_WIFI_IP_SETTINGS = "android.settings.WIFI_IP_SETTINGS";
+ field public static final java.lang.String ACTION_WIFI_SAVED_NETWORK_SETTINGS = "android.settings.WIFI_SAVED_NETWORK_SETTINGS";
field public static final java.lang.String ACTION_WIFI_SETTINGS = "android.settings.WIFI_SETTINGS";
field public static final java.lang.String ACTION_WIRELESS_SETTINGS = "android.settings.WIRELESS_SETTINGS";
field public static final java.lang.String AUTHORITY = "settings";
@@ -39267,6 +39268,7 @@
field public static final int CAPABILITY_MULTI_USER = 32; // 0x20
field public static final int CAPABILITY_PLACE_EMERGENCY_CALLS = 16; // 0x10
field public static final int CAPABILITY_SIM_SUBSCRIPTION = 4; // 0x4
+ field public static final int CAPABILITY_SUPPORTS_VIDEO_CALLING = 1024; // 0x400
field public static final int CAPABILITY_VIDEO_CALLING = 8; // 0x8
field public static final int CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE = 256; // 0x100
field public static final android.os.Parcelable.Creator<android.telecom.PhoneAccount> CREATOR;
@@ -39638,6 +39640,7 @@
field public static final java.lang.String KEY_MMS_UA_PROF_TAG_NAME_STRING = "uaProfTagName";
field public static final java.lang.String KEY_MMS_UA_PROF_URL_STRING = "uaProfUrl";
field public static final java.lang.String KEY_MMS_USER_AGENT_STRING = "userAgent";
+ field public static final java.lang.String KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY = "only_single_dc_allowed_int_array";
field public static final java.lang.String KEY_OPERATOR_SELECTION_EXPAND_BOOL = "operator_selection_expand_bool";
field public static final java.lang.String KEY_PREFER_2G_BOOL = "prefer_2g_bool";
field public static final java.lang.String KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL = "require_entitlement_checks_bool";
@@ -39790,6 +39793,7 @@
method public int getAsuLevel();
method public int getDbm();
method public int getLevel();
+ method public int getTimingAdvance();
method public int hashCode();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.telephony.CellSignalStrengthGsm> CREATOR;
@@ -39799,8 +39803,12 @@
method public int describeContents();
method public boolean equals(java.lang.Object);
method public int getAsuLevel();
+ method public int getCqi();
method public int getDbm();
method public int getLevel();
+ method public int getRsrp();
+ method public int getRsrq();
+ method public int getRssnr();
method public int getTimingAdvance();
method public int hashCode();
method public void writeToParcel(android.os.Parcel, int);
@@ -40150,6 +40158,7 @@
method public boolean canChangeDtmfToneLength();
method public int checkCarrierPrivilegesForPackage(java.lang.String);
method public int checkCarrierPrivilegesForPackageAnyPhone(java.lang.String);
+ method public android.telephony.TelephonyManager createForPhoneAccountHandle(android.telecom.PhoneAccountHandle);
method public android.telephony.TelephonyManager createForSubscriptionId(int);
method public void dial(java.lang.String);
method public boolean disableDataConnectivity();
@@ -40187,6 +40196,7 @@
method public int getNetworkType();
method public int getPhoneCount();
method public int getPhoneType();
+ method public android.telephony.ServiceState getServiceState();
method public java.lang.String getSimCountryIso();
method public java.lang.String getSimOperator();
method public java.lang.String getSimOperatorName();
@@ -66200,7 +66210,10 @@
method public java.lang.String getComment();
method public long getCompressedSize();
method public long getCrc();
+ method public java.nio.file.attribute.FileTime getCreationTime();
method public byte[] getExtra();
+ method public java.nio.file.attribute.FileTime getLastAccessTime();
+ method public java.nio.file.attribute.FileTime getLastModifiedTime();
method public int getMethod();
method public java.lang.String getName();
method public long getSize();
@@ -66209,7 +66222,10 @@
method public void setComment(java.lang.String);
method public void setCompressedSize(long);
method public void setCrc(long);
+ method public java.util.zip.ZipEntry setCreationTime(java.nio.file.attribute.FileTime);
method public void setExtra(byte[]);
+ method public java.util.zip.ZipEntry setLastAccessTime(java.nio.file.attribute.FileTime);
+ method public java.util.zip.ZipEntry setLastModifiedTime(java.nio.file.attribute.FileTime);
method public void setMethod(int);
method public void setSize(long);
method public void setTime(long);
@@ -66280,6 +66296,7 @@
method public java.io.InputStream getInputStream(java.util.zip.ZipEntry) throws java.io.IOException;
method public java.lang.String getName();
method public int size();
+ method public java.util.stream.Stream<? extends java.util.zip.ZipEntry> stream();
field public static final int CENATT = 36; // 0x24
field public static final int CENATX = 38; // 0x26
field public static final int CENCOM = 32; // 0x20
diff --git a/api/test-current.txt b/api/test-current.txt
index 7a58e1e..e21afe2 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -36440,6 +36440,7 @@
field public static final int CAPABILITY_CONNECTION_MANAGER = 1; // 0x1
field public static final int CAPABILITY_PLACE_EMERGENCY_CALLS = 16; // 0x10
field public static final int CAPABILITY_SIM_SUBSCRIPTION = 4; // 0x4
+ field public static final int CAPABILITY_SUPPORTS_VIDEO_CALLING = 1024; // 0x400
field public static final int CAPABILITY_VIDEO_CALLING = 8; // 0x8
field public static final int CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE = 256; // 0x100
field public static final android.os.Parcelable.Creator<android.telecom.PhoneAccount> CREATOR;
@@ -36780,6 +36781,7 @@
field public static final java.lang.String KEY_MMS_UA_PROF_TAG_NAME_STRING = "uaProfTagName";
field public static final java.lang.String KEY_MMS_UA_PROF_URL_STRING = "uaProfUrl";
field public static final java.lang.String KEY_MMS_USER_AGENT_STRING = "userAgent";
+ field public static final java.lang.String KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY = "only_single_dc_allowed_int_array";
field public static final java.lang.String KEY_OPERATOR_SELECTION_EXPAND_BOOL = "operator_selection_expand_bool";
field public static final java.lang.String KEY_PREFER_2G_BOOL = "prefer_2g_bool";
field public static final java.lang.String KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL = "require_entitlement_checks_bool";
@@ -36932,6 +36934,7 @@
method public int getAsuLevel();
method public int getDbm();
method public int getLevel();
+ method public int getTimingAdvance();
method public int hashCode();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.telephony.CellSignalStrengthGsm> CREATOR;
@@ -36941,8 +36944,12 @@
method public int describeContents();
method public boolean equals(java.lang.Object);
method public int getAsuLevel();
+ method public int getCqi();
method public int getDbm();
method public int getLevel();
+ method public int getRsrp();
+ method public int getRsrq();
+ method public int getRssnr();
method public int getTimingAdvance();
method public int hashCode();
method public void writeToParcel(android.os.Parcel, int);
@@ -37288,11 +37295,13 @@
public class TelephonyManager {
method public boolean canChangeDtmfToneLength();
+ method public android.telephony.TelephonyManager createForPhoneAccountHandle(android.telecom.PhoneAccountHandle);
method public android.telephony.TelephonyManager createForSubscriptionId(int);
method public java.util.List<android.telephony.CellInfo> getAllCellInfo();
method public int getCallState();
method public android.telephony.CellLocation getCellLocation();
method public int getDataActivity();
+ method public boolean getDataEnabled();
method public int getDataNetworkType();
method public int getDataState();
method public java.lang.String getDeviceId();
@@ -37310,6 +37319,7 @@
method public int getNetworkType();
method public int getPhoneCount();
method public int getPhoneType();
+ method public android.telephony.ServiceState getServiceState();
method public java.lang.String getSimCountryIso();
method public java.lang.String getSimOperator();
method public java.lang.String getSimOperatorName();
@@ -37336,6 +37346,7 @@
method public boolean isWorldPhone();
method public void listen(android.telephony.PhoneStateListener, int);
method public java.lang.String sendEnvelopeWithStatus(java.lang.String);
+ method public void setDataEnabled(boolean);
method public boolean setLine1NumberForDisplay(java.lang.String, java.lang.String);
method public boolean setOperatorBrandOverride(java.lang.String);
method public boolean setPreferredNetworkTypeToGlobal();
@@ -37343,6 +37354,7 @@
field public static final java.lang.String ACTION_CONFIGURE_VOICEMAIL = "android.telephony.action.CONFIGURE_VOICEMAIL";
field public static final java.lang.String ACTION_PHONE_STATE_CHANGED = "android.intent.action.PHONE_STATE";
field public static final java.lang.String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE";
+ field public static final java.lang.String ACTION_SHOW_VOICEMAIL_NOTIFICATION = "android.telephony.action.SHOW_VOICEMAIL_NOTIFICATION";
field public static final int APPTYPE_CSIM = 4; // 0x4
field public static final int APPTYPE_ISIM = 5; // 0x5
field public static final int APPTYPE_RUIM = 3; // 0x3
@@ -37362,11 +37374,15 @@
field public static final int DATA_CONNECTING = 1; // 0x1
field public static final int DATA_DISCONNECTED = 0; // 0x0
field public static final int DATA_SUSPENDED = 3; // 0x3
+ field public static final java.lang.String EXTRA_CALL_VOICEMAIL_INTENT = "android.telephony.extra.CALL_VOICEMAIL_INTENT";
field public static final java.lang.String EXTRA_INCOMING_NUMBER = "incoming_number";
+ field public static final java.lang.String EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT = "android.telephony.extra.LAUNCH_VOICEMAIL_SETTINGS_INTENT";
+ field public static final java.lang.String EXTRA_NOTIFICATION_COUNT = "android.telephony.extra.NOTIFICATION_COUNT";
field public static final java.lang.String EXTRA_STATE = "state";
field public static final java.lang.String EXTRA_STATE_IDLE;
field public static final java.lang.String EXTRA_STATE_OFFHOOK;
field public static final java.lang.String EXTRA_STATE_RINGING;
+ field public static final java.lang.String EXTRA_VOICEMAIL_NUMBER = "android.telephony.extra.VOICEMAIL_NUMBER";
field public static final int NETWORK_TYPE_1xRTT = 7; // 0x7
field public static final int NETWORK_TYPE_CDMA = 4; // 0x4
field public static final int NETWORK_TYPE_EDGE = 2; // 0x2
@@ -62923,7 +62939,10 @@
method public java.lang.String getComment();
method public long getCompressedSize();
method public long getCrc();
+ method public java.nio.file.attribute.FileTime getCreationTime();
method public byte[] getExtra();
+ method public java.nio.file.attribute.FileTime getLastAccessTime();
+ method public java.nio.file.attribute.FileTime getLastModifiedTime();
method public int getMethod();
method public java.lang.String getName();
method public long getSize();
@@ -62932,7 +62951,10 @@
method public void setComment(java.lang.String);
method public void setCompressedSize(long);
method public void setCrc(long);
+ method public java.util.zip.ZipEntry setCreationTime(java.nio.file.attribute.FileTime);
method public void setExtra(byte[]);
+ method public java.util.zip.ZipEntry setLastAccessTime(java.nio.file.attribute.FileTime);
+ method public java.util.zip.ZipEntry setLastModifiedTime(java.nio.file.attribute.FileTime);
method public void setMethod(int);
method public void setSize(long);
method public void setTime(long);
@@ -63003,6 +63025,7 @@
method public java.io.InputStream getInputStream(java.util.zip.ZipEntry) throws java.io.IOException;
method public java.lang.String getName();
method public int size();
+ method public java.util.stream.Stream<? extends java.util.zip.ZipEntry> stream();
field public static final int CENATT = 36; // 0x24
field public static final int CENATX = 38; // 0x26
field public static final int CENCOM = 32; // 0x20
diff --git a/cmds/app_process/Android.mk b/cmds/app_process/Android.mk
index b5f1c2a..eaad3a7 100644
--- a/cmds/app_process/Android.mk
+++ b/cmds/app_process/Android.mk
@@ -1,79 +1,67 @@
LOCAL_PATH:= $(call my-dir)
+app_process_common_shared_libs := \
+ libandroid_runtime \
+ libbinder \
+ libcutils \
+ libdl \
+ liblog \
+ libnativeloader \
+ libutils \
+
# This is a list of libraries that need to be included in order to avoid
# bad apps. This prevents a library from having a mismatch when resolving
# new/delete from an app shared library.
# See b/21032018 for more details.
-app_process_common_shared_libs := \
+app_process_common_shared_libs += \
libwilhelm \
+app_process_common_static_libs := \
+ libsigchain \
+
+app_process_src_files := \
+ app_main.cpp \
+
+app_process_cflags := \
+ -Wall -Werror -Wunused -Wunreachable-code
+
+app_process_ldflags_32 := \
+ -Wl,--version-script,art/sigchainlib/version-script32.txt -Wl,--export-dynamic
+app_process_ldflags_64 := \
+ -Wl,--version-script,art/sigchainlib/version-script64.txt -Wl,--export-dynamic
+
include $(CLEAR_VARS)
-LOCAL_SRC_FILES:= \
- app_main.cpp
+LOCAL_SRC_FILES:= $(app_process_src_files)
-LOCAL_LDFLAGS_32 := -Wl,--version-script,art/sigchainlib/version-script32.txt -Wl,--export-dynamic
-LOCAL_LDFLAGS_64 := -Wl,--version-script,art/sigchainlib/version-script64.txt -Wl,--export-dynamic
+LOCAL_LDFLAGS_32 := $(app_process_ldflags_32)
+LOCAL_LDFLAGS_64 := $(app_process_ldflags_64)
-LOCAL_SHARED_LIBRARIES := \
- libdl \
- libcutils \
- libutils \
- liblog \
- libbinder \
- libnativeloader \
- libandroid_runtime \
- $(app_process_common_shared_libs) \
+LOCAL_SHARED_LIBRARIES := $(app_process_common_shared_libs)
-LOCAL_WHOLE_STATIC_LIBRARIES := libsigchain
+LOCAL_WHOLE_STATIC_LIBRARIES := $(app_process_common_static_libs)
LOCAL_MODULE:= app_process
LOCAL_MULTILIB := both
LOCAL_MODULE_STEM_32 := app_process32
LOCAL_MODULE_STEM_64 := app_process64
-LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
+LOCAL_CFLAGS += $(app_process_cflags)
+
+# In SANITIZE_LITE mode, we create the sanitized binary in a separate location (but reuse
+# the same module). Using the same module also works around an issue with make: binaries
+# that depend on sanitized libraries will be relinked, even if they set LOCAL_SANITIZE := never.
+#
+# Also pull in the asanwrapper helper.
+ifeq ($(SANITIZE_LITE),true)
+LOCAL_MODULE_PATH := $(TARGET_OUT_EXECUTABLES)/asan
+LOCAL_REQUIRED_MODULES := asanwrapper
+endif
include $(BUILD_EXECUTABLE)
# Create a symlink from app_process to app_process32 or 64
# depending on the target configuration.
+ifneq ($(SANITIZE_LITE),true)
include $(BUILD_SYSTEM)/executable_prefer_symlink.mk
-
-# Build a variant of app_process binary linked with ASan runtime.
-# ARM-only at the moment.
-ifeq ($(TARGET_ARCH),arm)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
- app_main.cpp
-
-LOCAL_SHARED_LIBRARIES := \
- libcutils \
- libutils \
- liblog \
- libbinder \
- libandroid_runtime \
- $(app_process_common_shared_libs) \
-
-LOCAL_WHOLE_STATIC_LIBRARIES := libsigchain
-
-LOCAL_LDFLAGS := -ldl
-LOCAL_LDFLAGS_32 := -Wl,--version-script,art/sigchainlib/version-script32.txt -Wl,--export-dynamic
-LOCAL_LDFLAGS_64 := -Wl,--version-script,art/sigchainlib/version-script64.txt -Wl,--export-dynamic
-
-LOCAL_MODULE := app_process__asan
-LOCAL_MULTILIB := both
-LOCAL_MODULE_STEM_32 := app_process32
-LOCAL_MODULE_STEM_64 := app_process64
-
-LOCAL_SANITIZE := address
-LOCAL_CLANG := true
-LOCAL_MODULE_PATH := $(TARGET_OUT_EXECUTABLES)/asan
-
-LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
-
-include $(BUILD_EXECUTABLE)
-
-endif # ifeq($(TARGET_ARCH),arm)
+endif
diff --git a/cmds/svc/src/com/android/commands/svc/UsbCommand.java b/cmds/svc/src/com/android/commands/svc/UsbCommand.java
index a6ef25f..4dcb05e 100644
--- a/cmds/svc/src/com/android/commands/svc/UsbCommand.java
+++ b/cmds/svc/src/com/android/commands/svc/UsbCommand.java
@@ -50,7 +50,7 @@
IUsbManager usbMgr = IUsbManager.Stub.asInterface(ServiceManager.getService(
Context.USB_SERVICE));
try {
- usbMgr.setCurrentFunction((args.length >=3 ? args[2] : null));
+ usbMgr.setCurrentFunction((args.length >=3 ? args[2] : null), false);
} catch (RemoteException e) {
System.err.println("Error communicating with UsbManager: " + e);
}
diff --git a/cmds/svc/src/com/android/commands/svc/WifiCommand.java b/cmds/svc/src/com/android/commands/svc/WifiCommand.java
index 94214ff..633dd97 100644
--- a/cmds/svc/src/com/android/commands/svc/WifiCommand.java
+++ b/cmds/svc/src/com/android/commands/svc/WifiCommand.java
@@ -52,7 +52,7 @@
IWifiManager wifiMgr
= IWifiManager.Stub.asInterface(ServiceManager.getService(Context.WIFI_SERVICE));
try {
- wifiMgr.setWifiEnabled(flag);
+ wifiMgr.setWifiEnabled("com.android.shell", flag);
}
catch (RemoteException e) {
System.err.println("Wi-Fi operation failed: " + e);
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index c1b37e6..d242ba1 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -5149,7 +5149,14 @@
}
updateDefaultDensity();
- final boolean is24Hr = "24".equals(mCoreSettings.getString(Settings.System.TIME_12_24));
+ final String use24HourSetting = mCoreSettings.getString(Settings.System.TIME_12_24);
+ Boolean is24Hr = null;
+ if (use24HourSetting != null) {
+ is24Hr = "24".equals(use24HourSetting) ? Boolean.TRUE : Boolean.FALSE;
+ }
+ // null : use locale default for 12/24 hour formatting,
+ // false : use 12 hour format,
+ // true : use 24 hour format.
DateFormat.set24HourTimePref(is24Hr);
View.mDebugViewAttributes =
diff --git a/core/java/android/app/ApplicationLoaders.java b/core/java/android/app/ApplicationLoaders.java
index 6a73829..ef2db4a 100644
--- a/core/java/android/app/ApplicationLoaders.java
+++ b/core/java/android/app/ApplicationLoaders.java
@@ -16,17 +16,19 @@
package android.app;
+import android.os.Build;
import android.os.Trace;
import android.util.ArrayMap;
import com.android.internal.os.PathClassLoaderFactory;
import dalvik.system.PathClassLoader;
-class ApplicationLoaders {
+/** @hide */
+public class ApplicationLoaders {
public static ApplicationLoaders getDefault() {
return gApplicationLoaders;
}
- public ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
+ ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
String librarySearchPath, String libraryPermittedPath,
ClassLoader parent) {
/*
@@ -80,6 +82,19 @@
}
}
+ /**
+ * Creates a classloader for the WebView APK and places it in the cache of loaders maintained
+ * by this class. This is used in the WebView zygote, where its presence in the cache speeds up
+ * startup and enables memory sharing.
+ */
+ public ClassLoader createAndCacheWebViewClassLoader(String packagePath, String libsPath) {
+ // The correct paths are calculated by WebViewZygote in the system server and passed to
+ // us here. We hardcode the other parameters: WebView always targets the current SDK,
+ // does not need to use non-public system libraries, and uses the base classloader as its
+ // parent to permit usage of the cache.
+ return getClassLoader(packagePath, Build.VERSION.SDK_INT, false, libsPath, null, null);
+ }
+
private static native void setupVulkanLayerPath(ClassLoader classLoader, String librarySearchPath);
/**
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index b889c8f..8824c981 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -339,39 +339,43 @@
* concatenation of both apps' shared library lists.
*/
- String instrumentationPackageName = activityThread.mInstrumentationPackageName;
- String instrumentationAppDir = activityThread.mInstrumentationAppDir;
- String[] instrumentationSplitAppDirs = activityThread.mInstrumentationSplitAppDirs;
- String instrumentationLibDir = activityThread.mInstrumentationLibDir;
-
- String instrumentedAppDir = activityThread.mInstrumentedAppDir;
- String[] instrumentedSplitAppDirs = activityThread.mInstrumentedSplitAppDirs;
- String instrumentedLibDir = activityThread.mInstrumentedLibDir;
String[] instrumentationLibs = null;
+ // activityThread will be null when called from the WebView zygote; just assume
+ // no instrumentation applies in this case.
+ if (activityThread != null) {
+ String instrumentationPackageName = activityThread.mInstrumentationPackageName;
+ String instrumentationAppDir = activityThread.mInstrumentationAppDir;
+ String[] instrumentationSplitAppDirs = activityThread.mInstrumentationSplitAppDirs;
+ String instrumentationLibDir = activityThread.mInstrumentationLibDir;
- if (appDir.equals(instrumentationAppDir)
- || appDir.equals(instrumentedAppDir)) {
- outZipPaths.clear();
- outZipPaths.add(instrumentationAppDir);
- if (instrumentationSplitAppDirs != null) {
- Collections.addAll(outZipPaths, instrumentationSplitAppDirs);
- }
- if (!instrumentationAppDir.equals(instrumentedAppDir)) {
- outZipPaths.add(instrumentedAppDir);
- if (instrumentedSplitAppDirs != null) {
- Collections.addAll(outZipPaths, instrumentedSplitAppDirs);
+ String instrumentedAppDir = activityThread.mInstrumentedAppDir;
+ String[] instrumentedSplitAppDirs = activityThread.mInstrumentedSplitAppDirs;
+ String instrumentedLibDir = activityThread.mInstrumentedLibDir;
+
+ if (appDir.equals(instrumentationAppDir)
+ || appDir.equals(instrumentedAppDir)) {
+ outZipPaths.clear();
+ outZipPaths.add(instrumentationAppDir);
+ if (instrumentationSplitAppDirs != null) {
+ Collections.addAll(outZipPaths, instrumentationSplitAppDirs);
}
- }
-
- if (outLibPaths != null) {
- outLibPaths.add(instrumentationLibDir);
- if (!instrumentationLibDir.equals(instrumentedLibDir)) {
- outLibPaths.add(instrumentedLibDir);
+ if (!instrumentationAppDir.equals(instrumentedAppDir)) {
+ outZipPaths.add(instrumentedAppDir);
+ if (instrumentedSplitAppDirs != null) {
+ Collections.addAll(outZipPaths, instrumentedSplitAppDirs);
+ }
}
- }
- if (!instrumentedAppDir.equals(instrumentationAppDir)) {
- instrumentationLibs = getLibrariesFor(instrumentationPackageName);
+ if (outLibPaths != null) {
+ outLibPaths.add(instrumentationLibDir);
+ if (!instrumentationLibDir.equals(instrumentedLibDir)) {
+ outLibPaths.add(instrumentedLibDir);
+ }
+ }
+
+ if (!instrumentedAppDir.equals(instrumentationAppDir)) {
+ instrumentationLibs = getLibrariesFor(instrumentationPackageName);
+ }
}
}
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index c4673a3..744bd30 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -793,7 +793,8 @@
for (int i = mResourceImpls.size() - 1; i >= 0; i--) {
ResourcesKey key = mResourceImpls.keyAt(i);
- ResourcesImpl r = mResourceImpls.valueAt(i).get();
+ WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
+ ResourcesImpl r = weakImplRef != null ? weakImplRef.get() : null;
if (r != null) {
if (DEBUG || DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources "
+ r + " config to: " + config);
@@ -854,8 +855,9 @@
final int implCount = mResourceImpls.size();
for (int i = 0; i < implCount; i++) {
- final ResourcesImpl impl = mResourceImpls.valueAt(i).get();
final ResourcesKey key = mResourceImpls.keyAt(i);
+ final WeakReference<ResourcesImpl> weakImplRef = mResourceImpls.valueAt(i);
+ final ResourcesImpl impl = weakImplRef != null ? weakImplRef.get() : null;
if (impl != null && key.mResDir.equals(assetPath)) {
if (!ArrayUtils.contains(key.mLibDirs, libAsset)) {
final int newLibAssetCount = 1 +
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 42ddf10..4c5fb13 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -84,8 +84,8 @@
import android.net.wifi.RttManager;
import android.net.wifi.WifiManager;
import android.net.wifi.WifiScanner;
-import android.net.wifi.nan.IWifiNanManager;
-import android.net.wifi.nan.WifiNanManager;
+import android.net.wifi.aware.IWifiAwareManager;
+import android.net.wifi.aware.WifiAwareManager;
import android.net.wifi.p2p.IWifiP2pManager;
import android.net.wifi.p2p.WifiP2pManager;
import android.nfc.NfcManager;
@@ -514,16 +514,16 @@
return new WifiP2pManager(service);
}});
- registerService(Context.WIFI_NAN_SERVICE, WifiNanManager.class,
- new CachedServiceFetcher<WifiNanManager>() {
+ registerService(Context.WIFI_AWARE_SERVICE, WifiAwareManager.class,
+ new CachedServiceFetcher<WifiAwareManager>() {
@Override
- public WifiNanManager createService(ContextImpl ctx) {
- IBinder b = ServiceManager.getService(Context.WIFI_NAN_SERVICE);
- IWifiNanManager service = IWifiNanManager.Stub.asInterface(b);
+ public WifiAwareManager createService(ContextImpl ctx) {
+ IBinder b = ServiceManager.getService(Context.WIFI_AWARE_SERVICE);
+ IWifiAwareManager service = IWifiAwareManager.Stub.asInterface(b);
if (service == null) {
return null;
}
- return new WifiNanManager(ctx.getOuterContext(), service);
+ return new WifiAwareManager(ctx.getOuterContext(), service);
}});
registerService(Context.WIFI_SCANNING_SERVICE, WifiScanner.class,
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index b57a4e0..4271e3f 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -601,10 +601,6 @@
*/
public BluetoothLeAdvertiser getBluetoothLeAdvertiser() {
if (!getLeAccess()) return null;
- if (!isMultipleAdvertisementSupported() && !isPeripheralModeSupported()) {
- Log.e(TAG, "Bluetooth LE advertising not supported");
- return null;
- }
synchronized(mLock) {
if (sBluetoothLeAdvertiser == null) {
sBluetoothLeAdvertiser = new BluetoothLeAdvertiser(mManagerService);
@@ -1354,24 +1350,6 @@
}
/**
- * Returns whether peripheral mode is supported.
- *
- * @hide
- */
- public boolean isPeripheralModeSupported() {
- if (getState() != STATE_ON) return false;
- try {
- mServiceLock.readLock().lock();
- if (mService != null) return mService.isPeripheralModeSupported();
- } catch (RemoteException e) {
- Log.e(TAG, "failed to get peripheral mode capability: ", e);
- } finally {
- mServiceLock.readLock().unlock();
- }
- return false;
- }
-
- /**
* Return true if offloaded filters are supported
*
* @return true if chipset supports on-chip filtering
@@ -1513,6 +1491,36 @@
}
/**
+ * Gets the currently supported profiles by the adapter.
+ *
+ *<p> This can be used to check whether a profile is supported before attempting
+ * to connect to its respective proxy.
+ *
+ * @return a list of integers indicating the ids of supported profiles as defined in
+ * {@link BluetoothProfile}.
+ * @hide
+ */
+ public List<Integer> getSupportedProfiles() {
+ final ArrayList<Integer> supportedProfiles = new ArrayList<Integer>();
+
+ try {
+ synchronized (mManagerCallback) {
+ if (mService != null) {
+ final long supportedProfilesBitMask = mService.getSupportedProfiles();
+
+ for (int i = 0; i <= BluetoothProfile.MAX_PROFILE_ID; i++) {
+ if ((supportedProfilesBitMask & (1 << i)) != 0) {
+ supportedProfiles.add(i);
+ }
+ }
+ }
+ }
+ } catch (RemoteException e) {Log.e(TAG, "getSupportedProfiles:", e);}
+
+ return supportedProfiles;
+ }
+
+ /**
* Get the current connection state of the local Bluetooth adapter.
* This can be used to check whether the local Bluetooth adapter is connected
* to any profile of any other remote Bluetooth Device.
@@ -1943,6 +1951,9 @@
} else if (profile == BluetoothProfile.PBAP_CLIENT) {
BluetoothPbapClient pbapClient = new BluetoothPbapClient(context, listener);
return true;
+ } else if (profile == BluetoothProfile.MAP_CLIENT) {
+ BluetoothMapClient mapClient = new BluetoothMapClient(context, listener);
+ return true;
} else {
return false;
}
@@ -2015,6 +2026,10 @@
BluetoothPbapClient pbapClient = (BluetoothPbapClient)proxy;
pbapClient.close();
break;
+ case BluetoothProfile.MAP_CLIENT:
+ BluetoothMapClient mapClient = (BluetoothMapClient)proxy;
+ mapClient.close();
+ break;
}
}
diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java
new file mode 100644
index 0000000..4252482
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothMapClient.java
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.net.Uri;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class provides the APIs to control the Bluetooth MAP MCE Profile.
+ *
+ * @hide
+ */
+public final class BluetoothMapClient implements BluetoothProfile {
+
+ private static final String TAG = "BluetoothMapClient";
+ private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE);
+
+ public static final String ACTION_CONNECTION_STATE_CHANGED =
+ "android.bluetooth.mapmce.profile.action.CONNECTION_STATE_CHANGED";
+ public static final String ACTION_MESSAGE_RECEIVED =
+ "android.bluetooth.mapmce.profile.action.MESSAGE_RECEIVED";
+ /* Actions to be used for pending intents */
+ public static final String ACTION_MESSAGE_SENT_SUCCESSFULLY =
+ "android.bluetooth.mapmce.profile.action.MESSAGE_SENT_SUCCESSFULLY";
+ public static final String ACTION_MESSAGE_DELIVERED_SUCCESSFULLY =
+ "android.bluetooth.mapmce.profile.action.MESSAGE_DELIVERED_SUCCESSFULLY";
+
+ private IBluetoothMapClient mService;
+ private final Context mContext;
+ private ServiceListener mServiceListener;
+ private BluetoothAdapter mAdapter;
+
+ /** There was an error trying to obtain the state */
+ public static final int STATE_ERROR = -1;
+
+ public static final int RESULT_FAILURE = 0;
+ public static final int RESULT_SUCCESS = 1;
+ /** Connection canceled before completion. */
+ public static final int RESULT_CANCELED = 2;
+
+ final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
+ new IBluetoothStateChangeCallback.Stub() {
+ public void onBluetoothStateChange(boolean up) {
+ if (DBG) Log.d(TAG, "onBluetoothStateChange: up=" + up);
+ if (!up) {
+ if (VDBG) Log.d(TAG, "Unbinding service...");
+ synchronized (mConnection) {
+ try {
+ mService = null;
+ mContext.unbindService(mConnection);
+ } catch (Exception re) {
+ Log.e(TAG, "", re);
+ }
+ }
+ } else {
+ synchronized (mConnection) {
+ try {
+ if (mService == null) {
+ if (VDBG) Log.d(TAG, "Binding service...");
+ doBind();
+ }
+ } catch (Exception re) {
+ Log.e(TAG, "", re);
+ }
+ }
+ }
+ }
+ };
+
+ /**
+ * Create a BluetoothMapClient proxy object.
+ */
+ /*package*/ BluetoothMapClient(Context context, ServiceListener l) {
+ if (DBG) Log.d(TAG, "Create BluetoothMapClient proxy object");
+ mContext = context;
+ mServiceListener = l;
+ mAdapter = BluetoothAdapter.getDefaultAdapter();
+ IBluetoothManager mgr = mAdapter.getBluetoothManager();
+ if (mgr != null) {
+ try {
+ mgr.registerStateChangeCallback(mBluetoothStateChangeCallback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ }
+ }
+ doBind();
+ }
+
+ boolean doBind() {
+ Intent intent = new Intent(IBluetoothMapClient.class.getName());
+ ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
+ intent.setComponent(comp);
+ if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0,
+ android.os.Process.myUserHandle())) {
+ Log.e(TAG, "Could not bind to Bluetooth MAP MCE Service with " + intent);
+ return false;
+ }
+ return true;
+ }
+
+ protected void finalize() throws Throwable {
+ try {
+ close();
+ } finally {
+ super.finalize();
+ }
+ }
+
+ /**
+ * Close the connection to the backing service.
+ * Other public functions of BluetoothMap will return default error
+ * results once close() has been called. Multiple invocations of close()
+ * are ok.
+ */
+ public void close() {
+ IBluetoothManager mgr = mAdapter.getBluetoothManager();
+ if (mgr != null) {
+ try {
+ mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback);
+ } catch (Exception e) {
+ Log.e(TAG, "", e);
+ }
+ }
+
+ synchronized (mConnection) {
+ if (mService != null) {
+ try {
+ mService = null;
+ mContext.unbindService(mConnection);
+ } catch (Exception re) {
+ Log.e(TAG, "", re);
+ }
+ }
+ }
+ mServiceListener = null;
+ }
+
+ /**
+ * Returns true if the specified Bluetooth device is connected.
+ * Returns false if not connected, or if this proxy object is not
+ * currently connected to the Map service.
+ */
+ public boolean isConnected(BluetoothDevice device) {
+ if (VDBG) Log.d(TAG, "isConnected(" + device + ")");
+ if (mService != null) {
+ try {
+ return mService.isConnected(device);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString());
+ }
+ } else {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ }
+ return false;
+ }
+
+ /**
+ * Initiate connection. Initiation of outgoing connections is not
+ * supported for MAP server.
+ */
+ public boolean connect(BluetoothDevice device) {
+ if (DBG) Log.d(TAG, "connect(" + device + ")" + "for MAPS MCE");
+ if (mService != null) {
+ try {
+ return mService.connect(device);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.toString());
+ }
+ } else {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable()));
+ }
+ return false;
+ }
+
+ /**
+ * Initiate disconnect.
+ *
+ * @param device Remote Bluetooth Device
+ * @return false on error, true otherwise
+ */
+ public boolean disconnect(BluetoothDevice device) {
+ if (DBG) Log.d(TAG, "disconnect(" + device + ")");
+ if (mService != null && isEnabled() &&
+ isValidDevice(device)) {
+ try {
+ return mService.disconnect(device);
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ }
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return false;
+ }
+
+ /**
+ * Get the list of connected devices. Currently at most one.
+ *
+ * @return list of connected devices
+ */
+ @Override
+ public List<BluetoothDevice> getConnectedDevices() {
+ if (DBG) Log.d(TAG, "getConnectedDevices()");
+ if (mService != null && isEnabled()) {
+ try {
+ return mService.getConnectedDevices();
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ return new ArrayList<>();
+ }
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return new ArrayList<>();
+ }
+
+ /**
+ * Get the list of devices matching specified states. Currently at most one.
+ *
+ * @return list of matching devices
+ */
+ @Override
+ public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
+ if (DBG) Log.d(TAG, "getDevicesMatchingStates()");
+ if (mService != null && isEnabled()) {
+ try {
+ return mService.getDevicesMatchingConnectionStates(states);
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ return new ArrayList<>();
+ }
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return new ArrayList<>();
+ }
+
+ /**
+ * Get connection state of device
+ *
+ * @return device connection state
+ */
+ @Override
+ public int getConnectionState(BluetoothDevice device) {
+ if (DBG) Log.d(TAG, "getConnectionState(" + device + ")");
+ if (mService != null && isEnabled() &&
+ isValidDevice(device)) {
+ try {
+ return mService.getConnectionState(device);
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return BluetoothProfile.STATE_DISCONNECTED;
+ }
+
+ /**
+ * Set priority of the profile
+ *
+ * <p> The device should already be paired. Priority can be one of {@link #PRIORITY_ON} or
+ * {@link #PRIORITY_OFF},
+ *
+ * @param device Paired bluetooth device
+ * @return true if priority is set, false on error
+ */
+ public boolean setPriority(BluetoothDevice device, int priority) {
+ if (DBG) Log.d(TAG, "setPriority(" + device + ", " + priority + ")");
+ if (mService != null && isEnabled() &&
+ isValidDevice(device)) {
+ if (priority != BluetoothProfile.PRIORITY_OFF &&
+ priority != BluetoothProfile.PRIORITY_ON) {
+ return false;
+ }
+ try {
+ return mService.setPriority(device, priority);
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ return false;
+ }
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return false;
+ }
+
+ /**
+ * Get the priority of the profile.
+ *
+ * <p> The priority can be any of:
+ * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF},
+ * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED}
+ *
+ * @param device Bluetooth device
+ * @return priority of the device
+ */
+ public int getPriority(BluetoothDevice device) {
+ if (VDBG) Log.d(TAG, "getPriority(" + device + ")");
+ if (mService != null && isEnabled() &&
+ isValidDevice(device)) {
+ try {
+ return mService.getPriority(device);
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ return PRIORITY_OFF;
+ }
+ }
+ if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ return PRIORITY_OFF;
+ }
+
+ /**
+ * Send a message.
+ *
+ * Send an SMS message to either the contacts primary number or the telephone number specified.
+ *
+ * @param device Bluetooth device
+ * @param contacts Uri[] of the contacts
+ * @param message Message to be sent
+ * @param sentIntent intent issued when message is sent
+ * @param deliveredIntent intent issued when message is delivered
+ * @return true if the message is enqueued, false on error
+ */
+ public boolean sendMessage(BluetoothDevice device, Uri[] contacts, String message,
+ PendingIntent sentIntent, PendingIntent deliveredIntent) {
+ if (DBG) Log.d(TAG, "sendMessage(" + device + ", " + contacts + ", " + message);
+ if (mService != null && isEnabled() && isValidDevice(device)) {
+ try {
+ return mService.sendMessage(device, contacts, message, sentIntent, deliveredIntent);
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ return false;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Get unread messages. Unread messages will be published via {@link #ACTION_MESSAGE_RECEIVED}.
+ *
+ * @param device Bluetooth device
+ * @return true if the message is enqueued, false on error
+ */
+ public boolean getUnreadMessages(BluetoothDevice device) {
+ if (DBG) Log.d(TAG, "getUnreadMessages(" + device + ")");
+ if (mService != null && isEnabled() && isValidDevice(device)) {
+ try {
+ return mService.getUnreadMessages(device);
+ } catch (RemoteException e) {
+ Log.e(TAG, Log.getStackTraceString(new Throwable()));
+ return false;
+ }
+ }
+ return false;
+ }
+
+ private final ServiceConnection mConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ if (DBG) Log.d(TAG, "Proxy object connected");
+ mService = IBluetoothMapClient.Stub.asInterface(service);
+ if (mServiceListener != null) {
+ mServiceListener.onServiceConnected(BluetoothProfile.MAP_CLIENT,
+ BluetoothMapClient.this);
+ }
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ if (DBG) Log.d(TAG, "Proxy object disconnected");
+ mService = null;
+ if (mServiceListener != null) {
+ mServiceListener.onServiceDisconnected(BluetoothProfile.MAP_CLIENT);
+ }
+ }
+ };
+
+ private boolean isEnabled() {
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) return true;
+ if (DBG) Log.d(TAG, "Bluetooth is Not enabled");
+ return false;
+ }
+
+ private boolean isValidDevice(BluetoothDevice device) {
+ if (device == null) return false;
+
+ if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
+ return false;
+ }
+
+
+}
diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java
index eee66d1..f363607 100644
--- a/core/java/android/bluetooth/BluetoothProfile.java
+++ b/core/java/android/bluetooth/BluetoothProfile.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010-2014 The Android Open Source Project
+ * Copyright (C) 2010-2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -137,6 +137,19 @@
public static final int PBAP_CLIENT = 17;
/**
+ * MAP Messaging Client Equipment (MCE)
+ * @hide
+ */
+ public static final int MAP_CLIENT = 18;
+
+ /**
+ * Max profile ID. This value should be updated whenever a new profile is added to match
+ * the largest value assigned to a profile.
+ * @hide
+ */
+ public static final int MAX_PROFILE_ID = 17;
+
+ /**
* Default priority for devices that we try to auto-connect to and
* and allow incoming connections for the profile
* @hide
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index 8c98536..7c5458b 100644
--- a/core/java/android/bluetooth/IBluetooth.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -63,6 +63,7 @@
boolean removeBond(in BluetoothDevice device);
int getBondState(in BluetoothDevice device);
boolean isBondingInitiatedLocally(in BluetoothDevice device);
+ long getSupportedProfiles();
int getConnectionState(in BluetoothDevice device);
String getRemoteName(in BluetoothDevice device);
@@ -99,7 +100,6 @@
boolean factoryReset();
boolean isMultiAdvertisementSupported();
- boolean isPeripheralModeSupported();
boolean isOffloadedFilteringSupported();
boolean isOffloadedScanBatchingSupported();
boolean isActivityAndEnergyReportingSupported();
diff --git a/core/java/android/bluetooth/IBluetoothMapClient.aidl b/core/java/android/bluetooth/IBluetoothMapClient.aidl
new file mode 100644
index 0000000..df45af9
--- /dev/null
+++ b/core/java/android/bluetooth/IBluetoothMapClient.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.app.PendingIntent;
+import android.bluetooth.BluetoothDevice;
+import android.net.Uri;
+
+/**
+ * System private API for Bluetooth MAP MCE service
+ *
+ * {@hide}
+ */
+interface IBluetoothMapClient {
+ boolean connect(in BluetoothDevice device);
+ boolean disconnect(in BluetoothDevice device);
+ boolean isConnected(in BluetoothDevice device);
+ List<BluetoothDevice> getConnectedDevices();
+ List<BluetoothDevice> getDevicesMatchingConnectionStates(in int[] states);
+ int getConnectionState(in BluetoothDevice device);
+ boolean setPriority(in BluetoothDevice device,in int priority);
+ int getPriority(in BluetoothDevice device);
+ boolean sendMessage(in BluetoothDevice device, in Uri[] contacts, in String message,
+ in PendingIntent sentIntent, in PendingIntent deliveryIntent);
+ boolean getUnreadMessages(in BluetoothDevice device);
+}
diff --git a/core/java/android/bluetooth/OobData.java b/core/java/android/bluetooth/OobData.java
index f53ca94..9e87230 100644
--- a/core/java/android/bluetooth/OobData.java
+++ b/core/java/android/bluetooth/OobData.java
@@ -30,10 +30,24 @@
* @hide
*/
public class OobData implements Parcelable {
+ private byte[] leBluetoothDeviceAddress;
private byte[] securityManagerTk;
private byte[] leSecureConnectionsConfirmation;
private byte[] leSecureConnectionsRandom;
+ public byte[] getLeBluetoothDeviceAddress() {
+ return leBluetoothDeviceAddress;
+ }
+
+ /**
+ * Sets the LE Bluetooth Device Address value to be used during LE pairing.
+ * The value shall be 7 bytes. Please see Bluetooth CSSv6, Part A 1.16 for
+ * a detailed description.
+ */
+ public void setLeBluetoothDeviceAddress(byte[] leBluetoothDeviceAddress) {
+ this.leBluetoothDeviceAddress = leBluetoothDeviceAddress;
+ }
+
public byte[] getSecurityManagerTk() {
return securityManagerTk;
}
@@ -66,6 +80,7 @@
public OobData() { }
private OobData(Parcel in) {
+ leBluetoothDeviceAddress = in.createByteArray();
securityManagerTk = in.createByteArray();
leSecureConnectionsConfirmation = in.createByteArray();
leSecureConnectionsRandom = in.createByteArray();
@@ -77,6 +92,7 @@
@Override
public void writeToParcel(Parcel out, int flags) {
+ out.writeByteArray(leBluetoothDeviceAddress);
out.writeByteArray(securityManagerTk);
out.writeByteArray(leSecureConnectionsConfirmation);
out.writeByteArray(leSecureConnectionsRandom);
diff --git a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
index 26f2dea..94d03e5 100644
--- a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
+++ b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java
@@ -111,12 +111,6 @@
if (callback == null) {
throw new IllegalArgumentException("callback cannot be null");
}
- if (!mBluetoothAdapter.isMultipleAdvertisementSupported() &&
- !mBluetoothAdapter.isPeripheralModeSupported()) {
- postStartFailure(callback,
- AdvertiseCallback.ADVERTISE_FAILED_FEATURE_UNSUPPORTED);
- return;
- }
boolean isConnectable = settings.isConnectable();
if (totalBytes(advertiseData, isConnectable) > MAX_ADVERTISING_DATA_BYTES ||
totalBytes(scanResponse, false) > MAX_ADVERTISING_DATA_BYTES) {
@@ -236,11 +230,12 @@
private final AdvertiseSettings mSettings;
private final IBluetoothGatt mBluetoothGatt;
- // mAdvertiserId 0: not registered
- // -1: advertise stopped or registration timeout
- // >0: registered and advertising started
+ // mAdvertiserId -1: not registered
+ // -2: advertise stopped or registration timeout
+ // >=0: registered and advertising started
private int mAdvertiserId;
private boolean mIsAdvertising = false;
+ private int registrationError = AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR;
public AdvertiseCallbackWrapper(AdvertiseCallback advertiseCallback,
AdvertiseData advertiseData, AdvertiseData scanResponse,
@@ -251,12 +246,12 @@
mScanResponse = scanResponse;
mSettings = settings;
mBluetoothGatt = bluetoothGatt;
- mAdvertiserId = 0;
+ mAdvertiserId = -1;
}
public void startRegisteration() {
synchronized (this) {
- if (mAdvertiserId == -1) return;
+ if (mAdvertiserId == -2) return;
try {
mBluetoothGatt.registerAdvertiser(this);
@@ -264,21 +259,20 @@
} catch (InterruptedException | RemoteException e) {
Log.e(TAG, "Failed to start registeration", e);
}
- if (mAdvertiserId > 0 && mIsAdvertising) {
+ if (mAdvertiserId >= 0 && mIsAdvertising) {
mLeAdvertisers.put(mAdvertiseCallback, this);
- } else if (mAdvertiserId <= 0) {
+ } else if (mAdvertiserId < 0) {
- // Registration timeout, reset mClientIf to -1 so no subsequent operations can
+ // Registration timeout, reset mClientIf to -2 so no subsequent operations can
// proceed.
- if (mAdvertiserId == 0) mAdvertiserId = -1;
+ if (mAdvertiserId == -1) mAdvertiserId = -2;
// Post internal error if registration failed.
- postStartFailure(mAdvertiseCallback,
- AdvertiseCallback.ADVERTISE_FAILED_INTERNAL_ERROR);
+ postStartFailure(mAdvertiseCallback, registrationError);
} else {
// Unregister application if it's already registered but advertise failed.
try {
mBluetoothGatt.unregisterAdvertiser(mAdvertiserId);
- mAdvertiserId = -1;
+ mAdvertiserId = -2;
} catch (RemoteException e) {
Log.e(TAG, "remote exception when unregistering", e);
}
@@ -312,7 +306,7 @@
synchronized (this) {
if (status == BluetoothGatt.GATT_SUCCESS) {
try {
- if (mAdvertiserId == -1) {
+ if (mAdvertiserId == -2) {
// Registration succeeds after timeout, unregister advertiser.
mBluetoothGatt.unregisterAdvertiser(advertiserId);
} else {
@@ -324,9 +318,11 @@
} catch (RemoteException e) {
Log.e(TAG, "failed to start advertising", e);
}
+ } else if (status == AdvertiseCallback.ADVERTISE_FAILED_TOO_MANY_ADVERTISERS) {
+ registrationError = status;
}
// Registration failed.
- mAdvertiserId = -1;
+ mAdvertiserId = -2;
notifyAll();
}
}
@@ -348,7 +344,7 @@
// unregister advertiser for stop.
try {
mBluetoothGatt.unregisterAdvertiser(mAdvertiserId);
- mAdvertiserId = -1;
+ mAdvertiserId = -2;
mIsAdvertising = false;
mLeAdvertisers.remove(mAdvertiseCallback);
} catch (RemoteException e) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index fee927c..b79c72e 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2664,7 +2664,7 @@
NETWORK_STATS_SERVICE,
//@hide: NETWORK_POLICY_SERVICE,
WIFI_SERVICE,
- WIFI_NAN_SERVICE,
+ WIFI_AWARE_SERVICE,
WIFI_P2P_SERVICE,
WIFI_SCANNING_SERVICE,
//@hide: WIFI_RTT_SERVICE,
@@ -3134,14 +3134,14 @@
/**
* Use with {@link #getSystemService} to retrieve a
- * {@link android.net.wifi.nan.WifiNanManager} for handling management of
- * Wi-Fi NAN.
+ * {@link android.net.wifi.aware.WifiAwareManager} for handling management of
+ * Wi-Fi Aware.
*
* @see #getSystemService
- * @see android.net.wifi.nan.WifiNanManager
- * @hide PROPOSED_NAN_API
+ * @see android.net.wifi.aware.WifiAwareManager
+ * @hide PROPOSED_AWARE_API
*/
- public static final String WIFI_NAN_SERVICE = "wifinan";
+ public static final String WIFI_AWARE_SERVICE = "wifiaware";
/**
* Use with {@link #getSystemService} to retrieve a {@link
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 089a420..8122940 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2028,12 +2028,12 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and
- * {@link #hasSystemFeature}: The device supports Wi-Fi NAN.
+ * {@link #hasSystemFeature}: The device supports Wi-Fi Aware.
*
- * @hide PROPOSED_NAN_API
+ * @hide PROPOSED_AWARE_API
*/
@SdkConstant(SdkConstantType.FEATURE)
- public static final String FEATURE_WIFI_NAN = "android.hardware.wifi.nan";
+ public static final String FEATURE_WIFI_AWARE = "android.hardware.wifi.aware";
/**
* Feature for {@link #getSystemAvailableFeatures} and
diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl
index 6e4c9de..00b0bff 100644
--- a/core/java/android/hardware/usb/IUsbManager.aidl
+++ b/core/java/android/hardware/usb/IUsbManager.aidl
@@ -87,15 +87,13 @@
/* Returns true if the specified USB function is enabled. */
boolean isFunctionEnabled(String function);
- /* Sets the current USB function. */
- void setCurrentFunction(String function);
-
- /* Sets whether USB data (for example, MTP exposed pictures) should be made
- * available on the USB connection. Unlocking data should only be done with
- * user involvement, since exposing pictures or other data could leak sensitive
- * user information.
+ /* Sets the current USB function as well as whether USB data
+ * (for example, MTP exposed pictures) should be made available
+ * on the USB connection. Unlocking data should only be done with
+ * user involvement, since exposing pictures or other data could
+ * leak sensitive user information.
*/
- void setUsbDataUnlocked(boolean unlock);
+ void setCurrentFunction(String function, boolean usbDataUnlocked);
/* Allow USB debugging from the attached host. If alwaysAllow is true, add the
* the public key to list of host keys that the user has approved.
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index f9a7d19..6341cbc 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -509,33 +509,23 @@
* {@link #USB_FUNCTION_MIDI}, {@link #USB_FUNCTION_MTP}, {@link #USB_FUNCTION_PTP},
* or {@link #USB_FUNCTION_RNDIS}.
* </p><p>
+ * Also sets whether USB data (for example, MTP exposed pictures) should be made available
+ * on the USB connection when in device mode. Unlocking usb data should only be done with
+ * user involvement, since exposing pictures or other data could leak sensitive
+ * user information.
+ * </p><p>
* Note: This function is asynchronous and may fail silently without applying
* the requested changes.
* </p>
*
* @param function name of the USB function, or null to restore the default function
+ * @param usbDataUnlocked whether user data is accessible
*
* {@hide}
*/
- public void setCurrentFunction(String function) {
+ public void setCurrentFunction(String function, boolean usbDataUnlocked) {
try {
- mService.setCurrentFunction(function);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Sets whether USB data (for example, MTP exposed pictures) should be made available
- * on the USB connection when in device mode. Unlocking usb data should only be done with
- * user involvement, since exposing pictures or other data could leak sensitive
- * user information.
- *
- * {@hide}
- */
- public void setUsbDataUnlocked(boolean unlocked) {
- try {
- mService.setUsbDataUnlocked(unlocked);
+ mService.setCurrentFunction(function, usbDataUnlocked);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index ebb9601..cbb6dfe 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -406,14 +406,14 @@
public static final int TRANSPORT_VPN = 4;
/**
- * Indicates this network uses a Wi-Fi NAN transport.
+ * Indicates this network uses a Wi-Fi Aware transport.
*
- * @hide PROPOSED_NAN_API
+ * @hide PROPOSED_AWARE_API
*/
- public static final int TRANSPORT_WIFI_NAN = 5;
+ public static final int TRANSPORT_WIFI_AWARE = 5;
private static final int MIN_TRANSPORT = TRANSPORT_CELLULAR;
- private static final int MAX_TRANSPORT = TRANSPORT_WIFI_NAN;
+ private static final int MAX_TRANSPORT = TRANSPORT_WIFI_AWARE;
/**
* Adds the given transport type to this {@code NetworkCapability} instance.
@@ -869,7 +869,7 @@
case TRANSPORT_BLUETOOTH: transports += "BLUETOOTH"; break;
case TRANSPORT_ETHERNET: transports += "ETHERNET"; break;
case TRANSPORT_VPN: transports += "VPN"; break;
- case TRANSPORT_WIFI_NAN: transports += "WIFI_NAN"; break;
+ case TRANSPORT_WIFI_AWARE: transports += "WIFI_AWARE"; break;
}
if (++i < types.length) transports += "|";
}
diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java
index 01c160f..b93bf59 100644
--- a/core/java/android/net/NetworkScoreManager.java
+++ b/core/java/android/net/NetworkScoreManager.java
@@ -144,7 +144,7 @@
* scorer.
*/
public String getActiveScorerPackage() {
- NetworkScorerAppData app = NetworkScorerAppManager.getActiveScorer(mContext);
+ NetworkScorerAppData app = new NetworkScorerAppManager(mContext).getActiveScorer();
if (app == null) {
return null;
}
diff --git a/core/java/android/net/NetworkScorerAppManager.java b/core/java/android/net/NetworkScorerAppManager.java
index 29291ca..ebb31c90 100644
--- a/core/java/android/net/NetworkScorerAppManager.java
+++ b/core/java/android/net/NetworkScorerAppManager.java
@@ -41,14 +41,17 @@
*
* @hide
*/
-public final class NetworkScorerAppManager {
+public class NetworkScorerAppManager {
private static final String TAG = "NetworkScorerAppManager";
private static final Intent SCORE_INTENT =
new Intent(NetworkScoreManager.ACTION_SCORE_NETWORKS);
- /** This class cannot be instantiated. */
- private NetworkScorerAppManager() {}
+ private final Context mContext;
+
+ public NetworkScorerAppManager(Context context) {
+ mContext = context;
+ }
public static class NetworkScorerAppData {
/** Package name of this scorer app. */
@@ -108,7 +111,7 @@
*
* @return the list of scorers, or the empty list if there are no valid scorers.
*/
- public static Collection<NetworkScorerAppData> getAllValidScorers(Context context) {
+ public Collection<NetworkScorerAppData> getAllValidScorers() {
// Network scorer apps can only run as the primary user so exit early if we're not the
// primary user.
if (UserHandle.getCallingUserId() != UserHandle.USER_SYSTEM) {
@@ -116,7 +119,7 @@
}
List<NetworkScorerAppData> scorers = new ArrayList<>();
- PackageManager pm = context.getPackageManager();
+ PackageManager pm = mContext.getPackageManager();
// Only apps installed under the primary user of the device can be scorers.
// TODO: http://b/23422763
List<ResolveInfo> receivers =
@@ -179,10 +182,10 @@
* selected) or if the previously-set scorer is no longer a valid scorer app (e.g. because
* it was disabled or uninstalled).
*/
- public static NetworkScorerAppData getActiveScorer(Context context) {
- String scorerPackage = Settings.Global.getString(context.getContentResolver(),
+ public NetworkScorerAppData getActiveScorer() {
+ String scorerPackage = Settings.Global.getString(mContext.getContentResolver(),
Settings.Global.NETWORK_SCORER_APP);
- return getScorer(context, scorerPackage);
+ return getScorer(scorerPackage);
}
/**
@@ -190,13 +193,12 @@
*
* <p>The caller must have permission to write to {@link android.provider.Settings.Global}.
*
- * @param context the context of the calling application
* @param packageName the packageName of the new scorer to use. If null, scoring will be
* disabled. Otherwise, the scorer will only be set if it is a valid scorer application.
* @return true if the scorer was changed, or false if the package is not a valid scorer.
*/
- public static boolean setActiveScorer(Context context, String packageName) {
- String oldPackageName = Settings.Global.getString(context.getContentResolver(),
+ public boolean setActiveScorer(String packageName) {
+ String oldPackageName = Settings.Global.getString(mContext.getContentResolver(),
Settings.Global.NETWORK_SCORER_APP);
if (TextUtils.equals(oldPackageName, packageName)) {
// No change.
@@ -206,13 +208,13 @@
Log.i(TAG, "Changing network scorer from " + oldPackageName + " to " + packageName);
if (packageName == null) {
- Settings.Global.putString(context.getContentResolver(),
+ Settings.Global.putString(mContext.getContentResolver(),
Settings.Global.NETWORK_SCORER_APP, null);
return true;
} else {
// We only make the change if the new package is valid.
- if (getScorer(context, packageName) != null) {
- Settings.Global.putString(context.getContentResolver(),
+ if (getScorer(packageName) != null) {
+ Settings.Global.putString(mContext.getContentResolver(),
Settings.Global.NETWORK_SCORER_APP, packageName);
return true;
} else {
@@ -223,8 +225,8 @@
}
/** Determine whether the application with the given UID is the enabled scorer. */
- public static boolean isCallerActiveScorer(Context context, int callingUid) {
- NetworkScorerAppData defaultApp = getActiveScorer(context);
+ public boolean isCallerActiveScorer(int callingUid) {
+ NetworkScorerAppData defaultApp = getActiveScorer();
if (defaultApp == null) {
return false;
}
@@ -233,16 +235,16 @@
}
// To be extra safe, ensure the caller holds the SCORE_NETWORKS permission. It always
// should, since it couldn't become the active scorer otherwise, but this can't hurt.
- return context.checkCallingPermission(Manifest.permission.SCORE_NETWORKS) ==
+ return mContext.checkCallingPermission(Manifest.permission.SCORE_NETWORKS) ==
PackageManager.PERMISSION_GRANTED;
}
/** Returns the {@link NetworkScorerAppData} for the given app, or null if it's not a scorer. */
- public static NetworkScorerAppData getScorer(Context context, String packageName) {
+ public NetworkScorerAppData getScorer(String packageName) {
if (TextUtils.isEmpty(packageName)) {
return null;
}
- Collection<NetworkScorerAppData> applications = getAllValidScorers(context);
+ Collection<NetworkScorerAppData> applications = getAllValidScorers();
for (NetworkScorerAppData app : applications) {
if (packageName.equals(app.mPackageName)) {
return app;
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index 1ac9fca..e7436be 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -108,6 +108,26 @@
*/
public static final int TAG_SYSTEM_RESTORE = 0xFFFFFF04;
+ /** @hide */
+ public static final int TAG_SYSTEM_DHCP = 0xFFFFFF05;
+ /** @hide */
+ public static final int TAG_SYSTEM_NTP = 0xFFFFFF06;
+ /** @hide */
+ public static final int TAG_SYSTEM_PROBE = 0xFFFFFF07;
+ /** @hide */
+ public static final int TAG_SYSTEM_NEIGHBOR = 0xFFFFFF08;
+ /** @hide */
+ public static final int TAG_SYSTEM_GPS = 0xFFFFFF09;
+ /** @hide */
+ public static final int TAG_SYSTEM_PAC = 0xFFFFFF0A;
+
+ /**
+ * Sockets that are strictly local on device; never hits network.
+ *
+ * @hide
+ */
+ public static final int TAG_SYSTEM_LOCAL = 0xFFFFFFAA;
+
private static INetworkStatsService sStatsService;
private synchronized static INetworkStatsService getStatsService() {
diff --git a/core/java/android/os/Binder.java b/core/java/android/os/Binder.java
index ea8ba2f..50a3f4c 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -129,6 +129,17 @@
return sTransactionTracker;
}
+ /** @hide */
+ public static IBinder allowBlocking(IBinder binder) {
+ // NOTE: real implementation on internal branch
+ return binder;
+ }
+
+ /** @hide */
+ public static void copyAllowBlocking(IBinder fromBinder, IBinder toBinder) {
+ // NOTE: real implementation on internal branch
+ }
+
/* mObject is used by native code, do not remove or rename */
private long mObject;
private IInterface mOwner;
diff --git a/core/java/android/os/CountDownTimer.java b/core/java/android/os/CountDownTimer.java
index 58acbcf..c7bf0fd 100644
--- a/core/java/android/os/CountDownTimer.java
+++ b/core/java/android/os/CountDownTimer.java
@@ -125,19 +125,28 @@
if (millisLeft <= 0) {
onFinish();
- } else if (millisLeft < mCountdownInterval) {
- // no tick, just delay until done
- sendMessageDelayed(obtainMessage(MSG), millisLeft);
} else {
long lastTickStart = SystemClock.elapsedRealtime();
onTick(millisLeft);
// take into account user's onTick taking time to execute
- long delay = lastTickStart + mCountdownInterval - SystemClock.elapsedRealtime();
+ long lastTickDuration = SystemClock.elapsedRealtime() - lastTickStart;
+ long delay;
- // special case: user's onTick took more than interval to
- // complete, skip to next interval
- while (delay < 0) delay += mCountdownInterval;
+ if (millisLeft < mCountdownInterval) {
+ // just delay until done
+ delay = millisLeft - lastTickDuration;
+
+ // special case: user's onTick took more than interval to
+ // complete, trigger onFinish without delay
+ if (delay < 0) delay = 0;
+ } else {
+ delay = mCountdownInterval - lastTickDuration;
+
+ // special case: user's onTick took more than interval to
+ // complete, skip to next interval
+ while (delay < 0) delay += mCountdownInterval;
+ }
sendMessageDelayed(obtainMessage(MSG), delay);
}
diff --git a/core/java/android/os/HwBinder.java b/core/java/android/os/HwBinder.java
index 0e7da63..481b2dc 100644
--- a/core/java/android/os/HwBinder.java
+++ b/core/java/android/os/HwBinder.java
@@ -16,6 +16,7 @@
package android.os;
+import java.util.ArrayList;
import libcore.util.NativeAllocationRegistry;
/** @hide */
@@ -39,10 +40,12 @@
int code, HwParcel request, HwParcel reply, int flags);
public native final void registerService(
- String serviceName, int versionMajor, int versionMinor);
+ ArrayList<String> interfaceChain,
+ String serviceName);
public static native final IHwBinder getService(
- String serviceName, int versionMajor, int versionMinor);
+ String iface,
+ String serviceName);
// Returns address of the "freeFunction".
private static native final long native_init();
diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java
index 1479035..e47c238 100644
--- a/core/java/android/os/SystemProperties.java
+++ b/core/java/android/os/SystemProperties.java
@@ -39,6 +39,7 @@
private static native boolean native_get_boolean(String key, boolean def);
private static native void native_set(String key, String def);
private static native void native_add_change_callback();
+ private static native void native_report_sysprop_change();
/**
* Get the value for the given key.
@@ -151,4 +152,11 @@
}
}
}
+
+ /*
+ * Notifies listeners that a system property has changed
+ */
+ public static void reportSyspropChanged() {
+ native_report_sysprop_change();
+ }
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index eaf56ac..2f93afd 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -370,6 +370,22 @@
"android.settings.WIFI_IP_SETTINGS";
/**
+ * Activity Action: Show settings to allow configuration of Wi-Fi saved networks.
+ * <p>
+ * In some cases, a matching Activity may not exist, so ensure you
+ * safeguard against this.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing.
+ * @hide
+ */
+ @SystemApi
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_WIFI_SAVED_NETWORK_SETTINGS =
+ "android.settings.WIFI_SAVED_NETWORK_SETTINGS";
+
+ /**
* Activity Action: Show settings to allow configuration of Bluetooth.
* <p>
* In some cases, a matching Activity may not exist, so ensure you
@@ -7932,6 +7948,9 @@
BLUETOOTH_MAP_PRIORITY_PREFIX = "bluetooth_map_priority_";
/** {@hide} */
public static final String
+ BLUETOOTH_MAP_CLIENT_PRIORITY_PREFIX = "bluetooth_map_client_priority_";
+ /** {@hide} */
+ public static final String
BLUETOOTH_PBAP_CLIENT_PRIORITY_PREFIX = "bluetooth_pbap_client_priority_";
/** {@hide} */
public static final String
@@ -8132,6 +8151,14 @@
}
/**
+ * Get the key that retrieves a bluetooth map client priority.
+ * @hide
+ */
+ public static final String getBluetoothMapClientPriorityKey(String address) {
+ return BLUETOOTH_MAP_CLIENT_PRIORITY_PREFIX + address.toUpperCase(Locale.ROOT);
+ }
+
+ /**
* Get the key that retrieves a bluetooth pbap client priority.
* @hide
*/
@@ -8140,7 +8167,7 @@
}
/**
- * Get the key that retrieves a bluetooth map priority.
+ * Get the key that retrieves a bluetooth sap priority.
* @hide
*/
public static final String getBluetoothSapPriorityKey(String address) {
diff --git a/core/java/android/util/jar/StrictJarFile.java b/core/java/android/util/jar/StrictJarFile.java
index 60e4adf..d9556aa 100644
--- a/core/java/android/util/jar/StrictJarFile.java
+++ b/core/java/android/util/jar/StrictJarFile.java
@@ -17,19 +17,24 @@
package android.util.jar;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+
import dalvik.system.CloseGuard;
+import java.io.FileDescriptor;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.io.RandomAccessFile;
import java.security.cert.Certificate;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
+import java.util.jar.JarFile;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
import java.util.zip.ZipEntry;
-import java.util.jar.JarFile;
+import libcore.io.IoBridge;
import libcore.io.IoUtils;
import libcore.io.Streams;
@@ -46,7 +51,7 @@
// NOTE: It's possible to share a file descriptor with the native
// code, at the cost of some additional complexity.
- private final RandomAccessFile raf;
+ private final FileDescriptor fd;
private final StrictJarManifest manifest;
private final StrictJarVerifier verifier;
@@ -61,8 +66,30 @@
this(fileName, true, true);
}
+ public StrictJarFile(FileDescriptor fd)
+ throws IOException, SecurityException {
+ this(fd, true, true);
+ }
+
+ public StrictJarFile(FileDescriptor fd,
+ boolean verify,
+ boolean signatureSchemeRollbackProtectionsEnforced)
+ throws IOException, SecurityException {
+ this("[fd:" + fd.getInt$() + "]", fd, verify,
+ signatureSchemeRollbackProtectionsEnforced);
+ }
+
+ public StrictJarFile(String fileName,
+ boolean verify,
+ boolean signatureSchemeRollbackProtectionsEnforced)
+ throws IOException, SecurityException {
+ this(fileName, IoBridge.open(fileName, OsConstants.O_RDONLY),
+ verify, signatureSchemeRollbackProtectionsEnforced);
+ }
+
/**
- *
+ * @param name of the archive (not necessarily a path).
+ * @param fd seekable file descriptor for the JAR file.
* @param verify whether to verify the file's JAR signatures and collect the corresponding
* signer certificates.
* @param signatureSchemeRollbackProtectionsEnforced {@code true} to enforce protections against
@@ -70,12 +97,13 @@
* {@code false} to ignore any such protections. This parameter is ignored when
* {@code verify} is {@code false}.
*/
- public StrictJarFile(String fileName,
+ private StrictJarFile(String name,
+ FileDescriptor fd,
boolean verify,
boolean signatureSchemeRollbackProtectionsEnforced)
throws IOException, SecurityException {
- this.nativeHandle = nativeOpenJarFile(fileName);
- this.raf = new RandomAccessFile(fileName, "r");
+ this.nativeHandle = nativeOpenJarFile(name, fd.getInt$());
+ this.fd = fd;
try {
// Read the MANIFEST and signature files up front and try to
@@ -86,14 +114,14 @@
this.manifest = new StrictJarManifest(metaEntries.get(JarFile.MANIFEST_NAME), true);
this.verifier =
new StrictJarVerifier(
- fileName,
+ name,
manifest,
metaEntries,
signatureSchemeRollbackProtectionsEnforced);
Set<String> files = manifest.getEntries().keySet();
for (String file : files) {
if (findEntry(file) == null) {
- throw new SecurityException(fileName + ": File " + file + " in manifest does not exist");
+ throw new SecurityException("File " + file + " in manifest does not exist");
}
}
@@ -105,7 +133,7 @@
}
} catch (IOException | SecurityException e) {
nativeClose(this.nativeHandle);
- IoUtils.closeQuietly(this.raf);
+ IoUtils.closeQuietly(fd);
throw e;
}
@@ -192,10 +220,12 @@
public void close() throws IOException {
if (!closed) {
- guard.close();
+ if (guard != null) {
+ guard.close();
+ }
nativeClose(nativeHandle);
- IoUtils.closeQuietly(raf);
+ IoUtils.closeQuietly(fd);
closed = true;
}
}
@@ -214,11 +244,11 @@
private InputStream getZipInputStream(ZipEntry ze) {
if (ze.getMethod() == ZipEntry.STORED) {
- return new RAFStream(raf, ze.getDataOffset(),
+ return new FDStream(fd, ze.getDataOffset(),
ze.getDataOffset() + ze.getSize());
} else {
- final RAFStream wrapped = new RAFStream(
- raf, ze.getDataOffset(), ze.getDataOffset() + ze.getCompressedSize());
+ final FDStream wrapped = new FDStream(
+ fd, ze.getDataOffset(), ze.getDataOffset() + ze.getCompressedSize());
int bufSize = Math.max(1024, (int) Math.min(ze.getSize(), 65535L));
return new ZipInflaterInputStream(wrapped, new Inflater(true), bufSize, ze);
@@ -396,7 +426,7 @@
}
/**
- * Wrap a stream around a RandomAccessFile. The RandomAccessFile is shared
+ * Wrap a stream around a FileDescriptor. The file descriptor is shared
* among all streams returned by getInputStream(), so we have to synchronize
* access to it. (We can optimize this by adding buffering here to reduce
* collisions.)
@@ -405,22 +435,17 @@
*
* @hide
*/
- public static class RAFStream extends InputStream {
- private final RandomAccessFile sharedRaf;
+ public static class FDStream extends InputStream {
+ private final FileDescriptor fd;
private long endOffset;
private long offset;
-
- public RAFStream(RandomAccessFile raf, long initialOffset, long endOffset) {
- sharedRaf = raf;
+ public FDStream(FileDescriptor fd, long initialOffset, long endOffset) {
+ this.fd = fd;
offset = initialOffset;
this.endOffset = endOffset;
}
- public RAFStream(RandomAccessFile raf, long initialOffset) throws IOException {
- this(raf, initialOffset, raf.length());
- }
-
@Override public int available() throws IOException {
return (offset < endOffset ? 1 : 0);
}
@@ -430,13 +455,17 @@
}
@Override public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
- synchronized (sharedRaf) {
+ synchronized (this.fd) {
final long length = endOffset - offset;
if (byteCount > length) {
byteCount = (int) length;
}
- sharedRaf.seek(offset);
- int count = sharedRaf.read(buffer, byteOffset, byteCount);
+ try {
+ Os.lseek(fd, offset, OsConstants.SEEK_SET);
+ } catch (ErrnoException e) {
+ throw new IOException(e);
+ }
+ int count = IoBridge.read(fd, buffer, byteOffset, byteCount);
if (count > 0) {
offset += count;
return count;
@@ -455,8 +484,8 @@
}
}
-
- private static native long nativeOpenJarFile(String fileName) throws IOException;
+ private static native long nativeOpenJarFile(String name, int fd)
+ throws IOException;
private static native long nativeStartIteration(long nativeHandle, String prefix);
private static native ZipEntry nativeNextEntry(long iterationHandle);
private static native ZipEntry nativeFindEntry(long nativeHandle, String entryName);
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index f41a838..f1e8fc2d 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -56,7 +56,9 @@
@SystemApi
public final class WebViewFactory {
- private static final String CHROMIUM_WEBVIEW_FACTORY =
+ // visible for WebViewZygoteInit to look up the class by reflection and call preloadInZygote.
+ /** @hide */
+ public static final String CHROMIUM_WEBVIEW_FACTORY =
"com.android.webview.chromium.WebViewChromiumFactoryProvider";
private static final String NULL_WEBVIEW_FACTORY =
diff --git a/core/java/android/webkit/WebViewZygote.java b/core/java/android/webkit/WebViewZygote.java
index bc6e7b4..c206974 100644
--- a/core/java/android/webkit/WebViewZygote.java
+++ b/core/java/android/webkit/WebViewZygote.java
@@ -16,14 +16,19 @@
package android.webkit;
+import android.app.LoadedApk;
import android.content.pm.PackageInfo;
import android.os.Build;
import android.os.SystemService;
import android.os.ZygoteProcess;
+import android.text.TextUtils;
import android.util.Log;
+import java.io.File;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.List;
import java.util.concurrent.TimeoutException;
/** @hide */
@@ -122,11 +127,21 @@
try {
sZygote = new ZygoteProcess("webview_zygote", null);
- String packagePath = sPackage.applicationInfo.sourceDir;
- String libsPath = sPackage.applicationInfo.nativeLibraryDir;
+ // All the work below is usually done by LoadedApk, but the zygote can't talk to
+ // PackageManager or construct a LoadedApk since it's single-threaded pre-fork, so
+ // doesn't have an ActivityThread and can't use Binder.
+ // Instead, figure out the paths here, in the system server where we have access to
+ // the package manager. Reuse the logic from LoadedApk to determine the correct
+ // paths and pass them to the zygote as strings.
+ final List<String> zipPaths = new ArrayList<>(10);
+ final List<String> libPaths = new ArrayList<>(10);
+ LoadedApk.makePaths(null, sPackage.applicationInfo, zipPaths, libPaths);
+ final String librarySearchPath = TextUtils.join(File.pathSeparator, libPaths);
+ final String zip = (zipPaths.size() == 1) ? zipPaths.get(0) :
+ TextUtils.join(File.pathSeparator, zipPaths);
- Log.d(LOGTAG, "Preloading package " + packagePath + " " + libsPath);
- sZygote.preloadPackageForAbi(packagePath, libsPath, Build.SUPPORTED_ABIS[0]);
+ Log.d(LOGTAG, "Preloading package " + zip + " " + librarySearchPath);
+ sZygote.preloadPackageForAbi(zip, librarySearchPath, Build.SUPPORTED_ABIS[0]);
} catch (Exception e) {
Log.e(LOGTAG, "Error connecting to " + serviceName, e);
sZygote = null;
diff --git a/core/java/com/android/internal/os/WebViewZygoteInit.java b/core/java/com/android/internal/os/WebViewZygoteInit.java
index 11dd0e8..d968e3c 100644
--- a/core/java/com/android/internal/os/WebViewZygoteInit.java
+++ b/core/java/com/android/internal/os/WebViewZygoteInit.java
@@ -16,14 +16,17 @@
package com.android.internal.os;
+import android.app.ApplicationLoaders;
import android.net.LocalSocket;
import android.os.Build;
import android.system.ErrnoException;
import android.system.Os;
import android.text.TextUtils;
import android.util.Log;
+import android.webkit.WebViewFactory;
import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
/**
* Startup class for the WebView zygote process.
@@ -52,7 +55,27 @@
@Override
protected boolean handlePreloadPackage(String packagePath, String libsPath) {
- // TODO: Use preload information to setup the ClassLoader.
+ // Ask ApplicationLoaders to create and cache a classloader for the WebView APK so that
+ // our children will reuse the same classloader instead of creating their own.
+ // This enables us to preload Java and native code in the webview zygote process and
+ // have the preloaded versions actually be used post-fork.
+ ClassLoader loader = ApplicationLoaders.getDefault().createAndCacheWebViewClassLoader(
+ packagePath, libsPath);
+
+ // Once we have the classloader, look up the WebViewFactoryProvider implementation and
+ // call preloadInZygote() on it to give it the opportunity to preload the native library
+ // and perform any other initialisation work that should be shared among the children.
+ try {
+ Class providerClass = Class.forName(WebViewFactory.CHROMIUM_WEBVIEW_FACTORY, true,
+ loader);
+ Object result = providerClass.getMethod("preloadInZygote").invoke(null);
+ if (!((Boolean)result).booleanValue()) {
+ Log.e(TAG, "preloadInZygote returned false");
+ }
+ } catch (ClassNotFoundException | NoSuchMethodException | SecurityException |
+ IllegalAccessException | InvocationTargetException e) {
+ Log.e(TAG, "Exception while preloading package", e);
+ }
return false;
}
}
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 1c307c9..9979b84 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -258,7 +258,7 @@
continue;
}
- Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadClass " + line);
+ Trace.traceBegin(Trace.TRACE_TAG_DALVIK, line);
try {
if (false) {
Log.v(TAG, "Preloading " + line + "...");
diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java
index b0d45e1d1..be10608df 100644
--- a/core/java/com/android/internal/util/StateMachine.java
+++ b/core/java/com/android/internal/util/StateMachine.java
@@ -1190,6 +1190,26 @@
}
/**
+ * Remove a state from the state machine. Will not remove the state if it is currently
+ * active or if it has any children in the hierarchy.
+ * @param state the state to remove
+ */
+ private void removeState(State state) {
+ StateInfo stateInfo = mStateInfo.get(state);
+ if (stateInfo == null || stateInfo.active) {
+ return;
+ }
+ boolean isParent = mStateInfo.values().stream()
+ .filter(si -> si.parentStateInfo == stateInfo)
+ .findAny()
+ .isPresent();
+ if (isParent) {
+ return;
+ }
+ mStateInfo.remove(state);
+ }
+
+ /**
* Constructor
*
* @param looper for dispatching messages
@@ -1337,6 +1357,14 @@
}
/**
+ * Removes a state from the state machine, unless it is currently active or if it has children.
+ * @param state state to remove
+ */
+ public final void removeState(State state) {
+ mSmHandler.removeState(state);
+ }
+
+ /**
* Set the initial state. This must be invoked before
* and messages are sent to the state machine.
*
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index bb55610..9a596c6 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -250,6 +250,7 @@
libicuuc \
libicui18n \
libmedia \
+ libaudioclient \
libjpeg \
libusbhost \
libharfbuzz_ng \
@@ -266,7 +267,8 @@
libradio_metadata \
libnativeloader \
libmemunreachable \
- libhidl \
+ libhidlbase \
+ libhidltransport \
libhwbinder \
LOCAL_SHARED_LIBRARIES += \
diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp
index 816d5df..10090a1 100644
--- a/core/jni/android_os_HwBinder.cpp
+++ b/core/jni/android_os_HwBinder.cpp
@@ -24,8 +24,9 @@
#include "android_os_HwRemoteBinder.h"
#include <JNIHelp.h>
+#include <android/hidl/manager/1.0/IServiceManager.h>
#include <android_runtime/AndroidRuntime.h>
-#include <hidl/IServiceManager.h>
+#include <hidl/ServiceManagement.h>
#include <hidl/Status.h>
#include <hwbinder/ProcessState.h>
#include <nativehelper/ScopedLocalRef.h>
@@ -33,6 +34,8 @@
#include "core_jni_helpers.h"
using android::AndroidRuntime;
+using android::hardware::hidl_vec;
+using android::hardware::hidl_string;
#define PACKAGE_PATH "android/os"
#define CLASS_NAME "HwBinder"
@@ -40,10 +43,15 @@
namespace android {
+static jclass gArrayListClass;
+static struct {
+ jmethodID size;
+ jmethodID get;
+} gArrayListMethods;
+
static struct fields_t {
jfieldID contextID;
jmethodID onTransactID;
-
} gFields;
// static
@@ -198,93 +206,96 @@
static void JHwBinder_native_registerService(
JNIEnv *env,
jobject thiz,
- jstring serviceNameObj,
- jint versionMajor,
- jint versionMinor) {
+ jobject interfaceChainArrayList,
+ jstring serviceNameObj) {
if (serviceNameObj == NULL) {
jniThrowException(env, "java/lang/NullPointerException", NULL);
return;
}
- if (versionMajor < 0
- || versionMajor > 65535
- || versionMinor < 0
- || versionMinor > 65535) {
- jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
- return;
- }
-
- const jchar *serviceName = env->GetStringCritical(serviceNameObj, NULL);
-
+ const char *serviceName = env->GetStringUTFChars(serviceNameObj, NULL);
if (serviceName == NULL) {
return; // XXX exception already pending?
}
- const hardware::hidl_version kVersion =
- hardware::make_hidl_version(versionMajor, versionMinor);
+ jint numInterfaces = env->CallIntMethod(interfaceChainArrayList,
+ gArrayListMethods.size);
+ hidl_string *strings = new hidl_string[numInterfaces];
+
+ for (jint i = 0; i < numInterfaces; i++) {
+ jstring strObj = static_cast<jstring>(
+ env->CallObjectMethod(interfaceChainArrayList,
+ gArrayListMethods.get,
+ i)
+ );
+ const char * str = env->GetStringUTFChars(strObj, nullptr);
+ strings[i] = hidl_string(str);
+ env->ReleaseStringUTFChars(strObj, str);
+ }
+
+ hidl_vec<hidl_string> interfaceChain;
+ interfaceChain.setToExternal(strings, numInterfaces, true /* shouldOwn */);
+
+ using android::hidl::manager::V1_0::IServiceManager;
sp<hardware::IBinder> binder = JHwBinder::GetNativeContext(env, thiz);
- status_t err = hardware::defaultServiceManager()->addService(
- String16(
- reinterpret_cast<const char16_t *>(serviceName),
- env->GetStringLength(serviceNameObj)),
- binder,
- kVersion);
+ bool ok = hardware::defaultServiceManager()->add(
+ interfaceChain,
+ serviceName,
+ binder);
- env->ReleaseStringCritical(serviceNameObj, serviceName);
+ env->ReleaseStringUTFChars(serviceNameObj, serviceName);
serviceName = NULL;
- if (err == OK) {
+ if (ok) {
LOG(INFO) << "Starting thread pool.";
::android::hardware::ProcessState::self()->startThreadPool();
}
- signalExceptionForError(env, err);
+ signalExceptionForError(env, (ok ? OK : UNKNOWN_ERROR));
}
static jobject JHwBinder_native_getService(
JNIEnv *env,
jclass /* clazzObj */,
- jstring serviceNameObj,
- jint versionMajor,
- jint versionMinor) {
+ jstring ifaceNameObj,
+ jstring serviceNameObj) {
+
+ if (ifaceNameObj == NULL) {
+ jniThrowException(env, "java/lang/NullPointerException", NULL);
+ return NULL;
+ }
if (serviceNameObj == NULL) {
jniThrowException(env, "java/lang/NullPointerException", NULL);
return NULL;
}
- if (versionMajor < 0
- || versionMajor > 65535
- || versionMinor < 0
- || versionMinor > 65535) {
- jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
- return NULL;
+ const char *ifaceName = env->GetStringUTFChars(ifaceNameObj, NULL);
+ if (ifaceName == NULL) {
+ return NULL; // XXX exception already pending?
}
-
- const jchar *serviceName = env->GetStringCritical(serviceNameObj, NULL);
-
+ const char *serviceName = env->GetStringUTFChars(serviceNameObj, NULL);
if (serviceName == NULL) {
- return NULL; // XXX exception already pending?
+ env->ReleaseStringUTFChars(ifaceNameObj, ifaceName);
+ return NULL; // XXX exception already pending?
}
- const hardware::hidl_version kVersion =
- hardware::make_hidl_version(versionMajor, versionMinor);
-
LOG(INFO) << "looking for service '"
- << String8(String16(
- reinterpret_cast<const char16_t *>(serviceName),
- env->GetStringLength(serviceNameObj))).string()
+ << serviceName
<< "'";
- sp<hardware::IBinder> service =
- hardware::defaultServiceManager()->getService(
- String16(
- reinterpret_cast<const char16_t *>(serviceName),
- env->GetStringLength(serviceNameObj)),
- kVersion);
+ sp<hardware::IBinder> service;
+ hardware::defaultServiceManager()->get(
+ ifaceName,
+ serviceName,
+ [&service](sp<hardware::IBinder> out) {
+ service = out;
+ });
- env->ReleaseStringCritical(serviceNameObj, serviceName);
+ env->ReleaseStringUTFChars(ifaceNameObj, ifaceName);
+ ifaceName = NULL;
+ env->ReleaseStringUTFChars(serviceNameObj, serviceName);
serviceName = NULL;
if (service == NULL) {
@@ -306,16 +317,21 @@
"(IL" PACKAGE_PATH "/HwParcel;L" PACKAGE_PATH "/HwParcel;I)V",
(void *)JHwBinder_native_transact },
- { "registerService", "(Ljava/lang/String;II)V",
+ { "registerService", "(Ljava/util/ArrayList;Ljava/lang/String;)V",
(void *)JHwBinder_native_registerService },
- { "getService", "(Ljava/lang/String;II)L" PACKAGE_PATH "/IHwBinder;",
+ { "getService", "(Ljava/lang/String;Ljava/lang/String;)L" PACKAGE_PATH "/IHwBinder;",
(void *)JHwBinder_native_getService },
};
namespace android {
int register_android_os_HwBinder(JNIEnv *env) {
+ jclass arrayListClass = FindClassOrDie(env, "java/util/ArrayList");
+ gArrayListClass = MakeGlobalRefOrDie(env, arrayListClass);
+ gArrayListMethods.size = GetMethodIDOrDie(env, arrayListClass, "size", "()I");
+ gArrayListMethods.get = GetMethodIDOrDie(env, arrayListClass, "get", "(I)Ljava/lang/Object;");
+
return RegisterMethodsOrDie(env, CLASS_PATH, gMethods, NELEM(gMethods));
}
diff --git a/core/jni/android_os_HwBlob.h b/core/jni/android_os_HwBlob.h
index 6bd82e9..0920488 100644
--- a/core/jni/android_os_HwBlob.h
+++ b/core/jni/android_os_HwBlob.h
@@ -20,6 +20,7 @@
#include <android-base/macros.h>
#include <jni.h>
#include <hidl/HidlSupport.h>
+#include <hwbinder/Parcel.h>
#include <utils/RefBase.h>
#include <utils/Vector.h>
diff --git a/core/jni/android_os_HwParcel.cpp b/core/jni/android_os_HwParcel.cpp
index 7387b29..a10d807 100644
--- a/core/jni/android_os_HwParcel.cpp
+++ b/core/jni/android_os_HwParcel.cpp
@@ -26,6 +26,7 @@
#include <JNIHelp.h>
#include <android_runtime/AndroidRuntime.h>
+#include <hidl/HidlTransportSupport.h>
#include <hidl/Status.h>
#include <nativehelper/ScopedLocalRef.h>
@@ -267,17 +268,17 @@
const jchar *interfaceName = env->GetStringCritical(interfaceNameObj, NULL);
if (interfaceName) {
- hardware::Parcel *parcel =
- JHwParcel::GetNativeContext(env, thiz)->getParcel();
-
- status_t err = parcel->writeInterfaceToken(
- String16(
- reinterpret_cast<const char16_t *>(interfaceName),
- env->GetStringLength(interfaceNameObj)));
+ String8 nameCopy = String8(String16(
+ reinterpret_cast<const char16_t *>(interfaceName),
+ env->GetStringLength(interfaceNameObj)));
env->ReleaseStringCritical(interfaceNameObj, interfaceName);
interfaceName = NULL;
+ hardware::Parcel *parcel =
+ JHwParcel::GetNativeContext(env, thiz)->getParcel();
+
+ status_t err = parcel->writeInterfaceToken(nameCopy.string());
signalExceptionForError(env, err);
}
}
@@ -294,17 +295,18 @@
const jchar *interfaceName = env->GetStringCritical(interfaceNameObj, NULL);
if (interfaceName) {
- hardware::Parcel *parcel =
- JHwParcel::GetNativeContext(env, thiz)->getParcel();
-
- bool valid = parcel->enforceInterface(
- String16(
- reinterpret_cast<const char16_t *>(interfaceName),
- env->GetStringLength(interfaceNameObj)));
+ String8 interfaceNameCopy = String8(String16(
+ reinterpret_cast<const char16_t *>(interfaceName),
+ env->GetStringLength(interfaceNameObj)));
env->ReleaseStringCritical(interfaceNameObj, interfaceName);
interfaceName = NULL;
+ hardware::Parcel *parcel =
+ JHwParcel::GetNativeContext(env, thiz)->getParcel();
+
+ bool valid = parcel->enforceInterface(interfaceNameCopy.string());
+
if (!valid) {
jniThrowException(
env,
@@ -382,7 +384,7 @@
hardware::Parcel *parcel =
JHwParcel::GetNativeContext(env, thiz)->getParcel();
- status_t err = status.writeToParcel(parcel);
+ status_t err = ::android::hardware::writeToParcel(status, parcel);
signalExceptionForError(env, err);
}
@@ -393,7 +395,7 @@
JHwParcel::GetNativeContext(env, thiz)->getParcel();
Status status;
- status_t err = status.readFromParcel(*parcel);
+ status_t err = ::android::hardware::readFromParcel(&status, *parcel);
signalExceptionForError(env, err);
}
@@ -424,8 +426,8 @@
status_t err = parcel->writeBuffer(s, sizeof(*s), &parentHandle);
if (err == OK) {
- err = s->writeEmbeddedToParcel(
- parcel, parentHandle, 0 /* parentOffset */);
+ err = ::android::hardware::writeEmbeddedToParcel(
+ *s, parcel, parentHandle, 0 /* parentOffset */);
}
signalExceptionForError(env, err);
@@ -452,7 +454,8 @@
if (err == OK) { \
size_t childHandle; \
\
- err = vec->writeEmbeddedToParcel( \
+ err = ::android::hardware::writeEmbeddedToParcel( \
+ *vec, \
parcel, \
parentHandle, \
0 /* parentOffset */, \
@@ -507,7 +510,8 @@
if (err == OK) {
size_t childHandle;
- err = vec->writeEmbeddedToParcel(
+ err = ::android::hardware::writeEmbeddedToParcel(
+ *vec,
parcel,
parentHandle,
0 /* parentOffset */,
@@ -567,7 +571,8 @@
return NULL;
}
- status_t err = const_cast<hidl_string *>(s)->readEmbeddedFromParcel(
+ status_t err = ::android::hardware::readEmbeddedFromParcel(
+ const_cast<hidl_string *>(s),
*parcel, parentHandle, 0 /* parentOffset */);
if (err != OK) {
@@ -596,8 +601,8 @@
\
size_t childHandle; \
\
- status_t err = const_cast<hidl_vec<Type> *>(vec) \
- ->readEmbeddedFromParcel( \
+ status_t err = ::android::hardware::readEmbeddedFromParcel( \
+ const_cast<hidl_vec<Type> *>(vec), \
*parcel, \
parentHandle, \
0 /* parentOffset */, \
@@ -638,8 +643,8 @@
size_t childHandle;
- status_t err = const_cast<hidl_vec<bool> *>(vec)
- ->readEmbeddedFromParcel(
+ status_t err = ::android::hardware::readEmbeddedFromParcel(
+ const_cast<hidl_vec<bool> *>(vec),
*parcel,
parentHandle,
0 /* parentOffset */,
@@ -700,12 +705,13 @@
}
size_t childHandle;
- status_t err = const_cast<string_vec *>(vec)->readEmbeddedFromParcel(
+ status_t err = ::android::hardware::readEmbeddedFromParcel(
+ const_cast<string_vec *>(vec),
*parcel, parentHandle, 0 /* parentOffset */, &childHandle);
for (size_t i = 0; (err == OK) && (i < vec->size()); ++i) {
- err = const_cast<hidl_vec<hidl_string> *>(vec)
- ->readEmbeddedFromParcel(
+ err = android::hardware::readEmbeddedFromParcel(
+ const_cast<hidl_vec<hidl_string> *>(vec),
*parcel,
childHandle,
i * sizeof(hidl_string),
@@ -759,14 +765,16 @@
if (err == OK) {
size_t childHandle;
- err = vec->writeEmbeddedToParcel(
+ err = ::android::hardware::writeEmbeddedToParcel(
+ *vec,
parcel,
parentHandle,
0 /* parentOffset */,
&childHandle);
for (size_t i = 0; (err == OK) && (i < vec->size()); ++i) {
- err = (*vec)[i].writeEmbeddedToParcel(
+ err = ::android::hardware::writeEmbeddedToParcel(
+ (*vec)[i],
parcel,
childHandle,
i * sizeof(hidl_string));
diff --git a/core/jni/android_os_HwRemoteBinder.cpp b/core/jni/android_os_HwRemoteBinder.cpp
index 3023ba8..1d5d6d5 100644
--- a/core/jni/android_os_HwRemoteBinder.cpp
+++ b/core/jni/android_os_HwRemoteBinder.cpp
@@ -24,7 +24,6 @@
#include <JNIHelp.h>
#include <android_runtime/AndroidRuntime.h>
-#include <hidl/IServiceManager.h>
#include <hidl/Status.h>
#include <nativehelper/ScopedLocalRef.h>
diff --git a/core/jni/android_os_SystemProperties.cpp b/core/jni/android_os_SystemProperties.cpp
index 5dace6b..8844fb0 100644
--- a/core/jni/android_os_SystemProperties.cpp
+++ b/core/jni/android_os_SystemProperties.cpp
@@ -220,6 +220,11 @@
}
}
+static void SystemProperties_report_sysprop_change(JNIEnv /**env*/, jobject /*clazz*/)
+{
+ report_sysprop_change();
+}
+
static const JNINativeMethod method_table[] = {
{ "native_get", "(Ljava/lang/String;)Ljava/lang/String;",
(void*) SystemProperties_getS },
@@ -235,6 +240,8 @@
(void*) SystemProperties_set },
{ "native_add_change_callback", "()V",
(void*) SystemProperties_add_change_callback },
+ { "native_report_sysprop_change", "()V",
+ (void*) SystemProperties_report_sysprop_change },
};
int register_android_os_SystemProperties(JNIEnv *env)
diff --git a/core/jni/android_util_jar_StrictJarFile.cpp b/core/jni/android_util_jar_StrictJarFile.cpp
index bfdea8f..2e31c8b 100644
--- a/core/jni/android_util_jar_StrictJarFile.cpp
+++ b/core/jni/android_util_jar_StrictJarFile.cpp
@@ -51,14 +51,16 @@
static_cast<jlong>(entry.offset));
}
-static jlong StrictJarFile_nativeOpenJarFile(JNIEnv* env, jobject, jstring fileName) {
- ScopedUtfChars fileChars(env, fileName);
- if (fileChars.c_str() == NULL) {
+static jlong StrictJarFile_nativeOpenJarFile(JNIEnv* env, jobject, jstring name, jint fd) {
+ // Name argument is used for logging, and can be any string.
+ ScopedUtfChars nameChars(env, name);
+ if (nameChars.c_str() == NULL) {
return static_cast<jlong>(-1);
}
ZipArchiveHandle handle;
- int32_t error = OpenArchive(fileChars.c_str(), &handle);
+ int32_t error = OpenArchiveFd(fd, nameChars.c_str(), &handle,
+ false /* owned by Java side */);
if (error) {
CloseArchive(handle);
throwIoException(env, error);
@@ -154,7 +156,7 @@
}
static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(StrictJarFile, nativeOpenJarFile, "(Ljava/lang/String;)J"),
+ NATIVE_METHOD(StrictJarFile, nativeOpenJarFile, "(Ljava/lang/String;I)J"),
NATIVE_METHOD(StrictJarFile, nativeStartIteration, "(JLjava/lang/String;)J"),
NATIVE_METHOD(StrictJarFile, nativeNextEntry, "(J)Ljava/util/zip/ZipEntry;"),
NATIVE_METHOD(StrictJarFile, nativeFindEntry, "(JLjava/lang/String;)Ljava/util/zip/ZipEntry;"),
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 5202a98..ca09708 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -297,12 +297,6 @@
bool force_mount_namespace) {
// See storage config details at http://source.android.com/tech/storage/
- // Create a second private mount namespace for our process
- if (unshare(CLONE_NEWNS) == -1) {
- ALOGW("Failed to unshare(): %s", strerror(errno));
- return false;
- }
-
String8 storageSource;
if (mount_mode == MOUNT_EXTERNAL_DEFAULT) {
storageSource = "/mnt/runtime/default";
@@ -310,10 +304,17 @@
storageSource = "/mnt/runtime/read";
} else if (mount_mode == MOUNT_EXTERNAL_WRITE) {
storageSource = "/mnt/runtime/write";
- } else {
+ } else if (!force_mount_namespace) {
// Sane default of no storage visible
return true;
}
+
+ // Create a second private mount namespace for our process
+ if (unshare(CLONE_NEWNS) == -1) {
+ ALOGW("Failed to unshare(): %s", strerror(errno));
+ return false;
+ }
+
if (TEMP_FAILURE_RETRY(mount(storageSource.string(), "/storage",
NULL, MS_BIND | MS_REC | MS_SLAVE, NULL)) == -1) {
ALOGW("Failed to mount %s to /storage: %s", storageSource.string(), strerror(errno));
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c816502..33ba886 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -291,7 +291,7 @@
<protected-broadcast android:name="android.net.wifi.WIFI_AP_STATE_CHANGED" />
<protected-broadcast android:name="android.net.wifi.WIFI_CREDENTIAL_CHANGED" />
<protected-broadcast android:name="android.net.wifi.WIFI_SCAN_AVAILABLE" />
- <protected-broadcast android:name="android.net.wifi.nan.action.WIFI_NAN_STATE_CHANGED" />
+ <protected-broadcast android:name="android.net.wifi.aware.action.WIFI_AWARE_STATE_CHANGED" />
<protected-broadcast android:name="android.net.wifi.SCAN_RESULTS" />
<protected-broadcast android:name="android.net.wifi.RSSI_CHANGED" />
<protected-broadcast android:name="android.net.wifi.STATE_CHANGE" />
@@ -299,7 +299,6 @@
<protected-broadcast android:name="android.net.wifi.CONFIGURED_NETWORKS_CHANGE" />
<protected-broadcast android:name="android.net.wifi.supplicant.CONNECTION_CHANGE" />
<protected-broadcast android:name="android.net.wifi.supplicant.STATE_CHANGE" />
- <protected-broadcast android:name="android.net.wifi.nan.STATE_CHANGED" />
<protected-broadcast android:name="android.net.wifi.p2p.STATE_CHANGED" />
<protected-broadcast android:name="android.net.wifi.p2p.DISCOVERY_STATE_CHANGE" />
<protected-broadcast android:name="android.net.wifi.p2p.THIS_DEVICE_CHANGED" />
diff --git a/core/res/res/values-mcc204-mnc04/config.xml b/core/res/res/values-mcc204-mnc04/config.xml
index ddf0e9f..81e7a6f 100755
--- a/core/res/res/values-mcc204-mnc04/config.xml
+++ b/core/res/res/values-mcc204-mnc04/config.xml
@@ -25,12 +25,15 @@
-->
<integer name="config_mobile_mtu">1358</integer>
- <!-- Flag indicating whether strict threshold is used, or lenient threshold is used,
- when evaluating RSRP for LTE antenna bar display
- 0. Strict threshold
- 1. Lenient threshold
- -->
- <integer name="config_LTE_RSRP_threshold_type">0</integer>
+ <!--Thresholds for LTE dbm in status bar-->
+ <integer-array translatable="false" name="config_lteDbmThresholds">
+ <item>-140</item> <!-- SIGNAL_STRENGTH_NONE_OR_UNKNOWN -->
+ <item>-115</item> <!-- SIGNAL_STRENGTH_POOR -->
+ <item>-105</item> <!-- SIGNAL_STRENGTH_MODERATE -->
+ <item>-95</item> <!-- SIGNAL_STRENGTH_GOOD -->
+ <item>-85</item> <!-- SIGNAL_STRENGTH_GREAT -->
+ <item>-44</item>
+ </integer-array>
<string-array translatable="false" name="config_sms_convert_destination_number_support">
<item>true;BAE0000000000000</item>
diff --git a/core/res/res/values-mcc259-mnc05/config.xml b/core/res/res/values-mcc259-mnc05/config.xml
deleted file mode 100644
index 065668c..0000000
--- a/core/res/res/values-mcc259-mnc05/config.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2013, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- The list of ril radio technologies (see ServiceState.java) which only support
- a single data connection at one time. This may change by carrier via
- overlays (some don't support multiple pdp on UMTS). All unlisted radio
- tech types support unlimited types (practically only 2-4 used). -->
- <integer-array name="config_onlySingleDcAllowed">
- <item>1</item> <!-- GPRS -->
- <item>2</item> <!-- EDGE -->
- <item>3</item> <!-- UMTS -->
- <item>9</item> <!-- HSDPA -->
- <item>10</item> <!-- HSUPA -->
- <item>11</item> <!-- HSPA -->
- <item>14</item> <!-- LTE -->
- <item>15</item> <!-- HSPAP -->
- </integer-array>
-</resources>
diff --git a/core/res/res/values-mcc311-mnc480/config.xml b/core/res/res/values-mcc311-mnc480/config.xml
index 8d7fd61..378dee6 100755
--- a/core/res/res/values-mcc311-mnc480/config.xml
+++ b/core/res/res/values-mcc311-mnc480/config.xml
@@ -53,12 +53,15 @@
<bool name="config_auto_attach_data_on_creation">false</bool>
- <!-- Flag indicating whether strict threshold is used, or lenient threshold is used,
- when evaluating RSRP for LTE antenna bar display
- 0. Strict threshold
- 1. Lenient threshold
- -->
- <integer name="config_LTE_RSRP_threshold_type">0</integer>
+ <!--Thresholds for LTE dbm in status bar-->
+ <integer-array translatable="false" name="config_lteDbmThresholds">
+ <item>-140</item> <!-- SIGNAL_STRENGTH_NONE_OR_UNKNOWN -->
+ <item>-115</item> <!-- SIGNAL_STRENGTH_POOR -->
+ <item>-105</item> <!-- SIGNAL_STRENGTH_MODERATE -->
+ <item>-95</item> <!-- SIGNAL_STRENGTH_GOOD -->
+ <item>-85</item> <!-- SIGNAL_STRENGTH_GREAT -->
+ <item>-44</item>
+ </integer-array>
<string-array translatable="false" name="config_sms_convert_destination_number_support">
<item>true</item>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index a8be8d3..4c28284 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1867,19 +1867,6 @@
where if the preferred is used we don't try the others. -->
<bool name="config_dontPreferApn">false</bool>
- <!-- The list of ril radio technologies (see ServiceState.java) which only support
- a single data connection at one time. This may change by carrier via
- overlays (some don't support multiple pdp on UMTS). All unlisted radio
- tech types support unlimited types (practically only 2-4 used). -->
- <integer-array name="config_onlySingleDcAllowed">
- <item>4</item> <!-- IS95A -->
- <item>5</item> <!-- IS95B -->
- <item>6</item> <!-- 1xRTT -->
- <item>7</item> <!-- EVDO_0 -->
- <item>8</item> <!-- EVDO_A -->
- <item>12</item> <!-- EVDO_B -->
- </integer-array>
-
<!-- Set to true if after a provisioning apn the radio should be restarted -->
<bool name="config_restartRadioAfterProvisioning">false</bool>
@@ -2165,6 +2152,9 @@
provisioning, availability etc -->
<bool name="config_carrier_wfc_ims_available">false</bool>
+ <!-- Whether to use voip audio mode for ims call -->
+ <bool name="config_use_voip_mode_for_ims">false</bool>
+
<bool name="config_networkSamplingWakesDevice">true</bool>
<string-array translatable="false" name="config_cdma_home_system" />
@@ -2244,12 +2234,15 @@
<bool name="config_sms_force_7bit_encoding">false</bool>
- <!-- Flag indicating whether strict threshold is used, or lenient threshold is used,
- when evaluating RSRP for LTE antenna bar display
- 0. Strict threshold
- 1. Lenient threshold
- -->
- <integer name="config_LTE_RSRP_threshold_type">1</integer>
+ <!--Thresholds for LTE dbm in status bar-->
+ <integer-array translatable="false" name="config_lteDbmThresholds">
+ <item>-140</item> <!-- SIGNAL_STRENGTH_NONE_OR_UNKNOWN -->
+ <item>-128</item> <!-- SIGNAL_STRENGTH_POOR -->
+ <item>-118</item> <!-- SIGNAL_STRENGTH_MODERATE -->
+ <item>-108</item> <!-- SIGNAL_STRENGTH_GOOD -->
+ <item>-98</item> <!-- SIGNAL_STRENGTH_GREAT -->
+ <item>-44</item>
+ </integer-array>
<!-- Enabled built-in zen mode condition providers -->
<string-array translatable="false" name="config_system_condition_providers">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 6f2822b..27ddb5a 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1648,7 +1648,6 @@
<java-symbol type="array" name="config_testLocationProviders" />
<java-symbol type="array" name="config_defaultNotificationVibePattern" />
<java-symbol type="array" name="config_notificationFallbackVibePattern" />
- <java-symbol type="array" name="config_onlySingleDcAllowed" />
<java-symbol type="bool" name="config_useAttentionLight" />
<java-symbol type="bool" name="config_animateScreenLights" />
<java-symbol type="bool" name="config_automatic_brightness_available" />
@@ -2195,6 +2194,7 @@
<java-symbol type="bool" name="config_carrier_vt_available" />
<java-symbol type="bool" name="config_device_wfc_ims_available" />
<java-symbol type="bool" name="config_carrier_wfc_ims_available" />
+ <java-symbol type="bool" name="config_use_voip_mode_for_ims" />
<java-symbol type="attr" name="touchscreenBlocksFocus" />
<java-symbol type="layout" name="resolver_list_with_default" />
<java-symbol type="string" name="whichApplicationNamed" />
@@ -2258,7 +2258,7 @@
<java-symbol type="dimen" name="cascading_menus_min_smallest_width" />
<!-- From SignalStrength -->
- <java-symbol type="integer" name="config_LTE_RSRP_threshold_type" />
+ <java-symbol type="array" name="config_lteDbmThresholds" />
<java-symbol type="string" name="android_system_label" />
<java-symbol type="string" name="system_error_wipe_data" />
diff --git a/core/tests/coretests/apks/install_jni_lib/Android.mk b/core/tests/coretests/apks/install_jni_lib/Android.mk
index 9e45d09..d7b38e8 100644
--- a/core/tests/coretests/apks/install_jni_lib/Android.mk
+++ b/core/tests/coretests/apks/install_jni_lib/Android.mk
@@ -19,8 +19,7 @@
LOCAL_SRC_FILES := \
com_android_frameworks_coretests_JNITest.cpp
-LOCAL_SHARED_LIBRARIES := \
- libnativehelper
+LOCAL_SDK_VERSION := 16
LOCAL_CFLAGS += -Wall -Werror
diff --git a/core/tests/coretests/apks/install_jni_lib/com_android_frameworks_coretests_JNITest.cpp b/core/tests/coretests/apks/install_jni_lib/com_android_frameworks_coretests_JNITest.cpp
index 8d91192..0cf3a84 100644
--- a/core/tests/coretests/apks/install_jni_lib/com_android_frameworks_coretests_JNITest.cpp
+++ b/core/tests/coretests/apks/install_jni_lib/com_android_frameworks_coretests_JNITest.cpp
@@ -14,41 +14,23 @@
* limitations under the License.
*/
-#include "nativehelper/JNIHelp.h"
+#include <jni.h>
-namespace android {
-
-static jint checkFunction(JNIEnv*, jclass) {
+extern "C" JNIEXPORT
+jint JNICALL Java_com_android_frameworks_coretests_JNITests_checkFunction(JNIEnv*, jclass) {
return 1;
}
-static const JNINativeMethod sMethods[] = {
- /* name, signature, funcPtr */
- { "checkFunction", "()I", (void*) checkFunction },
-};
-
-int register_com_android_frameworks_coretests_JNITests(JNIEnv* env) {
- return jniRegisterNativeMethods(env, "com/android/frameworks/coretests/JNITests", sMethods,
- NELEM(sMethods));
-}
-
-}
-
/*
* JNI Initialization
*/
jint JNI_OnLoad(JavaVM *jvm, void */* reserved */) {
JNIEnv *e;
- int status;
// Check JNI version
if (jvm->GetEnv((void **) &e, JNI_VERSION_1_6)) {
return JNI_ERR;
}
- if ((status = android::register_com_android_frameworks_coretests_JNITests(e)) < 0) {
- return JNI_ERR;
- }
-
return JNI_VERSION_1_6;
}
diff --git a/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java b/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
index e7aca78..02c2517 100644
--- a/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
+++ b/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java
@@ -42,6 +42,8 @@
@Mock private Context mMockContext;
@Mock private PackageManager mMockPm;
+ private NetworkScorerAppManager mNetworkScorerAppManager;
+
@Override
public void setUp() throws Exception {
super.setUp();
@@ -54,6 +56,7 @@
MockitoAnnotations.initMocks(this);
Mockito.when(mMockContext.getPackageManager()).thenReturn(mMockPm);
+ mNetworkScorerAppManager = new NetworkScorerAppManager(mMockContext);
}
public void testGetAllValidScorers() throws Exception {
@@ -81,7 +84,7 @@
setScorers(scorers);
Iterator<NetworkScorerAppData> result =
- NetworkScorerAppManager.getAllValidScorers(mMockContext).iterator();
+ mNetworkScorerAppManager.getAllValidScorers().iterator();
assertTrue(result.hasNext());
NetworkScorerAppData next = result.next();
diff --git a/core/tests/coretests/src/android/provider/SettingsProviderTest.java b/core/tests/coretests/src/android/provider/SettingsProviderTest.java
index e6d3158..0a32e43 100644
--- a/core/tests/coretests/src/android/provider/SettingsProviderTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsProviderTest.java
@@ -349,6 +349,7 @@
assertCanBeHandled(new Intent(Settings.ACTION_USER_DICTIONARY_SETTINGS));
assertCanBeHandled(new Intent(Settings.ACTION_WIFI_IP_SETTINGS));
assertCanBeHandled(new Intent(Settings.ACTION_WIFI_SETTINGS));
+ assertCanBeHandled(new Intent(Settings.ACTION_WIFI_SAVED_NETWORK_SETTINGS));
assertCanBeHandled(new Intent(Settings.ACTION_WIRELESS_SETTINGS));
}
diff --git a/legacy-test/Android.mk b/legacy-test/Android.mk
new file mode 100644
index 0000000..fe5d8ca
--- /dev/null
+++ b/legacy-test/Android.mk
@@ -0,0 +1,29 @@
+#
+# Copyright (C) 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+# Build the legacy-test library
+# =============================
+# This contains the junit.framework classes that were in Android API level 25.
+include $(CLEAR_VARS)
+
+LOCAL_NO_STANDARD_LIBRARIES := true
+LOCAL_STATIC_JAVA_LIBRARIES := core-junit-static
+
+LOCAL_MODULE := legacy-test
+
+include $(BUILD_JAVA_LIBRARY)
diff --git a/libs/androidfw/Android.mk b/libs/androidfw/Android.mk
index 6bbfcd2..76d521d 100644
--- a/libs/androidfw/Android.mk
+++ b/libs/androidfw/Android.mk
@@ -49,6 +49,8 @@
LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
LOCAL_SRC_FILES:= $(hostSources)
LOCAL_C_INCLUDES := external/zlib
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
include $(BUILD_HOST_STATIC_LIBRARY)
@@ -71,6 +73,8 @@
libutils \
libz
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code
include $(BUILD_SHARED_LIBRARY)
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 07044d0..7a10343 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -804,6 +804,7 @@
sharedRes->add(oass, oidmap, offset + 1, false);
const_cast<AssetManager*>(this)->mAssetPaths.add(oap);
const_cast<AssetManager*>(this)->mZipSet.addOverlay(targetPackagePath, oap);
+ delete oidmap;
}
}
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 10a0a23..e10db05 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -3166,7 +3166,7 @@
{
Package(ResTable* _owner, const Header* _header, const ResTable_package* _package)
: owner(_owner), header(_header), package(_package), typeIdOffset(0) {
- if (dtohs(package->header.headerSize) == sizeof(package)) {
+ if (dtohs(package->header.headerSize) == sizeof(*package)) {
// The package structure is the same size as the definition.
// This means it contains the typeIdOffset field.
typeIdOffset = package->typeIdOffset;
@@ -3539,7 +3539,7 @@
}
if (pi != NULL) {
if (kDebugTableTheme) {
- ALOGI("Desired type index is %zd in avail %zu", t, Res_MAXTYPE + 1);
+ ALOGI("Desired type index is %u in avail %zu", t, Res_MAXTYPE + 1);
}
if (t <= Res_MAXTYPE) {
const type_info& ti = pi->types[t];
diff --git a/include/androidfw/Asset.h b/libs/androidfw/include/androidfw/Asset.h
similarity index 98%
rename from include/androidfw/Asset.h
rename to libs/androidfw/include/androidfw/Asset.h
index ee77e97..36efbe5 100644
--- a/include/androidfw/Asset.h
+++ b/libs/androidfw/include/androidfw/Asset.h
@@ -48,7 +48,7 @@
static int32_t getGlobalCount();
static String8 getAssetAllocations();
-
+
/* used when opening an asset */
typedef enum AccessMode {
ACCESS_UNKNOWN = 0,
@@ -197,7 +197,7 @@
AccessMode mAccessMode; // how the asset was opened
String8 mAssetSource; // debug string
-
+
Asset* mNext; // linked list.
Asset* mPrev;
};
@@ -260,7 +260,7 @@
FileMap* mMap; // for memory map
unsigned char* mBuf; // for read
-
+
const void* ensureAlignment(FileMap* map);
};
@@ -297,7 +297,7 @@
virtual const void* getBuffer(bool wordAligned);
virtual off64_t getLength(void) const { return mUncompressedLen; }
virtual off64_t getRemainingLength(void) const { return mUncompressedLen-mOffset; }
- virtual int openFileDescriptor(off64_t* outStart, off64_t* outLength) const { return -1; }
+ virtual int openFileDescriptor(off64_t* /* outStart */, off64_t* /* outLength */) const { return -1; }
virtual bool isAllocated(void) const { return mBuf != NULL; }
private:
diff --git a/include/androidfw/AssetDir.h b/libs/androidfw/include/androidfw/AssetDir.h
similarity index 100%
rename from include/androidfw/AssetDir.h
rename to libs/androidfw/include/androidfw/AssetDir.h
diff --git a/include/androidfw/AssetManager.h b/libs/androidfw/include/androidfw/AssetManager.h
similarity index 97%
rename from include/androidfw/AssetManager.h
rename to libs/androidfw/include/androidfw/AssetManager.h
index 914ac3d..1a59e44 100644
--- a/include/androidfw/AssetManager.h
+++ b/libs/androidfw/include/androidfw/AssetManager.h
@@ -33,17 +33,8 @@
/*
* Native-app access is via the opaque typedef struct AAssetManager in the C namespace.
*/
-#ifdef __cplusplus
-extern "C" {
-#endif
-
struct AAssetManager { };
-#ifdef __cplusplus
-};
-#endif
-
-
/*
* Now the proper C++ android-namespace definitions
*/
@@ -87,8 +78,8 @@
virtual ~AssetManager(void);
static int32_t getGlobalCount();
-
- /*
+
+ /*
* Add a new source for assets. This can be called multiple times to
* look in multiple places for assets. It can be either a directory (for
* finding assets as raw files on the disk) or a ZIP file. This newly
@@ -203,7 +194,7 @@
*/
FileType getFileType(const char* fileName);
- /*
+ /*
* Return the complete resource table to find things in the package.
*/
const ResTable& getResources(bool required = true) const;
@@ -302,12 +293,12 @@
ResTable* getResourceTable();
ResTable* setResourceTable(ResTable* res);
-
+
bool isUpToDate();
void addOverlay(const asset_path& ap);
bool getOverlay(size_t idx, asset_path* out) const;
-
+
protected:
~SharedZip();
@@ -359,7 +350,7 @@
void addOverlay(const String8& path, const asset_path& overlay);
bool getOverlay(const String8& path, size_t idx, asset_path* out) const;
-
+
private:
void closeZip(int idx);
diff --git a/include/androidfw/AttributeFinder.h b/libs/androidfw/include/androidfw/AttributeFinder.h
similarity index 100%
rename from include/androidfw/AttributeFinder.h
rename to libs/androidfw/include/androidfw/AttributeFinder.h
diff --git a/include/androidfw/BackupHelpers.h b/libs/androidfw/include/androidfw/BackupHelpers.h
similarity index 100%
rename from include/androidfw/BackupHelpers.h
rename to libs/androidfw/include/androidfw/BackupHelpers.h
diff --git a/include/androidfw/ByteBucketArray.h b/libs/androidfw/include/androidfw/ByteBucketArray.h
similarity index 100%
rename from include/androidfw/ByteBucketArray.h
rename to libs/androidfw/include/androidfw/ByteBucketArray.h
diff --git a/include/androidfw/CursorWindow.h b/libs/androidfw/include/androidfw/CursorWindow.h
similarity index 100%
rename from include/androidfw/CursorWindow.h
rename to libs/androidfw/include/androidfw/CursorWindow.h
diff --git a/include/androidfw/DisplayEventDispatcher.h b/libs/androidfw/include/androidfw/DisplayEventDispatcher.h
similarity index 100%
rename from include/androidfw/DisplayEventDispatcher.h
rename to libs/androidfw/include/androidfw/DisplayEventDispatcher.h
diff --git a/include/androidfw/LocaleData.h b/libs/androidfw/include/androidfw/LocaleData.h
similarity index 100%
rename from include/androidfw/LocaleData.h
rename to libs/androidfw/include/androidfw/LocaleData.h
diff --git a/include/androidfw/ObbFile.h b/libs/androidfw/include/androidfw/ObbFile.h
similarity index 96%
rename from include/androidfw/ObbFile.h
rename to libs/androidfw/include/androidfw/ObbFile.h
index 47559cd..3dbf997d 100644
--- a/include/androidfw/ObbFile.h
+++ b/libs/androidfw/include/androidfw/ObbFile.h
@@ -124,20 +124,13 @@
/* Flags for this OBB type. */
int32_t mFlags;
- /* Whether the file is salted. */
- bool mSalted;
-
/* The encryption salt. */
unsigned char mSalt[8];
const char* mFileName;
- size_t mFileSize;
-
size_t mFooterStart;
- unsigned char* mReadBuf;
-
bool parseObbFile(int fd);
};
diff --git a/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
similarity index 100%
rename from include/androidfw/ResourceTypes.h
rename to libs/androidfw/include/androidfw/ResourceTypes.h
diff --git a/include/androidfw/StreamingZipInflater.h b/libs/androidfw/include/androidfw/StreamingZipInflater.h
similarity index 100%
rename from include/androidfw/StreamingZipInflater.h
rename to libs/androidfw/include/androidfw/StreamingZipInflater.h
diff --git a/include/androidfw/TypeWrappers.h b/libs/androidfw/include/androidfw/TypeWrappers.h
similarity index 98%
rename from include/androidfw/TypeWrappers.h
rename to libs/androidfw/include/androidfw/TypeWrappers.h
index 7bdf8af..fd84873 100644
--- a/include/androidfw/TypeWrappers.h
+++ b/libs/androidfw/include/androidfw/TypeWrappers.h
@@ -30,6 +30,7 @@
iterator& operator=(const iterator& rhs) {
mTypeVariant = rhs.mTypeVariant;
mIndex = rhs.mIndex;
+ return *this;
}
bool operator==(const iterator& rhs) const {
diff --git a/include/androidfw/ZipFileRO.h b/libs/androidfw/include/androidfw/ZipFileRO.h
similarity index 100%
rename from include/androidfw/ZipFileRO.h
rename to libs/androidfw/include/androidfw/ZipFileRO.h
diff --git a/include/androidfw/ZipUtils.h b/libs/androidfw/include/androidfw/ZipUtils.h
similarity index 100%
rename from include/androidfw/ZipUtils.h
rename to libs/androidfw/include/androidfw/ZipUtils.h
diff --git a/include/androidfw/misc.h b/libs/androidfw/include/androidfw/misc.h
similarity index 100%
rename from include/androidfw/misc.h
rename to libs/androidfw/include/androidfw/misc.h
diff --git a/libs/hwui/hwui_static_deps.mk b/libs/hwui/hwui_static_deps.mk
index 2990952..dca78b3 100644
--- a/libs/hwui/hwui_static_deps.mk
+++ b/libs/hwui/hwui_static_deps.mk
@@ -24,7 +24,8 @@
libprotobuf-cpp-lite \
libharfbuzz_ng \
libft2 \
- libminikin
+ libminikin \
+ libandroidfw
ifneq (false,$(ANDROID_ENABLE_RENDERSCRIPT))
LOCAL_SHARED_LIBRARIES += libRS libRScpp
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 3c1c0bce..0d9ede0 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -20,6 +20,7 @@
#include "CanvasContext.h"
#include "EglManager.h"
#include "RenderProxy.h"
+#include "utils/FatVector.h"
#include <gui/DisplayEventReceiver.h>
#include <gui/ISurfaceComposer.h>
@@ -282,10 +283,18 @@
"RenderThread Looper POLL_ERROR!");
nsecs_t nextWakeup;
- // Process our queue, if we have anything
- while (RenderTask* task = nextTask(&nextWakeup)) {
- task->run();
- // task may have deleted itself, do not reference it again
+ {
+ FatVector<RenderTask*, 10> workQueue;
+ // Process our queue, if we have anything. By first acquiring
+ // all the pending events then processing them we avoid vsync
+ // starvation if more tasks are queued while we are processing tasks.
+ while (RenderTask* task = nextTask(&nextWakeup)) {
+ workQueue.push_back(task);
+ }
+ for (auto task : workQueue) {
+ task->run();
+ // task may have deleted itself, do not reference it again
+ }
}
if (nextWakeup == LLONG_MAX) {
timeoutMillis = -1;
diff --git a/libs/hwui/tests/microbench/FrameBuilderBench.cpp b/libs/hwui/tests/microbench/FrameBuilderBench.cpp
index 84ef9c2..362890b 100644
--- a/libs/hwui/tests/microbench/FrameBuilderBench.cpp
+++ b/libs/hwui/tests/microbench/FrameBuilderBench.cpp
@@ -113,7 +113,7 @@
void BM_FrameBuilder_defer_scene(benchmark::State& state) {
TestUtils::runOnRenderThread([&state](RenderThread& thread) {
- const char* sceneName = *(SCENES.begin() + state.range_x());
+ const char* sceneName = *(SCENES.begin() + state.range(0));
state.SetLabel(sceneName);
auto node = getSyncedSceneNode(sceneName);
while (state.KeepRunning()) {
@@ -129,7 +129,7 @@
void BM_FrameBuilder_deferAndRender_scene(benchmark::State& state) {
TestUtils::runOnRenderThread([&state](RenderThread& thread) {
- const char* sceneName = *(SCENES.begin() + state.range_x());
+ const char* sceneName = *(SCENES.begin() + state.range(0));
state.SetLabel(sceneName);
auto node = getSyncedSceneNode(sceneName);
diff --git a/media/java/android/media/AmrInputStream.java b/media/java/android/media/AmrInputStream.java
index f90f1e2..fb91bbb 100644
--- a/media/java/android/media/AmrInputStream.java
+++ b/media/java/android/media/AmrInputStream.java
@@ -18,45 +18,69 @@
import java.io.InputStream;
import java.io.IOException;
+import java.nio.ByteBuffer;
+
+import android.media.MediaCodec.BufferInfo;
+import android.util.Log;
/**
* AmrInputStream
* @hide
*/
-public final class AmrInputStream extends InputStream
-{
- static {
- System.loadLibrary("media_jni");
- }
-
+public final class AmrInputStream extends InputStream {
private final static String TAG = "AmrInputStream";
// frame is 20 msec at 8.000 khz
private final static int SAMPLES_PER_FRAME = 8000 * 20 / 1000;
-
+
+ MediaCodec mCodec;
+ BufferInfo mInfo;
+ boolean mSawOutputEOS;
+ boolean mSawInputEOS;
+
// pcm input stream
private InputStream mInputStream;
-
- // native handle
- private long mGae;
-
+
// result amr stream
private final byte[] mBuf = new byte[SAMPLES_PER_FRAME * 2];
private int mBufIn = 0;
private int mBufOut = 0;
-
+
// helper for bytewise read()
private byte[] mOneByte = new byte[1];
-
+
/**
* Create a new AmrInputStream, which converts 16 bit PCM to AMR
* @param inputStream InputStream containing 16 bit PCM.
*/
public AmrInputStream(InputStream inputStream) {
mInputStream = inputStream;
- mGae = GsmAmrEncoderNew();
- GsmAmrEncoderInitialize(mGae);
+
+ MediaFormat format = new MediaFormat();
+ format.setString(MediaFormat.KEY_MIME, MediaFormat.MIMETYPE_AUDIO_AMR_NB);
+ format.setInteger(MediaFormat.KEY_SAMPLE_RATE, 8000);
+ format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
+ format.setInteger(MediaFormat.KEY_BIT_RATE, 12200);
+
+ MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+ String name = mcl.findEncoderForFormat(format);
+ if (name != null) {
+ try {
+ mCodec = MediaCodec.createByCodecName(name);
+ mCodec.configure(format,
+ null /* surface */,
+ null /* crypto */,
+ MediaCodec.CONFIGURE_FLAG_ENCODE);
+ mCodec.start();
+ } catch (IOException e) {
+ if (mCodec != null) {
+ mCodec.release();
+ }
+ mCodec = null;
+ }
+ }
+ mInfo = new BufferInfo();
}
@Override
@@ -64,7 +88,7 @@
int rtn = read(mOneByte, 0, 1);
return rtn == 1 ? (0xff & mOneByte[0]) : -1;
}
-
+
@Override
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
@@ -72,67 +96,100 @@
@Override
public int read(byte[] b, int offset, int length) throws IOException {
- if (mGae == 0) throw new IllegalStateException("not open");
-
- // local buffer of amr encoded audio empty
- if (mBufOut >= mBufIn) {
- // reset the buffer
+ if (mCodec == null) {
+ throw new IllegalStateException("not open");
+ }
+
+ if (mBufOut >= mBufIn && !mSawOutputEOS) {
+ // no data left in buffer, refill it
mBufOut = 0;
mBufIn = 0;
-
- // fetch a 20 msec frame of pcm
- for (int i = 0; i < SAMPLES_PER_FRAME * 2; ) {
- int n = mInputStream.read(mBuf, i, SAMPLES_PER_FRAME * 2 - i);
- if (n == -1) return -1;
- i += n;
+
+ // first push as much data into the encoder as possible
+ while (!mSawInputEOS) {
+ int index = mCodec.dequeueInputBuffer(0);
+ if (index < 0) {
+ // no input buffer currently available
+ break;
+ } else {
+ int numRead;
+ for (numRead = 0; numRead < SAMPLES_PER_FRAME * 2; ) {
+ int n = mInputStream.read(mBuf, numRead, SAMPLES_PER_FRAME * 2 - numRead);
+ if (n == -1) {
+ mSawInputEOS = true;
+ break;
+ }
+ numRead += n;
+ }
+ ByteBuffer buf = mCodec.getInputBuffer(index);
+ buf.put(mBuf, 0, numRead);
+ mCodec.queueInputBuffer(index,
+ 0 /* offset */,
+ numRead,
+ 0 /* presentationTimeUs */,
+ mSawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0 /* flags */);
+ }
}
-
- // encode it
- mBufIn = GsmAmrEncoderEncode(mGae, mBuf, 0, mBuf, 0);
+
+ // now read encoded data from the encoder (blocking, since we just filled up the
+ // encoder's input with data it should be able to output at least one buffer)
+ while (true) {
+ int index = mCodec.dequeueOutputBuffer(mInfo, -1);
+ if (index >= 0) {
+ mBufIn = mInfo.size;
+ ByteBuffer out = mCodec.getOutputBuffer(index);
+ out.get(mBuf, 0 /* offset */, mBufIn /* length */);
+ mCodec.releaseOutputBuffer(index, false /* render */);
+ if ((mInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+ mSawOutputEOS = true;
+ }
+ break;
+ }
+ }
}
-
- // return encoded audio to user
- if (length > mBufIn - mBufOut) length = mBufIn - mBufOut;
- System.arraycopy(mBuf, mBufOut, b, offset, length);
- mBufOut += length;
-
- return length;
+
+ if (mBufOut < mBufIn) {
+ // there is data in the buffer
+ if (length > mBufIn - mBufOut) {
+ length = mBufIn - mBufOut;
+ }
+ System.arraycopy(mBuf, mBufOut, b, offset, length);
+ mBufOut += length;
+ return length;
+ }
+
+ if (mSawInputEOS && mSawOutputEOS) {
+ // no more data available in buffer, codec or input stream
+ return -1;
+ }
+
+ // caller should try again
+ return 0;
}
@Override
public void close() throws IOException {
try {
- if (mInputStream != null) mInputStream.close();
+ if (mInputStream != null) {
+ mInputStream.close();
+ }
} finally {
mInputStream = null;
try {
- if (mGae != 0) GsmAmrEncoderCleanup(mGae);
- } finally {
- try {
- if (mGae != 0) GsmAmrEncoderDelete(mGae);
- } finally {
- mGae = 0;
+ if (mCodec != null) {
+ mCodec.release();
}
+ } finally {
+ mCodec = null;
}
}
}
@Override
protected void finalize() throws Throwable {
- if (mGae != 0) {
- close();
- throw new IllegalStateException("someone forgot to close AmrInputStream");
+ if (mCodec != null) {
+ Log.w(TAG, "AmrInputStream wasn't closed");
+ mCodec.release();
}
}
-
- //
- // AudioRecord JNI interface
- //
- private static native long GsmAmrEncoderNew();
- private static native void GsmAmrEncoderInitialize(long gae);
- private static native int GsmAmrEncoderEncode(long gae,
- byte[] pcm, int pcmOffset, byte[] amr, int amrOffset) throws IOException;
- private static native void GsmAmrEncoderCleanup(long gae);
- private static native void GsmAmrEncoderDelete(long gae);
-
}
diff --git a/media/jni/Android.mk b/media/jni/Android.mk
index 2c28a10..8640565 100644
--- a/media/jni/Android.mk
+++ b/media/jni/Android.mk
@@ -2,7 +2,6 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
- android_media_AmrInputStream.cpp \
android_media_ExifInterface.cpp \
android_media_ImageWriter.cpp \
android_media_ImageReader.cpp \
@@ -46,10 +45,9 @@
libusbhost \
libexif \
libpiex \
- libstagefright_amrnb_common
+ libandroidfw
LOCAL_STATIC_LIBRARIES := \
- libstagefright_amrnbenc
LOCAL_C_INCLUDES += \
external/libexif/ \
@@ -59,9 +57,6 @@
frameworks/base/libs/hwui \
frameworks/av/media/libmedia \
frameworks/av/media/libstagefright \
- frameworks/av/media/libstagefright/codecs/amrnb/enc/src \
- frameworks/av/media/libstagefright/codecs/amrnb/common \
- frameworks/av/media/libstagefright/codecs/amrnb/common/include \
frameworks/av/media/mtp \
frameworks/native/include/media/openmax \
$(call include-path-for, libhardware)/hardware \
diff --git a/media/jni/android_media_AmrInputStream.cpp b/media/jni/android_media_AmrInputStream.cpp
deleted file mode 100644
index b56a364..0000000
--- a/media/jni/android_media_AmrInputStream.cpp
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
-**
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#define LOG_TAG "AmrInputStream"
-#include "utils/Log.h"
-
-#include "jni.h"
-#include "JNIHelp.h"
-#include "android_runtime/AndroidRuntime.h"
-#include "gsmamr_enc.h"
-
-// ----------------------------------------------------------------------------
-
-using namespace android;
-
-// Corresponds to max bit rate of 12.2 kbps.
-static const int MAX_OUTPUT_BUFFER_SIZE = 32;
-static const int FRAME_DURATION_MS = 20;
-static const int SAMPLING_RATE_HZ = 8000;
-static const int SAMPLES_PER_FRAME = ((SAMPLING_RATE_HZ * FRAME_DURATION_MS) / 1000);
-static const int BYTES_PER_SAMPLE = 2; // Assume 16-bit PCM samples
-static const int BYTES_PER_FRAME = (SAMPLES_PER_FRAME * BYTES_PER_SAMPLE);
-
-struct GsmAmrEncoderState {
- GsmAmrEncoderState()
- : mEncState(NULL),
- mSidState(NULL),
- mLastModeUsed(0) {
- }
-
- ~GsmAmrEncoderState() {}
-
- void* mEncState;
- void* mSidState;
- int32_t mLastModeUsed;
-};
-
-static jlong android_media_AmrInputStream_GsmAmrEncoderNew
- (JNIEnv *env, jclass /* clazz */) {
- GsmAmrEncoderState* gae = new GsmAmrEncoderState();
- if (gae == NULL) {
- jniThrowRuntimeException(env, "Out of memory");
- }
- return (jlong)gae;
-}
-
-static void android_media_AmrInputStream_GsmAmrEncoderInitialize
- (JNIEnv *env, jclass /* clazz */, jlong gae) {
- GsmAmrEncoderState *state = (GsmAmrEncoderState *) gae;
- int32_t nResult = AMREncodeInit(&state->mEncState, &state->mSidState, false);
- if (nResult != OK) {
- jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
- "GsmAmrEncoder initialization failed %d", nResult);
- }
-}
-
-static jint android_media_AmrInputStream_GsmAmrEncoderEncode
- (JNIEnv *env, jclass /* clazz */,
- jlong gae, jbyteArray pcm, jint pcmOffset, jbyteArray amr, jint amrOffset) {
-
- jbyte inBuf[BYTES_PER_FRAME];
- jbyte outBuf[MAX_OUTPUT_BUFFER_SIZE];
-
- env->GetByteArrayRegion(pcm, pcmOffset, sizeof(inBuf), inBuf);
- GsmAmrEncoderState *state = (GsmAmrEncoderState *) gae;
- int32_t length = AMREncode(state->mEncState, state->mSidState,
- (Mode) MR122,
- (int16_t *) inBuf,
- (unsigned char *) outBuf,
- (Frame_Type_3GPP*) &state->mLastModeUsed,
- AMR_TX_WMF);
- if (length < 0) {
- jniThrowExceptionFmt(env, "java/io/IOException",
- "Failed to encode a frame with error code: %d", length);
- return (jint)-1;
- }
-
- // The 1st byte of PV AMR frames are WMF (Wireless Multimedia Forum)
- // bitpacked, i.e.;
- // [P(4) + FT(4)]. Q=1 for good frame, P=padding bit, 0
- // Here we are converting the header to be as specified in Section 5.3 of
- // RFC 3267 (AMR storage format) i.e.
- // [P(1) + FT(4) + Q(1) + P(2)].
- if (length > 0) {
- outBuf[0] = (outBuf[0] << 3) | 0x4;
- }
-
- env->SetByteArrayRegion(amr, amrOffset, length, outBuf);
-
- return (jint)length;
-}
-
-static void android_media_AmrInputStream_GsmAmrEncoderCleanup
- (JNIEnv* /* env */, jclass /* clazz */, jlong gae) {
- GsmAmrEncoderState *state = (GsmAmrEncoderState *) gae;
- AMREncodeExit(&state->mEncState, &state->mSidState);
- state->mEncState = NULL;
- state->mSidState = NULL;
-}
-
-static void android_media_AmrInputStream_GsmAmrEncoderDelete
- (JNIEnv* /* env */, jclass /* clazz */, jlong gae) {
- delete (GsmAmrEncoderState*)gae;
-}
-
-// ----------------------------------------------------------------------------
-
-static const JNINativeMethod gMethods[] = {
- {"GsmAmrEncoderNew", "()J", (void*)android_media_AmrInputStream_GsmAmrEncoderNew},
- {"GsmAmrEncoderInitialize", "(J)V", (void*)android_media_AmrInputStream_GsmAmrEncoderInitialize},
- {"GsmAmrEncoderEncode", "(J[BI[BI)I", (void*)android_media_AmrInputStream_GsmAmrEncoderEncode},
- {"GsmAmrEncoderCleanup", "(J)V", (void*)android_media_AmrInputStream_GsmAmrEncoderCleanup},
- {"GsmAmrEncoderDelete", "(J)V", (void*)android_media_AmrInputStream_GsmAmrEncoderDelete},
-};
-
-
-int register_android_media_AmrInputStream(JNIEnv *env)
-{
- const char* const kClassPathName = "android/media/AmrInputStream";
-
- return AndroidRuntime::registerNativeMethods(env,
- kClassPathName, gMethods, NELEM(gMethods));
-}
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 2fb1a3b..8f14b79 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -1106,7 +1106,6 @@
extern int register_android_media_MediaSync(JNIEnv *env);
extern int register_android_media_ResampleInputStream(JNIEnv *env);
extern int register_android_media_MediaProfiles(JNIEnv *env);
-extern int register_android_media_AmrInputStream(JNIEnv *env);
extern int register_android_mtp_MtpDatabase(JNIEnv *env);
extern int register_android_mtp_MtpDevice(JNIEnv *env);
extern int register_android_mtp_MtpServer(JNIEnv *env);
@@ -1152,11 +1151,6 @@
goto bail;
}
- if (register_android_media_AmrInputStream(env) < 0) {
- ALOGE("ERROR: AmrInputStream native registration failed\n");
- goto bail;
- }
-
if (register_android_media_ResampleInputStream(env) < 0) {
ALOGE("ERROR: ResampleInputStream native registration failed\n");
goto bail;
diff --git a/media/jni/audioeffect/Android.mk b/media/jni/audioeffect/Android.mk
index 5c22c9b..8bd8857 100644
--- a/media/jni/audioeffect/Android.mk
+++ b/media/jni/audioeffect/Android.mk
@@ -11,7 +11,8 @@
libutils \
libandroid_runtime \
libnativehelper \
- libmedia
+ libmedia \
+ libaudioclient \
LOCAL_C_INCLUDES := \
$(call include-path-for, audio-effects)
diff --git a/media/jni/soundpool/Android.mk b/media/jni/soundpool/Android.mk
index 2bc41b5..509a59b 100644
--- a/media/jni/soundpool/Android.mk
+++ b/media/jni/soundpool/Android.mk
@@ -12,7 +12,7 @@
libutils \
libandroid_runtime \
libnativehelper \
- libmedia \
+ libaudioclient \
libmediandk \
libbinder
diff --git a/media/mca/filterfw/jni/jni_gl_environment.cpp b/media/mca/filterfw/jni/jni_gl_environment.cpp
index 096120e..823e88a 100644
--- a/media/mca/filterfw/jni/jni_gl_environment.cpp
+++ b/media/mca/filterfw/jni/jni_gl_environment.cpp
@@ -63,7 +63,8 @@
};
jboolean Java_android_filterfw_core_GLEnvironment_nativeAllocate(JNIEnv* env, jobject thiz) {
- return ToJBool(WrapObjectInJava(new GLEnv(), env, thiz, true));
+ std::unique_ptr<GLEnv> glEnv(new GLEnv());
+ return ToJBool(WrapOwnedObjectInJava(std::move(glEnv), env, thiz, true));
}
jboolean Java_android_filterfw_core_GLEnvironment_nativeDeallocate(JNIEnv* env, jobject thiz) {
diff --git a/media/mca/filterfw/jni/jni_gl_frame.cpp b/media/mca/filterfw/jni/jni_gl_frame.cpp
index b55bc5d..27b4cd2 100644
--- a/media/mca/filterfw/jni/jni_gl_frame.cpp
+++ b/media/mca/filterfw/jni/jni_gl_frame.cpp
@@ -48,13 +48,11 @@
jint height) {
GLEnv* gl_env_ptr = ConvertFromJava<GLEnv>(env, gl_env);
if (!gl_env_ptr) return JNI_FALSE;
- GLFrame* frame = new GLFrame(gl_env_ptr);
+ std::unique_ptr<GLFrame> frame(new GLFrame(gl_env_ptr));
if (frame->Init(width, height)) {
- return ToJBool(WrapObjectInJava(frame, env, thiz, true));
- } else {
- delete frame;
- return JNI_FALSE;
+ return ToJBool(WrapOwnedObjectInJava(std::move(frame), env, thiz, true));
}
+ return JNI_FALSE;
}
jboolean Java_android_filterfw_core_GLFrame_nativeAllocateWithTexture(JNIEnv* env,
@@ -65,13 +63,11 @@
jint height) {
GLEnv* gl_env_ptr = ConvertFromJava<GLEnv>(env, gl_env);
if (!gl_env_ptr) return JNI_FALSE;
- GLFrame* frame = new GLFrame(gl_env_ptr);
+ std::unique_ptr<GLFrame> frame(new GLFrame(gl_env_ptr));
if (frame->InitWithTexture(tex_id, width, height)) {
- return ToJBool(WrapObjectInJava(frame, env, thiz, true));
- } else {
- delete frame;
- return JNI_FALSE;
+ return ToJBool(WrapOwnedObjectInJava(std::move(frame), env, thiz, true));
}
+ return JNI_FALSE;
}
jboolean Java_android_filterfw_core_GLFrame_nativeAllocateWithFbo(JNIEnv* env,
@@ -82,13 +78,11 @@
jint height) {
GLEnv* gl_env_ptr = ConvertFromJava<GLEnv>(env, gl_env);
if (!gl_env_ptr) return JNI_FALSE;
- GLFrame* frame = new GLFrame(gl_env_ptr);
+ std::unique_ptr<GLFrame> frame(new GLFrame(gl_env_ptr));
if (frame->InitWithFbo(fbo_id, width, height)) {
- return ToJBool(WrapObjectInJava(frame, env, thiz, true));
- } else {
- delete frame;
- return JNI_FALSE;
+ return ToJBool(WrapOwnedObjectInJava(std::move(frame), env, thiz, true));
}
+ return JNI_FALSE;
}
jboolean Java_android_filterfw_core_GLFrame_nativeAllocateExternal(JNIEnv* env,
@@ -96,13 +90,11 @@
jobject gl_env) {
GLEnv* gl_env_ptr = ConvertFromJava<GLEnv>(env, gl_env);
if (!gl_env_ptr) return JNI_FALSE;
- GLFrame* frame = new GLFrame(gl_env_ptr);
+ std::unique_ptr<GLFrame> frame(new GLFrame(gl_env_ptr));
if (frame->InitWithExternalTexture()) {
- return ToJBool(WrapObjectInJava(frame, env, thiz, true));
- } else {
- delete frame;
- return JNI_FALSE;
+ return ToJBool(WrapOwnedObjectInJava(std::move(frame), env, thiz, true));
}
+ return JNI_FALSE;
}
jboolean Java_android_filterfw_core_GLFrame_nativeDeallocate(JNIEnv* env, jobject thiz) {
diff --git a/media/mca/filterfw/jni/jni_native_frame.cpp b/media/mca/filterfw/jni/jni_native_frame.cpp
index c8f2352..1a11a19 100644
--- a/media/mca/filterfw/jni/jni_native_frame.cpp
+++ b/media/mca/filterfw/jni/jni_native_frame.cpp
@@ -35,7 +35,8 @@
jboolean Java_android_filterfw_core_NativeFrame_nativeAllocate(JNIEnv* env,
jobject thiz,
jint size) {
- return ToJBool(WrapObjectInJava(new NativeFrame(size), env, thiz, true));
+ std::unique_ptr<NativeFrame> frame(new NativeFrame(size));
+ return ToJBool(WrapOwnedObjectInJava(std::move(frame), env, thiz, true));
}
jboolean Java_android_filterfw_core_NativeFrame_nativeDeallocate(JNIEnv* env, jobject thiz) {
diff --git a/media/mca/filterfw/jni/jni_native_program.cpp b/media/mca/filterfw/jni/jni_native_program.cpp
index b30b769..1424607 100644
--- a/media/mca/filterfw/jni/jni_native_program.cpp
+++ b/media/mca/filterfw/jni/jni_native_program.cpp
@@ -28,7 +28,8 @@
using android::filterfw::NativeProgram;
jboolean Java_android_filterfw_core_NativeProgram_allocate(JNIEnv* env, jobject thiz) {
- return ToJBool(WrapObjectInJava(new NativeProgram(), env, thiz, true));
+ std::unique_ptr<NativeProgram> program(new NativeProgram());
+ return ToJBool(WrapOwnedObjectInJava(std::move(program), env, thiz, true));
}
jboolean Java_android_filterfw_core_NativeProgram_deallocate(JNIEnv* env, jobject thiz) {
diff --git a/media/mca/filterfw/jni/jni_shader_program.cpp b/media/mca/filterfw/jni/jni_shader_program.cpp
index 19f43cd..98be04c 100644
--- a/media/mca/filterfw/jni/jni_shader_program.cpp
+++ b/media/mca/filterfw/jni/jni_shader_program.cpp
@@ -46,21 +46,14 @@
// Create the shader
if (!fragment_shader || !gl_env_ptr)
return false;
- else if (!vertex_shader)
- return ToJBool(WrapObjectInJava(new ShaderProgram(
- gl_env_ptr,
- ToCppString(env, fragment_shader)),
- env,
- thiz,
- true));
+
+ std::unique_ptr<ShaderProgram> shader;
+ if (!vertex_shader)
+ shader.reset(new ShaderProgram(gl_env_ptr, ToCppString(env, fragment_shader)));
else
- return ToJBool(WrapObjectInJava(new ShaderProgram(
- gl_env_ptr,
- ToCppString(env, vertex_shader),
- ToCppString(env, fragment_shader)),
- env,
- thiz,
- true));
+ shader.reset(new ShaderProgram(gl_env_ptr, ToCppString(env, vertex_shader),
+ ToCppString(env, fragment_shader)));
+ return ToJBool(WrapOwnedObjectInJava(std::move(shader), env, thiz, true));
}
jboolean Java_android_filterfw_core_ShaderProgram_deallocate(JNIEnv* env, jobject thiz) {
diff --git a/media/mca/filterfw/jni/jni_util.h b/media/mca/filterfw/jni/jni_util.h
index 11c0871..803ed29 100644
--- a/media/mca/filterfw/jni/jni_util.h
+++ b/media/mca/filterfw/jni/jni_util.h
@@ -16,6 +16,7 @@
#include <jni.h>
+#include <memory>
#include <unordered_map>
#include <string>
@@ -214,6 +215,17 @@
return pool ? pool->WrapObject(c_object, env, j_object, owns) : false;
}
+// Calls WrapObjectInJava, safely freeing c_object if object creation fails.
+template<typename T>
+bool WrapOwnedObjectInJava(std::unique_ptr<T> c_object, JNIEnv* env,
+ jobject j_object, bool owns) {
+ if (!WrapObjectInJava<T>(c_object.get(), env, j_object, owns))
+ return false;
+ // If we succeeded, a Java object now owns our c object; don't free it.
+ c_object.release();
+ return true;
+}
+
// Creates a new Java instance, which wraps the passed C++ instance. Returns
// the wrapped object or JNI_NULL if there was an error. Pass true to owns, if
// the Java layer should own the object.
diff --git a/media/mca/filterfw/jni/jni_vertex_frame.cpp b/media/mca/filterfw/jni/jni_vertex_frame.cpp
index caae938..d0439fe 100644
--- a/media/mca/filterfw/jni/jni_vertex_frame.cpp
+++ b/media/mca/filterfw/jni/jni_vertex_frame.cpp
@@ -24,7 +24,8 @@
jboolean Java_android_filterfw_core_VertexFrame_nativeAllocate(JNIEnv* env,
jobject thiz,
jint size) {
- return ToJBool(WrapObjectInJava(new VertexFrame(size), env, thiz, true));
+ std::unique_ptr<VertexFrame> frame(new VertexFrame(size));
+ return ToJBool(WrapOwnedObjectInJava(std::move(frame), env, thiz, true));
}
jboolean Java_android_filterfw_core_VertexFrame_nativeDeallocate(JNIEnv* env, jobject thiz) {
diff --git a/media/tests/audiotests/Android.mk b/media/tests/audiotests/Android.mk
index 57be372..01e42bd 100644
--- a/media/tests/audiotests/Android.mk
+++ b/media/tests/audiotests/Android.mk
@@ -15,7 +15,8 @@
libutils \
libbinder \
libhardware_legacy \
- libmedia
+ libmedia \
+ libaudioclient \
LOCAL_MODULE_TAGS := tests
diff --git a/native/graphics/jni/Android.mk b/native/graphics/jni/Android.mk
index 175f730..4c8a9db 100644
--- a/native/graphics/jni/Android.mk
+++ b/native/graphics/jni/Android.mk
@@ -20,7 +20,8 @@
LOCAL_SHARED_LIBRARIES := \
libandroid_runtime \
- libskia
+ libskia \
+ libandroidfw
LOCAL_C_INCLUDES += \
frameworks/base/native/include \
diff --git a/packages/Osu/src/com/android/hotspot2/WifiNetworkAdapter.java b/packages/Osu/src/com/android/hotspot2/WifiNetworkAdapter.java
index 518e64e..63b1903 100644
--- a/packages/Osu/src/com/android/hotspot2/WifiNetworkAdapter.java
+++ b/packages/Osu/src/com/android/hotspot2/WifiNetworkAdapter.java
@@ -134,8 +134,8 @@
public HomeSP addSP(MOTree instanceTree) throws IOException, SAXException {
WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
String xml = instanceTree.toXml();
- wifiManager.addPasspointManagementObject(xml);
- return MOManager.buildSP(xml);
+ // TODO(b/32883320): use the new API for adding Passpoint configuration.
+ return null;
}
public void removeSP(String fqdn) throws IOException {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index 9608daa..24ede16 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -116,6 +116,10 @@
List<BluetoothDevice> sinks = getConnectedDevices();
if (sinks != null) {
for (BluetoothDevice sink : sinks) {
+ if (sink.equals(device)) {
+ Log.w(TAG, "Connecting to device " + device + " : disconnect skipped");
+ continue;
+ }
mService.disconnect(sink);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 7f0e27a..1ea592d 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -22,6 +22,7 @@
import android.bluetooth.BluetoothHeadset;
import android.bluetooth.BluetoothHeadsetClient;
import android.bluetooth.BluetoothMap;
+import android.bluetooth.BluetoothMapClient;
import android.bluetooth.BluetoothInputDevice;
import android.bluetooth.BluetoothPan;
import android.bluetooth.BluetoothPbapClient;
@@ -81,12 +82,14 @@
private HeadsetProfile mHeadsetProfile;
private HfpClientProfile mHfpClientProfile;
private MapProfile mMapProfile;
+ private MapClientProfile mMapClientProfile;
private final HidProfile mHidProfile;
private OppProfile mOppProfile;
private final PanProfile mPanProfile;
private PbapClientProfile mPbapClientProfile;
private final PbapServerProfile mPbapProfile;
private final boolean mUsePbapPce;
+ private final boolean mUseMapClient;
/**
* Mapping from profile name, e.g. "HEADSET" to profile object.
@@ -104,6 +107,8 @@
mDeviceManager = deviceManager;
mEventManager = eventManager;
mUsePbapPce = mContext.getResources().getBoolean(R.bool.enable_pbap_pce_profile);
+ // MAP Client is typically used in the same situations as PBAP Client
+ mUseMapClient = mContext.getResources().getBoolean(R.bool.enable_pbap_pce_profile);
// pass this reference to adapter and event manager (circular dependency)
mLocalAdapter.setProfileManager(this);
mEventManager.setProfileManager(this);
@@ -125,10 +130,15 @@
BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
if(DEBUG) Log.d(TAG, "Adding local MAP profile");
- mMapProfile = new MapProfile(mContext, mLocalAdapter,
- mDeviceManager, this);
- addProfile(mMapProfile, MapProfile.NAME,
- BluetoothMap.ACTION_CONNECTION_STATE_CHANGED);
+ if (mUseMapClient) {
+ mMapClientProfile = new MapClientProfile(mContext, mLocalAdapter, mDeviceManager, this);
+ addProfile(mMapClientProfile, MapClientProfile.NAME,
+ BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED);
+ } else {
+ mMapProfile = new MapProfile(mContext, mLocalAdapter, mDeviceManager, this);
+ addProfile(mMapProfile, MapProfile.NAME,
+ BluetoothMap.ACTION_CONNECTION_STATE_CHANGED);
+ }
//Create PBAP server profile, but do not add it to list of profiles
// as we do not need to monitor the profile as part of profile list
@@ -199,6 +209,22 @@
Log.d(TAG, "Handsfree Uuid not found.");
}
+ // Message Access Profile Client
+ if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.MNS)) {
+ if (mMapClientProfile == null) {
+ if(DEBUG) Log.d(TAG, "Adding local Map Client profile");
+ mMapClientProfile =
+ new MapClientProfile(mContext, mLocalAdapter, mDeviceManager, this);
+ addProfile(mMapClientProfile, MapClientProfile.NAME,
+ BluetoothMapClient.ACTION_CONNECTION_STATE_CHANGED);
+ }
+ } else if (mMapClientProfile != null) {
+ Log.w(TAG,
+ "Warning: MAP Client profile was previously added but the UUID is now missing.");
+ } else {
+ Log.d(TAG, "MAP Client Uuid not found.");
+ }
+
// OPP
if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.ObexObjectPush)) {
if (mOppProfile == null) {
@@ -383,6 +409,10 @@
return mMapProfile;
}
+ public MapClientProfile getMapClientProfile() {
+ return mMapClientProfile;
+ }
+
/**
* Fill in a list of LocalBluetoothProfile objects that are supported by
* the local device and the remote device.
@@ -465,6 +495,11 @@
mMapProfile.setPreferred(device, true);
}
+ if (mMapClientProfile != null) {
+ profiles.add(mMapClientProfile);
+ removedProfiles.remove(mMapClientProfile);
+ }
+
if (mUsePbapPce) {
profiles.add(mPbapClientProfile);
removedProfiles.remove(mPbapClientProfile);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
new file mode 100644
index 0000000..a7621fc
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/MapClientProfile.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.bluetooth;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothClass;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothMapClient;
+import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothUuid;
+import android.content.Context;
+import android.os.ParcelUuid;
+import android.util.Log;
+
+import com.android.settingslib.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * MapClientProfile handles Bluetooth MAP profile.
+ */
+public final class MapClientProfile implements LocalBluetoothProfile {
+ private static final String TAG = "MapClientProfile";
+ private static boolean V = false;
+
+ private BluetoothMapClient mService;
+ private boolean mIsProfileReady;
+
+ private final LocalBluetoothAdapter mLocalAdapter;
+ private final CachedBluetoothDeviceManager mDeviceManager;
+ private final LocalBluetoothProfileManager mProfileManager;
+
+ static final ParcelUuid[] UUIDS = {
+ BluetoothUuid.MAP,
+ BluetoothUuid.MNS,
+ BluetoothUuid.MAS,
+ };
+
+ static final String NAME = "MAP Client";
+
+ // Order of this profile in device profiles list
+ private static final int ORDINAL = 0;
+
+ // These callbacks run on the main thread.
+ private final class MapClientServiceListener
+ implements BluetoothProfile.ServiceListener {
+
+ public void onServiceConnected(int profile, BluetoothProfile proxy) {
+ if (V) Log.d(TAG,"Bluetooth service connected");
+ mService = (BluetoothMapClient) proxy;
+ // We just bound to the service, so refresh the UI for any connected MAP devices.
+ List<BluetoothDevice> deviceList = mService.getConnectedDevices();
+ while (!deviceList.isEmpty()) {
+ BluetoothDevice nextDevice = deviceList.remove(0);
+ CachedBluetoothDevice device = mDeviceManager.findDevice(nextDevice);
+ // we may add a new device here, but generally this should not happen
+ if (device == null) {
+ Log.w(TAG, "MapProfile found new device: " + nextDevice);
+ device = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, nextDevice);
+ }
+ device.onProfileStateChanged(MapClientProfile.this,
+ BluetoothProfile.STATE_CONNECTED);
+ device.refresh();
+ }
+
+ mProfileManager.callServiceConnectedListeners();
+ mIsProfileReady=true;
+ }
+
+ public void onServiceDisconnected(int profile) {
+ if (V) Log.d(TAG,"Bluetooth service disconnected");
+ mProfileManager.callServiceDisconnectedListeners();
+ mIsProfileReady=false;
+ }
+ }
+
+ public boolean isProfileReady() {
+ if(V) Log.d(TAG,"isProfileReady(): "+ mIsProfileReady);
+ return mIsProfileReady;
+ }
+
+ MapClientProfile(Context context, LocalBluetoothAdapter adapter,
+ CachedBluetoothDeviceManager deviceManager,
+ LocalBluetoothProfileManager profileManager) {
+ mLocalAdapter = adapter;
+ mDeviceManager = deviceManager;
+ mProfileManager = profileManager;
+ mLocalAdapter.getProfileProxy(context, new MapClientServiceListener(),
+ BluetoothProfile.MAP_CLIENT);
+ }
+
+ public boolean isConnectable() {
+ return true;
+ }
+
+ public boolean isAutoConnectable() {
+ return true;
+ }
+
+ public boolean connect(BluetoothDevice device) {
+ if (mService == null) return false;
+ List<BluetoothDevice> connectedDevices = getConnectedDevices();
+ if (connectedDevices != null) {
+ for (BluetoothDevice connectedDevice : connectedDevices) {
+ mService.disconnect(connectedDevice);
+ }
+ }
+ return mService.connect(device);
+ }
+
+ public boolean disconnect(BluetoothDevice device) {
+ if (mService == null) return false;
+ // Downgrade priority as user is disconnecting.
+ if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) {
+ mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ }
+ return mService.disconnect(device);
+ }
+
+ public int getConnectionStatus(BluetoothDevice device) {
+ if (mService == null) return BluetoothProfile.STATE_DISCONNECTED;
+
+ return mService.getConnectionState(device);
+ }
+
+ public boolean isPreferred(BluetoothDevice device) {
+ if (mService == null) return false;
+ return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF;
+ }
+
+ public int getPreferred(BluetoothDevice device) {
+ if (mService == null) return BluetoothProfile.PRIORITY_OFF;
+ return mService.getPriority(device);
+ }
+
+ public void setPreferred(BluetoothDevice device, boolean preferred) {
+ if (mService == null) return;
+ if (preferred) {
+ if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) {
+ mService.setPriority(device, BluetoothProfile.PRIORITY_ON);
+ }
+ } else {
+ mService.setPriority(device, BluetoothProfile.PRIORITY_OFF);
+ }
+ }
+
+ public List<BluetoothDevice> getConnectedDevices() {
+ if (mService == null) return new ArrayList<BluetoothDevice>(0);
+ return mService.getDevicesMatchingConnectionStates(
+ new int[] {BluetoothProfile.STATE_CONNECTED,
+ BluetoothProfile.STATE_CONNECTING,
+ BluetoothProfile.STATE_DISCONNECTING});
+ }
+
+ public String toString() {
+ return NAME;
+ }
+
+ public int getOrdinal() {
+ return ORDINAL;
+ }
+
+ public int getNameResource(BluetoothDevice device) {
+ return R.string.bluetooth_profile_map;
+ }
+
+ public int getSummaryResourceForDevice(BluetoothDevice device) {
+ int state = getConnectionStatus(device);
+ switch (state) {
+ case BluetoothProfile.STATE_DISCONNECTED:
+ return R.string.bluetooth_map_profile_summary_use_for;
+
+ case BluetoothProfile.STATE_CONNECTED:
+ return R.string.bluetooth_map_profile_summary_connected;
+
+ default:
+ return Utils.getConnectionStateSummary(state);
+ }
+ }
+
+ public int getDrawableResource(BluetoothClass btClass) {
+ return R.drawable.ic_bt_cellphone;
+ }
+
+ protected void finalize() {
+ if (V) Log.d(TAG, "finalize()");
+ if (mService != null) {
+ try {
+ BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.MAP_CLIENT,
+ mService);
+ mService = null;
+ }catch (Throwable t) {
+ Log.w(TAG, "Error cleaning up MAP Client proxy", t);
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
index d7c9eab..72a3b3a 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapClientProfile.java
@@ -145,7 +145,7 @@
}
}
for (BluetoothDevice src : srcs) {
- mService.disconnect(device);
+ mService.disconnect(src);
}
}
Log.d(TAG,"PBAPClientProfile attempting to connect to " + device.getAddress());
diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java
index dd80750..005206f 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java
@@ -22,39 +22,93 @@
import android.content.Intent;
import android.content.IntentFilter;
-public abstract class CurrentUserTracker extends BroadcastReceiver {
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
- private Context mContext;
- private int mCurrentUserId;
+public abstract class CurrentUserTracker {
+ private final UserReceiver mUserReceiver;
+
+ private Consumer<Integer> mCallback = this::onUserSwitched;
public CurrentUserTracker(Context context) {
- mContext = context;
+ mUserReceiver = UserReceiver.getInstance(context);
}
public int getCurrentUserId() {
- return mCurrentUserId;
- }
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
- int oldUserId = mCurrentUserId;
- mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
- if (oldUserId != mCurrentUserId) {
- onUserSwitched(mCurrentUserId);
- }
- }
+ return mUserReceiver.getCurrentUserId();
}
public void startTracking() {
- mCurrentUserId = ActivityManager.getCurrentUser();
- IntentFilter filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
- mContext.registerReceiver(this, filter);
+ mUserReceiver.addTracker(mCallback);
}
public void stopTracking() {
- mContext.unregisterReceiver(this);
+ mUserReceiver.removeTracker(mCallback);
}
public abstract void onUserSwitched(int newUserId);
+
+ private static class UserReceiver extends BroadcastReceiver {
+ private static UserReceiver sInstance;
+
+ private Context mAppContext;
+ private boolean mReceiverRegistered;
+ private int mCurrentUserId;
+
+ private List<Consumer<Integer>> mCallbacks = new ArrayList<>();
+
+ private UserReceiver(Context context) {
+ mAppContext = context.getApplicationContext();
+ }
+
+ static UserReceiver getInstance(Context context) {
+ if (sInstance == null) {
+ sInstance = new UserReceiver(context);
+ }
+ return sInstance;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
+ notifyUserSwitched(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
+ }
+ }
+
+ public int getCurrentUserId() {
+ return mCurrentUserId;
+ }
+
+ private void addTracker(Consumer<Integer> callback) {
+ if (!mCallbacks.contains(callback)) {
+ mCallbacks.add(callback);
+ }
+ if (!mReceiverRegistered) {
+ mCurrentUserId = ActivityManager.getCurrentUser();
+ IntentFilter filter = new IntentFilter(Intent.ACTION_USER_SWITCHED);
+ mAppContext.registerReceiver(this, filter);
+ mReceiverRegistered = true;
+ }
+ }
+
+ private void removeTracker(Consumer<Integer> callback) {
+ if (mCallbacks.contains(callback)) {
+ mCallbacks.remove(callback);
+ if (mCallbacks.size() == 0 && mReceiverRegistered) {
+ mAppContext.unregisterReceiver(this);
+ mReceiverRegistered = false;
+ }
+ }
+ }
+
+ private void notifyUserSwitched(int newUserId) {
+ if (mCurrentUserId != newUserId) {
+ mCurrentUserId = newUserId;
+ for (Consumer<Integer> consumer : mCallbacks) {
+ consumer.accept(newUserId);
+ }
+ }
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index ebfa018..fa14346 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -159,6 +159,14 @@
mBrightnessMirror = findViewById(R.id.brightness_mirror);
}
+ @Override
+ public void onViewAdded(View child) {
+ super.onViewAdded(child);
+ if (child.getId() == R.id.brightness_mirror) {
+ mBrightnessMirror = child;
+ }
+ }
+
public void setService(PhoneStatusBar service) {
mService = service;
mDragDownHelper = new DragDownHelper(getContext(), this, mStackScrollLayout, mService);
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
index 70f2fdc..7afdbcb 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java
@@ -104,7 +104,9 @@
TunerService.showResetRequest(getContext(), new Runnable() {
@Override
public void run() {
- getActivity().finish();
+ if (getActivity() != null) {
+ getActivity().finish();
+ }
}
});
return true;
diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java
index 2a5b194..5b36598 100644
--- a/services/core/java/com/android/server/BluetoothManagerService.java
+++ b/services/core/java/com/android/server/BluetoothManagerService.java
@@ -58,6 +58,7 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.HashMap;
import java.util.Map;
@@ -117,7 +118,6 @@
private static final int SERVICE_IBLUETOOTHGATT = 2;
private final Context mContext;
- private static int mBleAppCount = 0;
// Locks are not provided for mName and mAddress.
// They are accessed in handler or broadcast receiver, same thread context.
@@ -211,10 +211,7 @@
if (isAirplaneModeOn()) {
// Clear registered LE apps to force shut-off
- synchronized (this) {
- mBleAppCount = 0;
- mBleApps.clear();
- }
+ clearBleApps();
if (st == BluetoothAdapter.STATE_BLE_ON) {
//if state is BLE_ON make sure you trigger disableBLE part
try {
@@ -438,28 +435,28 @@
class ClientDeathRecipient implements IBinder.DeathRecipient {
public void binderDied() {
if (DBG) Slog.d(TAG, "Binder is dead - unregister Ble App");
- if (mBleAppCount > 0) --mBleAppCount;
-
- if (mBleAppCount == 0) {
- if (DBG) Slog.d(TAG, "Disabling LE only mode after application crash");
- try {
- mBluetoothLock.readLock().lock();
- if (mBluetooth != null &&
- mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) {
- mEnable = false;
- mBluetooth.onBrEdrDown();
- }
- } catch (RemoteException e) {
- Slog.e(TAG,"Unable to call onBrEdrDown", e);
- } finally {
- mBluetoothLock.readLock().unlock();
+ if (isBleAppPresent()) {
+ // Nothing to do, another app is here.
+ return;
+ }
+ if (DBG) Slog.d(TAG, "Disabling LE only mode after application crash");
+ try {
+ mBluetoothLock.readLock().lock();
+ if (mBluetooth != null &&
+ mBluetooth.getState() == BluetoothAdapter.STATE_BLE_ON) {
+ mEnable = false;
+ mBluetooth.onBrEdrDown();
}
+ } catch (RemoteException e) {
+ Slog.e(TAG,"Unable to call onBrEdrDown", e);
+ } finally {
+ mBluetoothLock.readLock().unlock();
}
}
}
/** Internal death rec list */
- Map<IBinder, ClientDeathRecipient> mBleApps = new HashMap<IBinder, ClientDeathRecipient>();
+ Map<IBinder, ClientDeathRecipient> mBleApps = new ConcurrentHashMap<IBinder, ClientDeathRecipient>();
@Override
public boolean isBleScanAlwaysAvailable() {
@@ -479,17 +476,20 @@
ContentObserver contentObserver = new ContentObserver(null) {
@Override
public void onChange(boolean selfChange) {
- if (!isBleScanAlwaysAvailable()) {
- disableBleScanMode();
- clearBleApps();
- try {
- mBluetoothLock.readLock().lock();
- if (mBluetooth != null) mBluetooth.onBrEdrDown();
- } catch (RemoteException e) {
- Slog.e(TAG, "error when disabling bluetooth", e);
- } finally {
- mBluetoothLock.readLock().unlock();
- }
+ if (isBleScanAlwaysAvailable()) {
+ // Nothing to do
+ return;
+ }
+ // BLE scan is not available.
+ disableBleScanMode();
+ clearBleApps();
+ try {
+ mBluetoothLock.readLock().lock();
+ if (mBluetooth != null) mBluetooth.onBrEdrDown();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error when disabling bluetooth", e);
+ } finally {
+ mBluetoothLock.readLock().unlock();
}
}
};
@@ -525,9 +525,6 @@
throw new IllegalArgumentException("Wake lock is already dead.");
}
mBleApps.put(token, deathRec);
- synchronized (this) {
- ++mBleAppCount;
- }
if (DBG) Slog.d(TAG, "Registered for death Notification");
}
@@ -537,31 +534,26 @@
// Unregister death recipient as the app goes away.
token.unlinkToDeath(r, 0);
mBleApps.remove(token);
- synchronized (this) {
- if (mBleAppCount > 0) --mBleAppCount;
- }
if (DBG) Slog.d(TAG, "Unregistered for death Notification");
}
}
- if (DBG) Slog.d(TAG, "Updated BleAppCount" + mBleAppCount);
- if (mBleAppCount == 0 && mEnable) {
+ int appCount = mBleApps.size();
+ if (DBG) Slog.d(TAG, appCount + " registered Ble Apps");
+ if (appCount == 0 && mEnable) {
disableBleScanMode();
}
- return mBleAppCount;
+ return appCount;
}
// Clear all apps using BLE scan only mode.
private void clearBleApps() {
- synchronized (this) {
- mBleApps.clear();
- mBleAppCount = 0;
- }
+ mBleApps.clear();
}
/** @hide*/
public boolean isBleAppPresent() {
- if (DBG) Slog.d(TAG, "isBleAppPresent() count: " + mBleAppCount);
- return (mBleAppCount > 0);
+ if (DBG) Slog.d(TAG, "isBleAppPresent() count: " + mBleApps.size());
+ return mBleApps.size() > 0;
}
/**
@@ -1417,12 +1409,12 @@
if ((prevState == BluetoothAdapter.STATE_BLE_TURNING_ON) &&
(newState == BluetoothAdapter.STATE_OFF) &&
(mBluetooth != null) && mEnable) {
- recoverBluetoothServiceFromError();
+ recoverBluetoothServiceFromError(false);
}
if ((prevState == BluetoothAdapter.STATE_TURNING_ON) &&
(newState == BluetoothAdapter.STATE_BLE_ON) &&
(mBluetooth != null) && mEnable) {
- recoverBluetoothServiceFromError();
+ recoverBluetoothServiceFromError(true);
}
// If we tried to enable BT while BT was in the process of shutting down,
// wait for the BT process to fully tear down and then force a restart
@@ -1837,7 +1829,7 @@
quietMode ? 1 : 0, 0));
}
- private void recoverBluetoothServiceFromError() {
+ private void recoverBluetoothServiceFromError(boolean clearBle) {
Slog.e(TAG,"recoverBluetoothServiceFromError");
try {
mBluetoothLock.readLock().lock();
@@ -1875,6 +1867,10 @@
mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE);
mState = BluetoothAdapter.STATE_OFF;
+ if (clearBle) {
+ clearBleApps();
+ }
+
mEnable = false;
if (mErrorRecoveryRetryCounter++ < MAX_ERROR_RESTART_RETRIES) {
diff --git a/services/core/java/com/android/server/NetworkScoreService.java b/services/core/java/com/android/server/NetworkScoreService.java
index 83d6344..756cb30 100644
--- a/services/core/java/com/android/server/NetworkScoreService.java
+++ b/services/core/java/com/android/server/NetworkScoreService.java
@@ -41,6 +41,7 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
import java.io.FileDescriptor;
@@ -61,6 +62,7 @@
private static final boolean DBG = false;
private final Context mContext;
+ private final NetworkScorerAppManager mNetworkScorerAppManager;
private final Map<Integer, INetworkScoreCache> mScoreCaches;
/** Lock used to update mPackageMonitor when scorer package changes occur. */
private final Object mPackageMonitorLock = new Object[0];
@@ -131,7 +133,7 @@
+ ", forceUnbind=" + forceUnbind);
}
final NetworkScorerAppData activeScorer =
- NetworkScorerAppManager.getActiveScorer(mContext);
+ mNetworkScorerAppManager.getActiveScorer();
if (activeScorer == null) {
// Package change has invalidated a scorer, this will also unbind any service
// connection.
@@ -152,7 +154,13 @@
}
public NetworkScoreService(Context context) {
+ this(context, new NetworkScorerAppManager(context));
+ }
+
+ @VisibleForTesting
+ NetworkScoreService(Context context, NetworkScorerAppManager networkScoreAppManager) {
mContext = context;
+ mNetworkScorerAppManager = networkScoreAppManager;
mScoreCaches = new HashMap<>();
IntentFilter filter = new IntentFilter(Intent.ACTION_USER_UNLOCKED);
// TODO: Need to update when we support per-user scorers. http://b/23422763
@@ -171,7 +179,7 @@
String defaultPackage = mContext.getResources().getString(
R.string.config_defaultNetworkScorerPackageName);
if (!TextUtils.isEmpty(defaultPackage)) {
- NetworkScorerAppManager.setActiveScorer(mContext, defaultPackage);
+ mNetworkScorerAppManager.setActiveScorer(defaultPackage);
}
Settings.Global.putInt(cr, Settings.Global.NETWORK_SCORING_PROVISIONED, 1);
}
@@ -192,7 +200,7 @@
private void registerPackageMonitorIfNeeded() {
if (DBG) Log.d(TAG, "registerPackageMonitorIfNeeded");
- NetworkScorerAppData scorer = NetworkScorerAppManager.getActiveScorer(mContext);
+ NetworkScorerAppData scorer = mNetworkScorerAppManager.getActiveScorer();
synchronized (mPackageMonitorLock) {
// Unregister the current monitor if needed.
if (mPackageMonitor != null) {
@@ -220,7 +228,7 @@
private void bindToScoringServiceIfNeeded() {
if (DBG) Log.d(TAG, "bindToScoringServiceIfNeeded");
- NetworkScorerAppData scorerData = NetworkScorerAppManager.getActiveScorer(mContext);
+ NetworkScorerAppData scorerData = mNetworkScorerAppManager.getActiveScorer();
bindToScoringServiceIfNeeded(scorerData);
}
@@ -257,7 +265,7 @@
@Override
public boolean updateScores(ScoredNetwork[] networks) {
- if (!NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid())) {
+ if (!mNetworkScorerAppManager.isCallerActiveScorer(getCallingUid())) {
throw new SecurityException("Caller with UID " + getCallingUid() +
" is not the active scorer.");
}
@@ -296,7 +304,7 @@
public boolean clearScores() {
// Only the active scorer or the system (who can broadcast BROADCAST_NETWORK_PRIVILEGED)
// should be allowed to flush all scores.
- if (NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid()) ||
+ if (mNetworkScorerAppManager.isCallerActiveScorer(getCallingUid()) ||
mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED) ==
PackageManager.PERMISSION_GRANTED) {
clearInternal();
@@ -326,7 +334,7 @@
public void disableScoring() {
// Only the active scorer or the system (who can broadcast BROADCAST_NETWORK_PRIVILEGED)
// should be allowed to disable scoring.
- if (NetworkScorerAppManager.isCallerActiveScorer(mContext, getCallingUid()) ||
+ if (mNetworkScorerAppManager.isCallerActiveScorer(getCallingUid()) ||
mContext.checkCallingOrSelfPermission(permission.BROADCAST_NETWORK_PRIVILEGED) ==
PackageManager.PERMISSION_GRANTED) {
// The return value is discarded here because at this point, the call should always
@@ -350,8 +358,8 @@
// only be allowing valid apps to be set as scorers, so failure here should be rare.
clearInternal();
// Get the scorer that is about to be replaced, if any, so we can notify it directly.
- NetworkScorerAppData prevScorer = NetworkScorerAppManager.getActiveScorer(mContext);
- boolean result = NetworkScorerAppManager.setActiveScorer(mContext, packageName);
+ NetworkScorerAppData prevScorer = mNetworkScorerAppManager.getActiveScorer();
+ boolean result = mNetworkScorerAppManager.setActiveScorer(packageName);
// Unconditionally attempt to bind to the current scorer. If setActiveScorer() failed
// then we'll attempt to restore the previous binding (if any), otherwise an attempt
// will be made to bind to the new scorer.
@@ -409,7 +417,7 @@
@Override
protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
mContext.enforceCallingOrSelfPermission(permission.DUMP, TAG);
- NetworkScorerAppData currentScorer = NetworkScorerAppManager.getActiveScorer(mContext);
+ NetworkScorerAppData currentScorer = mNetworkScorerAppManager.getActiveScorer();
if (currentScorer == null) {
writer.println("Scoring is disabled.");
return;
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 2932a1a..62f4f19 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1381,7 +1381,6 @@
}
Intent intent = new Intent(TelephonyIntents.ACTION_SIGNAL_STRENGTH_CHANGED);
- intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
Bundle data = new Bundle();
signalStrength.fillInNotifierBundle(data);
intent.putExtras(data);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b59f468..d048af2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2891,9 +2891,11 @@
@Override
public void batterySendBroadcast(Intent intent) {
- broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
- AppOpsManager.OP_NONE, null, false, false,
- -1, Process.SYSTEM_UID, UserHandle.USER_ALL);
+ synchronized (this) {
+ broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,
+ AppOpsManager.OP_NONE, null, false, false,
+ -1, Process.SYSTEM_UID, UserHandle.USER_ALL);
+ }
}
/**
@@ -6351,13 +6353,18 @@
removeLruProcessLocked(app);
if (mBackupTarget != null && mBackupTarget.app.pid == pid) {
Slog.w(TAG, "Unattached app died before backup, skipping");
- try {
- IBackupManager bm = IBackupManager.Stub.asInterface(
- ServiceManager.getService(Context.BACKUP_SERVICE));
- bm.agentDisconnected(app.info.packageName);
- } catch (RemoteException e) {
- // Can't happen; the backup manager is local
- }
+ mHandler.post(new Runnable() {
+ @Override
+ public void run(){
+ try {
+ IBackupManager bm = IBackupManager.Stub.asInterface(
+ ServiceManager.getService(Context.BACKUP_SERVICE));
+ bm.agentDisconnected(app.info.packageName);
+ } catch (RemoteException e) {
+ // Can't happen; the backup manager is local
+ }
+ }
+ });
}
if (isPendingBroadcastProcessLocked(pid)) {
Slog.w(TAG, "Unattached app died before broadcast acknowledged, skipping");
@@ -16766,13 +16773,18 @@
if (mBackupTarget != null && app.pid == mBackupTarget.app.pid) {
if (DEBUG_BACKUP || DEBUG_CLEANUP) Slog.d(TAG_CLEANUP, "App "
+ mBackupTarget.appInfo + " died during backup");
- try {
- IBackupManager bm = IBackupManager.Stub.asInterface(
- ServiceManager.getService(Context.BACKUP_SERVICE));
- bm.agentDisconnected(app.info.packageName);
- } catch (RemoteException e) {
- // can't happen; backup manager is local
- }
+ mHandler.post(new Runnable() {
+ @Override
+ public void run(){
+ try {
+ IBackupManager bm = IBackupManager.Stub.asInterface(
+ ServiceManager.getService(Context.BACKUP_SERVICE));
+ bm.agentDisconnected(app.info.packageName);
+ } catch (RemoteException e) {
+ // can't happen; backup manager is local
+ }
+ }
+ });
}
for (int i = mPendingProcessChanges.size() - 1; i >= 0; i--) {
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index ba799f2c..96a804b 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -304,15 +304,19 @@
* @param crashInfo describing the failure
*/
void crashApplication(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) {
+ final int callingPid = Binder.getCallingPid();
+ final int callingUid = Binder.getCallingUid();
+
final long origId = Binder.clearCallingIdentity();
try {
- crashApplicationInner(r, crashInfo);
+ crashApplicationInner(r, crashInfo, callingPid, callingUid);
} finally {
Binder.restoreCallingIdentity(origId);
}
}
- void crashApplicationInner(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) {
+ void crashApplicationInner(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo,
+ int callingPid, int callingUid) {
long timeMillis = System.currentTimeMillis();
String shortMsg = crashInfo.exceptionClassName;
String longMsg = crashInfo.exceptionMessage;
@@ -331,7 +335,7 @@
* finish now and don't show the app error dialog.
*/
if (handleAppCrashInActivityController(r, crashInfo, shortMsg, longMsg, stackTrace,
- timeMillis)) {
+ timeMillis, callingPid, callingUid)) {
return;
}
@@ -433,15 +437,16 @@
private boolean handleAppCrashInActivityController(ProcessRecord r,
ApplicationErrorReport.CrashInfo crashInfo,
String shortMsg, String longMsg,
- String stackTrace, long timeMillis) {
+ String stackTrace, long timeMillis,
+ int callingPid, int callingUid) {
if (mService.mController == null) {
return false;
}
try {
String name = r != null ? r.processName : null;
- int pid = r != null ? r.pid : Binder.getCallingPid();
- int uid = r != null ? r.info.uid : Binder.getCallingUid();
+ int pid = r != null ? r.pid : callingPid;
+ int uid = r != null ? r.info.uid : callingUid;
if (!mService.mController.appCrashed(name, pid,
shortMsg, longMsg, timeMillis, crashInfo.stackTrace)) {
if ("1".equals(SystemProperties.get(SYSTEM_DEBUGGABLE, "0"))
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 7777ae23f..027c722 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -3097,14 +3097,28 @@
boolean success =
handleDeviceConnection(connected, outDevice, address, btDeviceName) &&
handleDeviceConnection(connected, inDevice, address, btDeviceName);
- if (success) {
- synchronized (mScoClients) {
- if (connected) {
- mBluetoothHeadsetDevice = btDevice;
- } else {
- mBluetoothHeadsetDevice = null;
- resetBluetoothSco();
- }
+
+ if (!success) {
+ return;
+ }
+
+ /* When one BT headset is disconnected while another BT headset
+ * is connected, don't mess with the headset device.
+ */
+ if ((state == BluetoothProfile.STATE_DISCONNECTED ||
+ state == BluetoothProfile.STATE_DISCONNECTING) &&
+ mBluetoothHeadset != null &&
+ mBluetoothHeadset.getAudioState(btDevice) == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
+ Log.w(TAG, "SCO connected through another device, returning");
+ return;
+ }
+
+ synchronized (mScoClients) {
+ if (connected) {
+ mBluetoothHeadsetDevice = btDevice;
+ } else {
+ mBluetoothHeadsetDevice = null;
+ resetBluetoothSco();
}
}
}
@@ -5250,7 +5264,6 @@
state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
BluetoothProfile.STATE_DISCONNECTED);
BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
-
setBtScoDeviceConnectionState(btDevice, state);
} else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
boolean broadcast = false;
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index e888faa..cec4141 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -900,7 +900,7 @@
}
} else {
mUsbTetherRequested = true;
- usbManager.setCurrentFunction(UsbManager.USB_FUNCTION_RNDIS);
+ usbManager.setCurrentFunction(UsbManager.USB_FUNCTION_RNDIS, false);
}
} else {
final long ident = Binder.clearCallingIdentity();
@@ -910,7 +910,7 @@
Binder.restoreCallingIdentity(ident);
}
if (mRndisEnabled) {
- usbManager.setCurrentFunction(null);
+ usbManager.setCurrentFunction(null, false);
}
mUsbTetherRequested = false;
}
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 1ef4a9f..acdcc72 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -219,19 +219,20 @@
final String dexoptType;
String oatDir = null;
- switch (dexoptNeeded) {
+ boolean isOdexLocation = (dexoptNeeded < 0);
+ switch (Math.abs(dexoptNeeded)) {
case DexFile.NO_DEXOPT_NEEDED:
continue;
- case DexFile.DEX2OAT_NEEDED:
+ case DexFile.DEX2OAT_FROM_SCRATCH:
+ case DexFile.DEX2OAT_FOR_BOOT_IMAGE:
+ case DexFile.DEX2OAT_FOR_FILTER:
+ case DexFile.DEX2OAT_FOR_RELOCATION:
dexoptType = "dex2oat";
oatDir = createOatDirIfSupported(pkg, dexCodeInstructionSet);
break;
- case DexFile.PATCHOAT_NEEDED:
+ case DexFile.PATCHOAT_FOR_RELOCATION:
dexoptType = "patchoat";
break;
- case DexFile.SELF_PATCHOAT_NEEDED:
- dexoptType = "self patchoat";
- break;
default:
throw new IllegalStateException("Invalid dexopt:" + dexoptNeeded);
}
@@ -383,7 +384,7 @@
protected int adjustDexoptNeeded(int dexoptNeeded) {
// Ensure compilation, no matter the current state.
// TODO: The return value is wrong when patchoat is needed.
- return DexFile.DEX2OAT_NEEDED;
+ return DexFile.DEX2OAT_FROM_SCRATCH;
}
}
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index dfd6dfe..99a6979 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -1410,7 +1410,6 @@
VersionInfo ver = mVersion.get(volumeUuid);
if (ver == null) {
ver = new VersionInfo();
- ver.forceCurrent();
mVersion.put(volumeUuid, ver);
}
return ver;
@@ -2795,8 +2794,8 @@
"No settings file; creating initial state");
// It's enough to just touch version details to create them
// with default values
- findOrCreateVersion(StorageManager.UUID_PRIVATE_INTERNAL);
- findOrCreateVersion(StorageManager.UUID_PRIMARY_PHYSICAL);
+ findOrCreateVersion(StorageManager.UUID_PRIVATE_INTERNAL).forceCurrent();
+ findOrCreateVersion(StorageManager.UUID_PRIMARY_PHYSICAL).forceCurrent();
return false;
}
str = new FileInputStream(mSettingsFilename);
diff --git a/services/core/java/com/android/server/webkit/SystemImpl.java b/services/core/java/com/android/server/webkit/SystemImpl.java
index 9f0f11a..302f9f6 100644
--- a/services/core/java/com/android/server/webkit/SystemImpl.java
+++ b/services/core/java/com/android/server/webkit/SystemImpl.java
@@ -26,6 +26,7 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
import android.content.res.XmlResourceParser;
+import android.database.ContentObserver;
import android.os.Build;
import android.os.RemoteException;
import android.os.UserHandle;
@@ -270,8 +271,21 @@
}
@Override
- public void setMultiprocessEnabled(boolean enabled) {
- WebViewZygote.setMultiprocessEnabled(enabled);
+ public void setMultiProcessEnabledFromContext(Context context) {
+ boolean enableMultiProcess = false;
+ try {
+ enableMultiProcess = Settings.Global.getInt(context.getContentResolver(),
+ Settings.Global.WEBVIEW_MULTIPROCESS) == 1;
+ } catch (Settings.SettingNotFoundException ex) {
+ }
+ WebViewZygote.setMultiprocessEnabled(enableMultiProcess);
+ }
+
+ @Override
+ public void registerContentObserver(Context context, ContentObserver contentObserver) {
+ context.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.WEBVIEW_MULTIPROCESS),
+ false, contentObserver);
}
// flags declaring we want extra info from the package manager for webview providers
diff --git a/services/core/java/com/android/server/webkit/SystemInterface.java b/services/core/java/com/android/server/webkit/SystemInterface.java
index 7c934fc..2d7a998 100644
--- a/services/core/java/com/android/server/webkit/SystemInterface.java
+++ b/services/core/java/com/android/server/webkit/SystemInterface.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.database.ContentObserver;
import android.webkit.WebViewProviderInfo;
/**
@@ -49,5 +50,6 @@
public PackageInfo getPackageInfoForProvider(WebViewProviderInfo configInfo)
throws NameNotFoundException;
- public void setMultiprocessEnabled(boolean enabled);
+ public void setMultiProcessEnabledFromContext(Context context);
+ public void registerContentObserver(Context context, ContentObserver contentObserver);
}
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
index 863408c..791fbbc 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
@@ -20,12 +20,10 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
-import android.content.ContentResolver;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.os.UserHandle;
-import android.provider.Settings;
import android.util.Base64;
import android.util.Slog;
import android.webkit.WebViewFactory;
@@ -48,7 +46,7 @@
private SystemInterface mSystemInterface;
private WebViewUpdater mWebViewUpdater;
private SettingsObserver mSettingsObserver;
- private Context mContext;
+ final private Context mContext;
public WebViewUpdateServiceImpl(Context context, SystemInterface systemInterface) {
mContext = context;
@@ -683,15 +681,10 @@
* appropriately.
*/
private class SettingsObserver extends ContentObserver {
- private final ContentResolver mResolver;
-
SettingsObserver() {
super(new Handler());
- mResolver = mContext.getContentResolver();
- mResolver.registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.WEBVIEW_MULTIPROCESS),
- false, this);
+ mSystemInterface.registerContentObserver(mContext, this);
// Push the current value of the setting immediately.
notifyZygote();
@@ -703,15 +696,7 @@
}
private void notifyZygote() {
- boolean enableMultiprocess = false;
-
- try {
- enableMultiprocess = Settings.Global.getInt(mResolver,
- Settings.Global.WEBVIEW_MULTIPROCESS) == 1;
- } catch (Settings.SettingNotFoundException ex) {
- }
-
- mSystemInterface.setMultiprocessEnabled(enableMultiprocess);
+ mSystemInterface.setMultiProcessEnabledFromContext(mContext);
}
}
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 58b71f5..18e0f3f 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -138,8 +138,8 @@
"com.android.server.midi.MidiService$Lifecycle";
private static final String WIFI_SERVICE_CLASS =
"com.android.server.wifi.WifiService";
- private static final String WIFI_NAN_SERVICE_CLASS =
- "com.android.server.wifi.nan.WifiNanService";
+ private static final String WIFI_AWARE_SERVICE_CLASS =
+ "com.android.server.wifi.aware.WifiAwareService";
private static final String WIFI_P2P_SERVICE_CLASS =
"com.android.server.wifi.p2p.WifiP2pService";
private static final String ETHERNET_SERVICE_CLASS =
@@ -820,10 +820,11 @@
}
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
- if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_NAN)) {
- mSystemServiceManager.startService(WIFI_NAN_SERVICE_CLASS);
+ if (context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_WIFI_AWARE)) {
+ mSystemServiceManager.startService(WIFI_AWARE_SERVICE_CLASS);
} else {
- Slog.i(TAG, "No Wi-Fi NAN Service (NAN support Not Present)");
+ Slog.i(TAG, "No Wi-Fi Aware Service (Aware support Not Present)");
}
mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS);
mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
diff --git a/services/tests/runtests.py b/services/tests/runtests.py
index 35fec90f..7980dc2 100755
--- a/services/tests/runtests.py
+++ b/services/tests/runtests.py
@@ -61,8 +61,8 @@
print 'Running tests...'
if len(sys.argv) != 1:
- run('adb shell am instrument -w "%s" %s' %
- (INSTRUMENTED_PACKAGE_RUNNER, ' '.join(sys.argv[1:])))
+ run('adb shell am instrument -w %s "%s"' %
+ (' '.join(sys.argv[1:]), INSTRUMENTED_PACKAGE_RUNNER))
return 0
# It would be nice if the activity manager accepted a list of packages, but
diff --git a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
index 3a2a2de..afa5bac 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.database.ContentObserver;
import android.webkit.WebViewProviderInfo;
import java.util.HashMap;
@@ -115,5 +116,8 @@
}
@Override
- public void setMultiprocessEnabled(boolean enabled) {}
+ public void setMultiProcessEnabledFromContext(Context context) {}
+
+ @Override
+ public void registerContentObserver(Context context, ContentObserver contentObserver) {}
}
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 b737033..82c7bdb 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/WebViewUpdateServiceTest.java
@@ -16,31 +16,42 @@
package com.android.server.webkit;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.Signature;
import android.os.Bundle;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.suitebuilder.annotation.MediumTest;
import android.util.Base64;
-import android.test.AndroidTestCase;
-
import android.webkit.WebViewFactory;
import android.webkit.WebViewProviderInfo;
import android.webkit.WebViewProviderResponse;
-import java.util.concurrent.CountDownLatch;
-
import org.hamcrest.Description;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import org.mockito.Mockito;
import org.mockito.Matchers;
import org.mockito.ArgumentMatcher;
+import java.util.concurrent.CountDownLatch;
+
/**
* Tests for WebViewUpdateService
*/
-public class WebViewUpdateServiceTest extends AndroidTestCase {
+// Use MediumTest instead of SmallTest as the implementation of WebViewUpdateService
+// is intended to work on several threads and uses at least one sleep/wait-statement.
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class WebViewUpdateServiceTest {
private final static String TAG = WebViewUpdateServiceTest.class.getSimpleName();
private WebViewUpdateServiceImpl mWebViewUpdateServiceImpl;
@@ -48,11 +59,6 @@
private static final String WEBVIEW_LIBRARY_FLAG = "com.android.webview.WebViewLibrary";
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- }
-
/**
* Creates a new instance.
*/
@@ -102,7 +108,7 @@
// Add (enabled and valid) package infos for each provider
setEnabledAndValidPackageInfos(webviewPackages);
- mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+ runWebViewBootPreparationOnMainSync();
Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
Mockito.argThat(new IsPackageInfoWithName(expectedProviderName)));
@@ -188,12 +194,27 @@
assertEquals(expectedPackage, response.packageInfo.packageName);
}
+ /**
+ * The WebView preparation boot phase is run on the main thread (especially on a thread with a
+ * looper) so to avoid bugs where our tests fail because a looper hasn't been attached to the
+ * thread running prepareWebViewInSystemServer we run it on the main thread.
+ */
+ private void runWebViewBootPreparationOnMainSync() {
+ InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+ }
+ });
+ }
+
// ****************
// Tests
// ****************
+ @Test
public void testWithSinglePackage() {
String testPackageName = "test.package.name";
checkCertainPackageUsedAfterWebViewBootPreparation(testPackageName,
@@ -202,6 +223,7 @@
true /*default available*/, false /* fallback */, null)});
}
+ @Test
public void testDefaultPackageUsedOverNonDefault() {
String defaultPackage = "defaultPackage";
String nonDefaultPackage = "nonDefaultPackage";
@@ -211,6 +233,7 @@
checkCertainPackageUsedAfterWebViewBootPreparation(defaultPackage, packages);
}
+ @Test
public void testSeveralRelros() {
String singlePackage = "singlePackage";
checkCertainPackageUsedAfterWebViewBootPreparation(
@@ -222,6 +245,7 @@
// Ensure that package with valid signatures is chosen rather than package with invalid
// signatures.
+ @Test
public void testWithSignatures() {
String validPackage = "valid package";
String invalidPackage = "invalid package";
@@ -247,7 +271,7 @@
true /* valid */, true /* installed */, new Signature[]{validSignature}
, 0 /* updateTime */));
- mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+ runWebViewBootPreparationOnMainSync();
checkPreparationPhasesForPackage(validPackage, 1 /* first preparation for this package */);
@@ -257,13 +281,14 @@
assertEquals(validPackage, validPackages[0].packageName);
}
+ @Test
public void testFailWaitingForRelro() {
WebViewProviderInfo[] packages = new WebViewProviderInfo[] {
new WebViewProviderInfo("packagename", "", true, true, null)};
setupWithPackages(packages);
setEnabledAndValidPackageInfos(packages);
- mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+ runWebViewBootPreparationOnMainSync();
Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
Mockito.argThat(new IsPackageInfoWithName(packages[0].packageName)));
@@ -274,12 +299,13 @@
assertEquals(WebViewFactory.LIBLOAD_FAILED_WAITING_FOR_RELRO, response.status);
}
+ @Test
public void testFailListingEmptyWebviewPackages() {
WebViewProviderInfo[] packages = new WebViewProviderInfo[0];
setupWithPackages(packages);
setEnabledAndValidPackageInfos(packages);
- mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+ runWebViewBootPreparationOnMainSync();
Mockito.verify(mTestSystemImpl, Mockito.never()).onWebViewProviderChanged(
Matchers.anyObject());
@@ -288,6 +314,7 @@
assertEquals(WebViewFactory.LIBLOAD_FAILED_LISTING_WEBVIEW_PACKAGES, response.status);
}
+ @Test
public void testFailListingInvalidWebviewPackage() {
WebViewProviderInfo wpi = new WebViewProviderInfo("package", "", true, true, null);
WebViewProviderInfo[] packages = new WebViewProviderInfo[] {wpi};
@@ -296,7 +323,7 @@
createPackageInfo(wpi.packageName, true /* enabled */, false /* valid */,
true /* installed */));
- mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+ runWebViewBootPreparationOnMainSync();
Mockito.verify(mTestSystemImpl, Mockito.never()).onWebViewProviderChanged(
Matchers.anyObject());
@@ -315,6 +342,7 @@
}
// Test that switching provider using changeProviderAndSetting works.
+ @Test
public void testSwitchingProvider() {
String firstPackage = "first";
String secondPackage = "second";
@@ -324,6 +352,7 @@
checkSwitchingProvider(packages, firstPackage, secondPackage);
}
+ @Test
public void testSwitchingProviderToNonDefault() {
String defaultPackage = "defaultPackage";
String nonDefaultPackage = "nonDefaultPackage";
@@ -344,11 +373,13 @@
}
// Change provider during relro creation by using changeProviderAndSetting
+ @Test
public void testSwitchingProviderDuringRelroCreation() {
checkChangingProviderDuringRelroCreation(true);
}
// Change provider during relro creation by enabling a provider
+ @Test
public void testChangingProviderThroughEnablingDuringRelroCreation() {
checkChangingProviderDuringRelroCreation(false);
}
@@ -373,7 +404,7 @@
CountDownLatch countdown = new CountDownLatch(1);
- mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+ runWebViewBootPreparationOnMainSync();
Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
Mockito.argThat(new IsPackageInfoWithName(firstPackage)));
@@ -420,10 +451,12 @@
}
}
+ @Test
public void testRunFallbackLogicIfEnabled() {
checkFallbackLogicBeingRun(true);
}
+ @Test
public void testDontRunFallbackLogicIfDisabled() {
checkFallbackLogicBeingRun(false);
}
@@ -439,7 +472,7 @@
setupWithPackages(packages, fallbackLogicEnabled);
setEnabledAndValidPackageInfos(packages);
- mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+ runWebViewBootPreparationOnMainSync();
// Verify that we disable the fallback package if fallback logic enabled, and don't disable
// the fallback package if that logic is disabled
if (fallbackLogicEnabled) {
@@ -475,6 +508,7 @@
* 2. Install non-fallback
* 3. Fallback should be disabled
*/
+ @Test
public void testInstallingNonFallbackPackage() {
String primaryPackage = "primary";
String fallbackPackage = "fallback";
@@ -488,7 +522,7 @@
createPackageInfo(fallbackPackage, true /* enabled */ , true /* valid */,
true /* installed */));
- mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+ runWebViewBootPreparationOnMainSync();
Mockito.verify(mTestSystemImpl, Mockito.never()).uninstallAndDisablePackageForAllUsers(
Matchers.anyObject(), Matchers.anyObject());
@@ -509,6 +543,7 @@
Mockito.verify(mTestSystemImpl).killPackageDependents(Mockito.eq(fallbackPackage));
}
+ @Test
public void testFallbackChangesEnabledState() {
String primaryPackage = "primary";
String fallbackPackage = "fallback";
@@ -520,7 +555,7 @@
setupWithPackages(packages, true /* fallbackLogicEnabled */);
setEnabledAndValidPackageInfos(packages);
- mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+ runWebViewBootPreparationOnMainSync();
// Verify fallback disabled at boot when primary package enabled
Mockito.verify(mTestSystemImpl).enablePackageForUser(
@@ -558,10 +593,12 @@
checkPreparationPhasesForPackage(primaryPackage, 2);
}
+ @Test
public void testAddUserWhenFallbackLogicEnabled() {
checkAddingNewUser(true);
}
+ @Test
public void testAddUserWhenFallbackLogicDisabled() {
checkAddingNewUser(false);
}
@@ -595,6 +632,7 @@
* Timing dependent test where we verify that the list of valid webview packages becoming empty
* at a certain point doesn't crash us or break our state.
*/
+ @Test
public void testNotifyRelroDoesntCrashIfNoPackages() {
String firstPackage = "first";
String secondPackage = "second";
@@ -607,7 +645,7 @@
// Add (enabled and valid) package infos for each provider
setEnabledAndValidPackageInfos(packages);
- mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+ runWebViewBootPreparationOnMainSync();
Mockito.verify(mTestSystemImpl).onWebViewProviderChanged(
Mockito.argThat(new IsPackageInfoWithName(firstPackage)));
@@ -646,6 +684,7 @@
* Verify that even if a user-chosen package is removed temporarily we start using it again when
* it is added back.
*/
+ @Test
public void testTempRemovePackageDoesntSwitchProviderPermanently() {
String firstPackage = "first";
String secondPackage = "second";
@@ -679,6 +718,7 @@
* Ensure that we update the user-chosen setting across boots if the chosen package is no
* longer installed and valid.
*/
+ @Test
public void testProviderSettingChangedDuringBootIfProviderNotAvailable() {
String chosenPackage = "chosenPackage";
String nonChosenPackage = "non-chosenPackage";
@@ -696,7 +736,7 @@
// Set user-chosen package
mTestSystemImpl.updateUserSetting(null, chosenPackage);
- mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+ runWebViewBootPreparationOnMainSync();
// Verify that we switch the setting to point to the current package
Mockito.verify(mTestSystemImpl).updateUserSetting(
@@ -706,10 +746,12 @@
checkPreparationPhasesForPackage(nonChosenPackage, 1);
}
+ @Test
public void testRecoverFailedListingWebViewPackagesSettingsChange() {
checkRecoverAfterFailListingWebviewPackages(true);
}
+ @Test
public void testRecoverFailedListingWebViewPackagesAddedPackage() {
checkRecoverAfterFailListingWebviewPackages(false);
}
@@ -756,10 +798,12 @@
checkPreparationPhasesForPackage(secondPackage, 1);
}
+ @Test
public void testDontKillIfPackageReplaced() {
checkDontKillIfPackageRemoved(true);
}
+ @Test
public void testDontKillIfPackageRemoved() {
checkDontKillIfPackageRemoved(false);
}
@@ -793,6 +837,7 @@
Mockito.anyObject());
}
+ @Test
public void testKillIfSettingChanged() {
String firstPackage = "first";
String secondPackage = "second";
@@ -814,6 +859,7 @@
* Test that we kill apps using an old provider when we change the provider setting, even if the
* new provider is not the one we intended to change to.
*/
+ @Test
public void testKillIfChangeProviderIncorrectly() {
String firstPackage = "first";
String secondPackage = "second";
@@ -831,7 +877,7 @@
// Start with the setting pointing to the third package
mTestSystemImpl.updateUserSetting(null, thirdPackage);
- mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+ runWebViewBootPreparationOnMainSync();
checkPreparationPhasesForPackage(thirdPackage, 1);
mTestSystemImpl.setPackageInfo(
@@ -849,6 +895,7 @@
// Ensure that the update service uses an uninstalled package if that is the only package
// available.
+ @Test
public void testWithSingleUninstalledPackage() {
String testPackageName = "test.package.name";
WebViewProviderInfo[] webviewPackages = new WebViewProviderInfo[] {
@@ -858,15 +905,17 @@
mTestSystemImpl.setPackageInfo(createPackageInfo(testPackageName, true /* enabled */,
true /* valid */, false /* installed */));
- mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+ runWebViewBootPreparationOnMainSync();
checkPreparationPhasesForPackage(testPackageName, 1 /* first preparation phase */);
}
+ @Test
public void testNonhiddenPackageUserOverHidden() {
checkVisiblePackageUserOverNonVisible(false /* true == uninstalled, false == hidden */);
}
+ @Test
public void testInstalledPackageUsedOverUninstalled() {
checkVisiblePackageUserOverNonVisible(true /* true == uninstalled, false == hidden */);
}
@@ -889,16 +938,18 @@
true /* valid */, (testUninstalled ? false : true) /* installed */,
null /* signatures */, 0 /* updateTime */, (testHidden ? true : false)));
- mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+ runWebViewBootPreparationOnMainSync();
checkPreparationPhasesForPackage(installedPackage, 1 /* first preparation phase */);
}
+ @Test
public void testCantSwitchToHiddenPackage () {
checkCantSwitchToNonVisiblePackage(false /* true == uninstalled, false == hidden */);
}
+ @Test
public void testCantSwitchToUninstalledPackage () {
checkCantSwitchToNonVisiblePackage(true /* true == uninstalled, false == hidden */);
}
@@ -927,7 +978,7 @@
null /* signatures */, 0 /* updateTime */,
(testHidden ? true : false) /* hidden */));
- mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+ runWebViewBootPreparationOnMainSync();
checkPreparationPhasesForPackage(installedPackage, 1 /* first preparation phase */);
@@ -947,11 +998,13 @@
Mockito.argThat(new IsPackageInfoWithName(installedPackage)));
}
+ @Test
public void testHiddenPackageNotPrioritizedEvenIfChosen() {
checkNonvisiblePackageNotPrioritizedEvenIfChosen(
false /* true == uninstalled, false == hidden */);
}
+ @Test
public void testUninstalledPackageNotPrioritizedEvenIfChosen() {
checkNonvisiblePackageNotPrioritizedEvenIfChosen(
true /* true == uninstalled, false == hidden */);
@@ -979,7 +1032,7 @@
// Start with the setting pointing to the uninstalled package
mTestSystemImpl.updateUserSetting(null, uninstalledPackage);
- mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+ runWebViewBootPreparationOnMainSync();
checkPreparationPhasesForPackage(installedPackage, 1 /* first preparation phase */);
}
@@ -988,6 +1041,7 @@
* Ensures that fallback becomes enabled if the primary package is uninstalled for the current
* user.
*/
+ @Test
public void testFallbackEnabledIfPrimaryUninstalled() {
String primaryPackage = "primary";
String fallbackPackage = "fallback";
@@ -1002,7 +1056,7 @@
mTestSystemImpl.setPackageInfo(createPackageInfo(fallbackPackage, true /* enabled */,
true /* valid */, true /* installed */));
- mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+ runWebViewBootPreparationOnMainSync();
// Verify that we enable the fallback package
Mockito.verify(mTestSystemImpl).enablePackageForAllUsers(
Mockito.anyObject(), Mockito.eq(fallbackPackage), Mockito.eq(true) /* enable */);
@@ -1010,6 +1064,7 @@
checkPreparationPhasesForPackage(fallbackPackage, 1 /* first preparation phase */);
}
+ @Test
public void testPreparationRunsIffNewPackage() {
String primaryPackage = "primary";
String fallbackPackage = "fallback";
@@ -1025,7 +1080,7 @@
mTestSystemImpl.setPackageInfo(createPackageInfo(fallbackPackage, true /* enabled */,
true /* valid */, true /* installed */));
- mWebViewUpdateServiceImpl.prepareWebViewInSystemServer();
+ runWebViewBootPreparationOnMainSync();
checkPreparationPhasesForPackage(primaryPackage, 1 /* first preparation phase */);
Mockito.verify(mTestSystemImpl, Mockito.times(1)).enablePackageForUser(
@@ -1070,5 +1125,4 @@
checkPreparationPhasesForPackage(primaryPackage, 3 /* third preparation phase */);
}
-
}
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index df9242d..43b8fa5 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -108,9 +108,8 @@
private static final int MSG_SYSTEM_READY = 3;
private static final int MSG_BOOT_COMPLETED = 4;
private static final int MSG_USER_SWITCHED = 5;
- private static final int MSG_SET_USB_DATA_UNLOCKED = 6;
- private static final int MSG_UPDATE_USER_RESTRICTIONS = 7;
- private static final int MSG_UPDATE_HOST_STATE = 8;
+ private static final int MSG_UPDATE_USER_RESTRICTIONS = 6;
+ private static final int MSG_UPDATE_HOST_STATE = 7;
private static final int AUDIO_MODE_SOURCE = 1;
@@ -287,7 +286,7 @@
if (functions != null) {
mAccessoryModeRequestTime = SystemClock.elapsedRealtime();
- setCurrentFunctions(functions);
+ setCurrentFunctions(functions, false);
}
}
@@ -335,14 +334,22 @@
// Restore default functions.
mCurrentFunctions = SystemProperties.get(USB_CONFIG_PROPERTY,
UsbManager.USB_FUNCTION_NONE);
- if (UsbManager.USB_FUNCTION_NONE.equals(mCurrentFunctions)) {
- mCurrentFunctions = UsbManager.USB_FUNCTION_MTP;
- }
mCurrentFunctionsApplied = mCurrentFunctions.equals(
SystemProperties.get(USB_STATE_PROPERTY));
mAdbEnabled = UsbManager.containsFunction(getDefaultFunctions(),
UsbManager.USB_FUNCTION_ADB);
- setEnabledFunctions(null, false);
+
+ /**
+ * Remove MTP from persistent config, to bring usb to a good state
+ * after fixes to b/31814300. This block can be removed after the update
+ */
+ String persisted = SystemProperties.get(USB_PERSISTENT_CONFIG_PROPERTY);
+ if (UsbManager.containsFunction(persisted, UsbManager.USB_FUNCTION_MTP)) {
+ SystemProperties.set(USB_PERSISTENT_CONFIG_PROPERTY,
+ UsbManager.removeFunction(persisted, UsbManager.USB_FUNCTION_MTP));
+ }
+
+ setEnabledFunctions(null, false, false);
String state = FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim();
updateState(state);
@@ -374,6 +381,14 @@
sendMessage(m);
}
+ public void sendMessage(int what, Object arg, boolean arg1) {
+ removeMessages(what);
+ Message m = Message.obtain(this, what);
+ m.obj = arg;
+ m.arg1 = (arg1 ? 1 : 0);
+ sendMessage(m);
+ }
+
public void updateState(String state) {
int connected, configured;
@@ -427,29 +442,24 @@
return waitForState(config);
}
- private void setUsbDataUnlocked(boolean enable) {
- if (DEBUG) Slog.d(TAG, "setUsbDataUnlocked: " + enable);
- mUsbDataUnlocked = enable;
- updateUsbNotification();
- updateUsbStateBroadcastIfNeeded();
- setEnabledFunctions(mCurrentFunctions, true);
- }
-
private void setAdbEnabled(boolean enable) {
if (DEBUG) Slog.d(TAG, "setAdbEnabled: " + enable);
if (enable != mAdbEnabled) {
mAdbEnabled = enable;
+ String oldFunctions = mCurrentFunctions;
- // Due to the persist.sys.usb.config property trigger, changing adb state requires
- // persisting default function
- String oldFunctions = getDefaultFunctions();
- String newFunctions = applyAdbFunction(oldFunctions);
- if (!oldFunctions.equals(newFunctions)) {
- SystemProperties.set(USB_PERSISTENT_CONFIG_PROPERTY, newFunctions);
+ // Persist the adb setting
+ String newFunction = applyAdbFunction(SystemProperties.get(
+ USB_PERSISTENT_CONFIG_PROPERTY, UsbManager.USB_FUNCTION_NONE));
+ SystemProperties.set(USB_PERSISTENT_CONFIG_PROPERTY, newFunction);
+
+ // Remove mtp from the config if file transfer is not enabled
+ if (oldFunctions.equals(UsbManager.USB_FUNCTION_MTP) &&
+ !mUsbDataUnlocked && enable) {
+ oldFunctions = UsbManager.USB_FUNCTION_NONE;
}
- // After persisting them use the lock-down aware function set
- setEnabledFunctions(mCurrentFunctions, false);
+ setEnabledFunctions(oldFunctions, true, mUsbDataUnlocked);
updateAdbNotification();
}
@@ -461,10 +471,17 @@
/**
* Evaluates USB function policies and applies the change accordingly.
*/
- private void setEnabledFunctions(String functions, boolean forceRestart) {
+ private void setEnabledFunctions(String functions, boolean forceRestart,
+ boolean usbDataUnlocked) {
if (DEBUG) Slog.d(TAG, "setEnabledFunctions functions=" + functions + ", "
+ "forceRestart=" + forceRestart);
+ if (usbDataUnlocked != mUsbDataUnlocked) {
+ mUsbDataUnlocked = usbDataUnlocked;
+ updateUsbNotification();
+ forceRestart = true;
+ }
+
// Try to set the enabled functions.
final String oldFunctions = mCurrentFunctions;
final boolean oldFunctionsApplied = mCurrentFunctionsApplied;
@@ -501,7 +518,8 @@
}
private boolean trySetEnabledFunctions(String functions, boolean forceRestart) {
- if (functions == null) {
+ if (functions == null || applyAdbFunction(functions)
+ .equals(UsbManager.USB_FUNCTION_NONE)) {
functions = getDefaultFunctions();
}
functions = applyAdbFunction(functions);
@@ -566,7 +584,7 @@
// make sure accessory mode is off
// and restore default functions
Slog.d(TAG, "exited USB accessory mode");
- setEnabledFunctions(null, false);
+ setEnabledFunctions(null, false, false);
if (mCurrentAccessory != null) {
if (mBootCompleted) {
@@ -583,10 +601,6 @@
if (mBroadcastedIntent == null) {
for (String key : keySet) {
if (intent.getBooleanExtra(key, false)) {
- // MTP function is enabled by default.
- if (UsbManager.USB_FUNCTION_MTP.equals(key)) {
- continue;
- }
return true;
}
}
@@ -699,10 +713,7 @@
case MSG_UPDATE_STATE:
mConnected = (msg.arg1 == 1);
mConfigured = (msg.arg2 == 1);
- if (!mConnected) {
- // When a disconnect occurs, relock access to sensitive user data
- mUsbDataUnlocked = false;
- }
+
updateUsbNotification();
updateAdbNotification();
if (UsbManager.containsFunction(mCurrentFunctions,
@@ -710,7 +721,7 @@
updateCurrentAccessory();
} else if (!mConnected) {
// restore defaults when USB is disconnected
- setEnabledFunctions(null, false);
+ setEnabledFunctions(null, false, false);
}
if (mBootCompleted) {
updateUsbStateBroadcastIfNeeded();
@@ -730,13 +741,10 @@
break;
case MSG_SET_CURRENT_FUNCTIONS:
String functions = (String)msg.obj;
- setEnabledFunctions(functions, false);
+ setEnabledFunctions(functions, false, msg.arg1 == 1);
break;
case MSG_UPDATE_USER_RESTRICTIONS:
- setEnabledFunctions(mCurrentFunctions, false);
- break;
- case MSG_SET_USB_DATA_UNLOCKED:
- setUsbDataUnlocked(msg.arg1 == 1);
+ setEnabledFunctions(mCurrentFunctions, false, mUsbDataUnlocked);
break;
case MSG_SYSTEM_READY:
updateUsbNotification();
@@ -764,8 +772,7 @@
Slog.v(TAG, "Current user switched to " + mCurrentUser
+ "; resetting USB host stack for MTP or PTP");
// avoid leaking sensitive data from previous user
- mUsbDataUnlocked = false;
- setEnabledFunctions(mCurrentFunctions, true);
+ setEnabledFunctions(mCurrentFunctions, true, false);
}
mCurrentUser = msg.arg1;
}
@@ -944,14 +951,10 @@
return UsbManager.containsFunction(SystemProperties.get(USB_CONFIG_PROPERTY), function);
}
- public void setCurrentFunctions(String functions) {
- if (DEBUG) Slog.d(TAG, "setCurrentFunctions(" + functions + ")");
- mHandler.sendMessage(MSG_SET_CURRENT_FUNCTIONS, functions);
- }
-
- public void setUsbDataUnlocked(boolean unlocked) {
- if (DEBUG) Slog.d(TAG, "setUsbDataUnlocked(" + unlocked + ")");
- mHandler.sendMessage(MSG_SET_USB_DATA_UNLOCKED, unlocked);
+ public void setCurrentFunctions(String functions, boolean usbDataUnlocked) {
+ if (DEBUG) Slog.d(TAG, "setCurrentFunctions(" + functions + ", " +
+ usbDataUnlocked + ")");
+ mHandler.sendMessage(MSG_SET_CURRENT_FUNCTIONS, functions, usbDataUnlocked);
}
private void readOemUsbOverrideConfig() {
diff --git a/services/usb/java/com/android/server/usb/UsbService.java b/services/usb/java/com/android/server/usb/UsbService.java
index d6dbe90..daccc00 100644
--- a/services/usb/java/com/android/server/usb/UsbService.java
+++ b/services/usb/java/com/android/server/usb/UsbService.java
@@ -287,7 +287,7 @@
}
@Override
- public void setCurrentFunction(String function) {
+ public void setCurrentFunction(String function, boolean usbDataUnlocked) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
if (!isSupportedCurrentFunction(function)) {
@@ -297,7 +297,7 @@
}
if (mDeviceManager != null) {
- mDeviceManager.setCurrentFunctions(function);
+ mDeviceManager.setCurrentFunctions(function, usbDataUnlocked);
} else {
throw new IllegalStateException("USB device mode not supported");
}
@@ -320,12 +320,6 @@
}
@Override
- public void setUsbDataUnlocked(boolean unlocked) {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
- mDeviceManager.setUsbDataUnlocked(unlocked);
- }
-
- @Override
public void allowUsbDebugging(boolean alwaysAllow, String publicKey) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
mDeviceManager.allowUsbDebugging(alwaysAllow, publicKey);
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 66d704b..f177a41 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -515,9 +515,12 @@
final boolean isUnknown = args.argi2 == 1;
if (!mAreAccountsInitialized) {
Log.d(this, "Enqueueing pre-init request %s", id);
- mPreInitializationConnectionRequests.add(new Runnable() {
+ mPreInitializationConnectionRequests.add(
+ new android.telecom.Logging.Runnable(
+ SESSION_HANDLER + SESSION_CREATE_CONN + ".pICR",
+ null /*lock*/) {
@Override
- public void run() {
+ public void loggedRun() {
createConnection(
connectionManagerPhoneAccount,
id,
@@ -525,7 +528,7 @@
isIncoming,
isUnknown);
}
- });
+ }.prepare());
} else {
createConnection(
connectionManagerPhoneAccount,
@@ -1378,9 +1381,9 @@
public void onResult(
final List<ComponentName> componentNames,
final List<IBinder> services) {
- mHandler.post(new Runnable() {
+ mHandler.post(new android.telecom.Logging.Runnable("oAA.qRCS.oR", null /*lock*/) {
@Override
- public void run() {
+ public void loggedRun() {
for (int i = 0; i < componentNames.size() && i < services.size(); i++) {
mRemoteConnectionManager.addConnectionService(
componentNames.get(i),
@@ -1389,17 +1392,17 @@
onAccountsInitialized();
Log.d(this, "remote connection services found: " + services);
}
- });
+ }.prepare());
}
@Override
public void onError() {
- mHandler.post(new Runnable() {
+ mHandler.post(new android.telecom.Logging.Runnable("oAA.qRCS.oE", null /*lock*/) {
@Override
- public void run() {
+ public void loggedRun() {
mAreAccountsInitialized = true;
}
- });
+ }.prepare());
}
});
}
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapter.java b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
index c8cd3c0..616eb8a 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapter.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
@@ -93,7 +93,8 @@
ParcelableConnection connection) {
for (IConnectionServiceAdapter adapter : mAdapters) {
try {
- adapter.handleCreateConnectionComplete(id, request, connection);
+ adapter.handleCreateConnectionComplete(id, request, connection,
+ Log.getExternalSession());
} catch (RemoteException e) {
}
}
@@ -108,7 +109,7 @@
void setActive(String callId) {
for (IConnectionServiceAdapter adapter : mAdapters) {
try {
- adapter.setActive(callId);
+ adapter.setActive(callId, Log.getExternalSession());
} catch (RemoteException e) {
}
}
@@ -122,7 +123,7 @@
void setRinging(String callId) {
for (IConnectionServiceAdapter adapter : mAdapters) {
try {
- adapter.setRinging(callId);
+ adapter.setRinging(callId, Log.getExternalSession());
} catch (RemoteException e) {
}
}
@@ -136,7 +137,7 @@
void setDialing(String callId) {
for (IConnectionServiceAdapter adapter : mAdapters) {
try {
- adapter.setDialing(callId);
+ adapter.setDialing(callId, Log.getExternalSession());
} catch (RemoteException e) {
}
}
@@ -152,7 +153,7 @@
void setDisconnected(String callId, DisconnectCause disconnectCause) {
for (IConnectionServiceAdapter adapter : mAdapters) {
try {
- adapter.setDisconnected(callId, disconnectCause);
+ adapter.setDisconnected(callId, disconnectCause, Log.getExternalSession());
} catch (RemoteException e) {
}
}
@@ -166,7 +167,7 @@
void setOnHold(String callId) {
for (IConnectionServiceAdapter adapter : mAdapters) {
try {
- adapter.setOnHold(callId);
+ adapter.setOnHold(callId, Log.getExternalSession());
} catch (RemoteException e) {
}
}
@@ -181,7 +182,7 @@
void setRingbackRequested(String callId, boolean ringback) {
for (IConnectionServiceAdapter adapter : mAdapters) {
try {
- adapter.setRingbackRequested(callId, ringback);
+ adapter.setRingbackRequested(callId, ringback, Log.getExternalSession());
} catch (RemoteException e) {
}
}
@@ -190,7 +191,7 @@
void setConnectionCapabilities(String callId, int capabilities) {
for (IConnectionServiceAdapter adapter : mAdapters) {
try {
- adapter.setConnectionCapabilities(callId, capabilities);
+ adapter.setConnectionCapabilities(callId, capabilities, Log.getExternalSession());
} catch (RemoteException ignored) {
}
}
@@ -199,7 +200,7 @@
void setConnectionProperties(String callId, int properties) {
for (IConnectionServiceAdapter adapter : mAdapters) {
try {
- adapter.setConnectionProperties(callId, properties);
+ adapter.setConnectionProperties(callId, properties, Log.getExternalSession());
} catch (RemoteException ignored) {
}
}
@@ -217,7 +218,7 @@
for (IConnectionServiceAdapter adapter : mAdapters) {
try {
Log.d(this, "sending connection %s with conference %s", callId, conferenceCallId);
- adapter.setIsConferenced(callId, conferenceCallId);
+ adapter.setIsConferenced(callId, conferenceCallId, Log.getExternalSession());
} catch (RemoteException ignored) {
}
}
@@ -232,7 +233,7 @@
for (IConnectionServiceAdapter adapter : mAdapters) {
try {
Log.d(this, "merge failed for call %s", callId);
- adapter.setConferenceMergeFailed(callId);
+ adapter.setConferenceMergeFailed(callId, Log.getExternalSession());
} catch (RemoteException ignored) {
}
}
@@ -247,7 +248,7 @@
void removeCall(String callId) {
for (IConnectionServiceAdapter adapter : mAdapters) {
try {
- adapter.removeCall(callId);
+ adapter.removeCall(callId, Log.getExternalSession());
} catch (RemoteException ignored) {
}
}
@@ -256,7 +257,7 @@
void onPostDialWait(String callId, String remaining) {
for (IConnectionServiceAdapter adapter : mAdapters) {
try {
- adapter.onPostDialWait(callId, remaining);
+ adapter.onPostDialWait(callId, remaining, Log.getExternalSession());
} catch (RemoteException ignored) {
}
}
@@ -265,7 +266,7 @@
void onPostDialChar(String callId, char nextChar) {
for (IConnectionServiceAdapter adapter : mAdapters) {
try {
- adapter.onPostDialChar(callId, nextChar);
+ adapter.onPostDialChar(callId, nextChar, Log.getExternalSession());
} catch (RemoteException ignored) {
}
}
@@ -279,7 +280,7 @@
void addConferenceCall(String callId, ParcelableConference parcelableConference) {
for (IConnectionServiceAdapter adapter : mAdapters) {
try {
- adapter.addConferenceCall(callId, parcelableConference);
+ adapter.addConferenceCall(callId, parcelableConference, Log.getExternalSession());
} catch (RemoteException ignored) {
}
}
@@ -292,7 +293,8 @@
// Only supported when there is only one adapter.
if (mAdapters.size() == 1) {
try {
- mAdapters.iterator().next().queryRemoteConnectionServices(callback);
+ mAdapters.iterator().next().queryRemoteConnectionServices(callback,
+ Log.getExternalSession());
} catch (RemoteException e) {
Log.e(this, e, "Exception trying to query for remote CSs");
}
@@ -311,7 +313,8 @@
try {
adapter.setVideoProvider(
callId,
- videoProvider == null ? null : videoProvider.getInterface());
+ videoProvider == null ? null : videoProvider.getInterface(),
+ Log.getExternalSession());
} catch (RemoteException e) {
}
}
@@ -326,7 +329,7 @@
void setIsVoipAudioMode(String callId, boolean isVoip) {
for (IConnectionServiceAdapter adapter : mAdapters) {
try {
- adapter.setIsVoipAudioMode(callId, isVoip);
+ adapter.setIsVoipAudioMode(callId, isVoip, Log.getExternalSession());
} catch (RemoteException e) {
}
}
@@ -335,7 +338,7 @@
void setStatusHints(String callId, StatusHints statusHints) {
for (IConnectionServiceAdapter adapter : mAdapters) {
try {
- adapter.setStatusHints(callId, statusHints);
+ adapter.setStatusHints(callId, statusHints, Log.getExternalSession());
} catch (RemoteException e) {
}
}
@@ -344,7 +347,7 @@
void setAddress(String callId, Uri address, int presentation) {
for (IConnectionServiceAdapter adapter : mAdapters) {
try {
- adapter.setAddress(callId, address, presentation);
+ adapter.setAddress(callId, address, presentation, Log.getExternalSession());
} catch (RemoteException e) {
}
}
@@ -353,7 +356,8 @@
void setCallerDisplayName(String callId, String callerDisplayName, int presentation) {
for (IConnectionServiceAdapter adapter : mAdapters) {
try {
- adapter.setCallerDisplayName(callId, callerDisplayName, presentation);
+ adapter.setCallerDisplayName(callId, callerDisplayName, presentation,
+ Log.getExternalSession());
} catch (RemoteException e) {
}
}
@@ -374,7 +378,7 @@
Log.v(this, "setVideoState: %d", videoState);
for (IConnectionServiceAdapter adapter : mAdapters) {
try {
- adapter.setVideoState(callId, videoState);
+ adapter.setVideoState(callId, videoState, Log.getExternalSession());
} catch (RemoteException ignored) {
}
}
@@ -384,7 +388,8 @@
Log.v(this, "setConferenceableConnections: %s, %s", callId, conferenceableCallIds);
for (IConnectionServiceAdapter adapter : mAdapters) {
try {
- adapter.setConferenceableConnections(callId, conferenceableCallIds);
+ adapter.setConferenceableConnections(callId, conferenceableCallIds,
+ Log.getExternalSession());
} catch (RemoteException ignored) {
}
}
@@ -400,7 +405,7 @@
Log.v(this, "addExistingConnection: %s", callId);
for (IConnectionServiceAdapter adapter : mAdapters) {
try {
- adapter.addExistingConnection(callId, connection);
+ adapter.addExistingConnection(callId, connection, Log.getExternalSession());
} catch (RemoteException ignored) {
}
}
@@ -416,7 +421,7 @@
Log.v(this, "putExtras: %s", callId);
for (IConnectionServiceAdapter adapter : mAdapters) {
try {
- adapter.putExtras(callId, extras);
+ adapter.putExtras(callId, extras, Log.getExternalSession());
} catch (RemoteException ignored) {
}
}
@@ -435,7 +440,7 @@
try {
Bundle bundle = new Bundle();
bundle.putBoolean(key, value);
- adapter.putExtras(callId, bundle);
+ adapter.putExtras(callId, bundle, Log.getExternalSession());
} catch (RemoteException ignored) {
}
}
@@ -454,7 +459,7 @@
try {
Bundle bundle = new Bundle();
bundle.putInt(key, value);
- adapter.putExtras(callId, bundle);
+ adapter.putExtras(callId, bundle, Log.getExternalSession());
} catch (RemoteException ignored) {
}
}
@@ -473,7 +478,7 @@
try {
Bundle bundle = new Bundle();
bundle.putString(key, value);
- adapter.putExtras(callId, bundle);
+ adapter.putExtras(callId, bundle, Log.getExternalSession());
} catch (RemoteException ignored) {
}
}
@@ -488,7 +493,7 @@
Log.v(this, "removeExtras: %s %s", callId, keys);
for (IConnectionServiceAdapter adapter : mAdapters) {
try {
- adapter.removeExtras(callId, keys);
+ adapter.removeExtras(callId, keys, Log.getExternalSession());
} catch (RemoteException ignored) {
}
}
@@ -505,7 +510,7 @@
Log.v(this, "onConnectionEvent: %s", event);
for (IConnectionServiceAdapter adapter : mAdapters) {
try {
- adapter.onConnectionEvent(callId, event, extras);
+ adapter.onConnectionEvent(callId, event, extras, Log.getExternalSession());
} catch (RemoteException ignored) {
}
}
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
index bf28feb..ba6dbd4 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
@@ -21,6 +21,7 @@
import android.os.Handler;
import android.os.Message;
import android.os.RemoteException;
+import android.telecom.Logging.Session;
import com.android.internal.os.SomeArgs;
import com.android.internal.telecom.IConnectionServiceAdapter;
@@ -86,46 +87,52 @@
mDelegate.handleCreateConnectionComplete(
(String) args.arg1,
(ConnectionRequest) args.arg2,
- (ParcelableConnection) args.arg3);
+ (ParcelableConnection) args.arg3,
+ null /*Session.Info*/);
} finally {
args.recycle();
}
break;
}
case MSG_SET_ACTIVE:
- mDelegate.setActive((String) msg.obj);
+ mDelegate.setActive((String) msg.obj, null /*Session.Info*/);
break;
case MSG_SET_RINGING:
- mDelegate.setRinging((String) msg.obj);
+ mDelegate.setRinging((String) msg.obj, null /*Session.Info*/);
break;
case MSG_SET_DIALING:
- mDelegate.setDialing((String) msg.obj);
+ mDelegate.setDialing((String) msg.obj, null /*Session.Info*/);
break;
case MSG_SET_DISCONNECTED: {
SomeArgs args = (SomeArgs) msg.obj;
try {
- mDelegate.setDisconnected((String) args.arg1, (DisconnectCause) args.arg2);
+ mDelegate.setDisconnected((String) args.arg1, (DisconnectCause) args.arg2,
+ null /*Session.Info*/);
} finally {
args.recycle();
}
break;
}
case MSG_SET_ON_HOLD:
- mDelegate.setOnHold((String) msg.obj);
+ mDelegate.setOnHold((String) msg.obj, null /*Session.Info*/);
break;
case MSG_SET_RINGBACK_REQUESTED:
- mDelegate.setRingbackRequested((String) msg.obj, msg.arg1 == 1);
+ mDelegate.setRingbackRequested((String) msg.obj, msg.arg1 == 1,
+ null /*Session.Info*/);
break;
case MSG_SET_CONNECTION_CAPABILITIES:
- mDelegate.setConnectionCapabilities((String) msg.obj, msg.arg1);
+ mDelegate.setConnectionCapabilities((String) msg.obj, msg.arg1,
+ null /*Session.Info*/);
break;
case MSG_SET_CONNECTION_PROPERTIES:
- mDelegate.setConnectionProperties((String) msg.obj, msg.arg1);
+ mDelegate.setConnectionProperties((String) msg.obj, msg.arg1,
+ null /*Session.Info*/);
break;
case MSG_SET_IS_CONFERENCED: {
SomeArgs args = (SomeArgs) msg.obj;
try {
- mDelegate.setIsConferenced((String) args.arg1, (String) args.arg2);
+ mDelegate.setIsConferenced((String) args.arg1, (String) args.arg2,
+ null /*Session.Info*/);
} finally {
args.recycle();
}
@@ -135,19 +142,22 @@
SomeArgs args = (SomeArgs) msg.obj;
try {
mDelegate.addConferenceCall(
- (String) args.arg1, (ParcelableConference) args.arg2);
+ (String) args.arg1, (ParcelableConference) args.arg2,
+ null /*Session.Info*/);
} finally {
args.recycle();
}
break;
}
case MSG_REMOVE_CALL:
- mDelegate.removeCall((String) msg.obj);
+ mDelegate.removeCall((String) msg.obj,
+ null /*Session.Info*/);
break;
case MSG_ON_POST_DIAL_WAIT: {
SomeArgs args = (SomeArgs) msg.obj;
try {
- mDelegate.onPostDialWait((String) args.arg1, (String) args.arg2);
+ mDelegate.onPostDialWait((String) args.arg1, (String) args.arg2,
+ null /*Session.Info*/);
} finally {
args.recycle();
}
@@ -156,35 +166,39 @@
case MSG_ON_POST_DIAL_CHAR: {
SomeArgs args = (SomeArgs) msg.obj;
try {
- mDelegate.onPostDialChar((String) args.arg1, (char) args.argi1);
+ mDelegate.onPostDialChar((String) args.arg1, (char) args.argi1,
+ null /*Session.Info*/);
} finally {
args.recycle();
}
break;
}
case MSG_QUERY_REMOTE_CALL_SERVICES:
- mDelegate.queryRemoteConnectionServices((RemoteServiceCallback) msg.obj);
+ mDelegate.queryRemoteConnectionServices((RemoteServiceCallback) msg.obj,
+ null /*Session.Info*/);
break;
case MSG_SET_VIDEO_STATE:
- mDelegate.setVideoState((String) msg.obj, msg.arg1);
+ mDelegate.setVideoState((String) msg.obj, msg.arg1, null /*Session.Info*/);
break;
case MSG_SET_VIDEO_CALL_PROVIDER: {
SomeArgs args = (SomeArgs) msg.obj;
try {
mDelegate.setVideoProvider((String) args.arg1,
- (IVideoProvider) args.arg2);
+ (IVideoProvider) args.arg2, null /*Session.Info*/);
} finally {
args.recycle();
}
break;
}
case MSG_SET_IS_VOIP_AUDIO_MODE:
- mDelegate.setIsVoipAudioMode((String) msg.obj, msg.arg1 == 1);
+ mDelegate.setIsVoipAudioMode((String) msg.obj, msg.arg1 == 1,
+ null /*Session.Info*/);
break;
case MSG_SET_STATUS_HINTS: {
SomeArgs args = (SomeArgs) msg.obj;
try {
- mDelegate.setStatusHints((String) args.arg1, (StatusHints) args.arg2);
+ mDelegate.setStatusHints((String) args.arg1, (StatusHints) args.arg2,
+ null /*Session.Info*/);
} finally {
args.recycle();
}
@@ -193,7 +207,8 @@
case MSG_SET_ADDRESS: {
SomeArgs args = (SomeArgs) msg.obj;
try {
- mDelegate.setAddress((String) args.arg1, (Uri) args.arg2, args.argi1);
+ mDelegate.setAddress((String) args.arg1, (Uri) args.arg2, args.argi1,
+ null /*Session.Info*/);
} finally {
args.recycle();
}
@@ -203,7 +218,8 @@
SomeArgs args = (SomeArgs) msg.obj;
try {
mDelegate.setCallerDisplayName(
- (String) args.arg1, (String) args.arg2, args.argi1);
+ (String) args.arg1, (String) args.arg2, args.argi1,
+ null /*Session.Info*/);
} finally {
args.recycle();
}
@@ -212,8 +228,8 @@
case MSG_SET_CONFERENCEABLE_CONNECTIONS: {
SomeArgs args = (SomeArgs) msg.obj;
try {
- mDelegate.setConferenceableConnections(
- (String) args.arg1, (List<String>) args.arg2);
+ mDelegate.setConferenceableConnections((String) args.arg1,
+ (List<String>) args.arg2, null /*Session.Info*/);
} finally {
args.recycle();
}
@@ -222,8 +238,8 @@
case MSG_ADD_EXISTING_CONNECTION: {
SomeArgs args = (SomeArgs) msg.obj;
try {
- mDelegate.addExistingConnection(
- (String) args.arg1, (ParcelableConnection) args.arg2);
+ mDelegate.addExistingConnection((String) args.arg1,
+ (ParcelableConnection) args.arg2, null /*Session.Info*/);
} finally {
args.recycle();
}
@@ -232,7 +248,8 @@
case MSG_SET_CONFERENCE_MERGE_FAILED: {
SomeArgs args = (SomeArgs) msg.obj;
try {
- mDelegate.setConferenceMergeFailed((String) args.arg1);
+ mDelegate.setConferenceMergeFailed((String) args.arg1,
+ null /*Session.Info*/);
} finally {
args.recycle();
}
@@ -241,7 +258,8 @@
case MSG_PUT_EXTRAS: {
SomeArgs args = (SomeArgs) msg.obj;
try {
- mDelegate.putExtras((String) args.arg1, (Bundle) args.arg2);
+ mDelegate.putExtras((String) args.arg1, (Bundle) args.arg2,
+ null /*Session.Info*/);
} finally {
args.recycle();
}
@@ -250,7 +268,8 @@
case MSG_REMOVE_EXTRAS: {
SomeArgs args = (SomeArgs) msg.obj;
try {
- mDelegate.removeExtras((String) args.arg1, (List<String>) args.arg2);
+ mDelegate.removeExtras((String) args.arg1, (List<String>) args.arg2,
+ null /*Session.Info*/);
} finally {
args.recycle();
}
@@ -260,7 +279,7 @@
SomeArgs args = (SomeArgs) msg.obj;
try {
mDelegate.onConnectionEvent((String) args.arg1, (String) args.arg2,
- (Bundle) args.arg3);
+ (Bundle) args.arg3, null /*Session.Info*/);
} finally {
args.recycle();
}
@@ -275,7 +294,8 @@
public void handleCreateConnectionComplete(
String id,
ConnectionRequest request,
- ParcelableConnection connection) {
+ ParcelableConnection connection,
+ Session.Info sessionInfo) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = id;
args.arg2 = request;
@@ -284,23 +304,23 @@
}
@Override
- public void setActive(String connectionId) {
+ public void setActive(String connectionId, Session.Info sessionInfo) {
mHandler.obtainMessage(MSG_SET_ACTIVE, connectionId).sendToTarget();
}
@Override
- public void setRinging(String connectionId) {
+ public void setRinging(String connectionId, Session.Info sessionInfo) {
mHandler.obtainMessage(MSG_SET_RINGING, connectionId).sendToTarget();
}
@Override
- public void setDialing(String connectionId) {
+ public void setDialing(String connectionId, Session.Info sessionInfo) {
mHandler.obtainMessage(MSG_SET_DIALING, connectionId).sendToTarget();
}
@Override
- public void setDisconnected(
- String connectionId, DisconnectCause disconnectCause) {
+ public void setDisconnected(String connectionId, DisconnectCause disconnectCause,
+ Session.Info sessionInfo) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = connectionId;
args.arg2 = disconnectCause;
@@ -308,39 +328,43 @@
}
@Override
- public void setOnHold(String connectionId) {
+ public void setOnHold(String connectionId, Session.Info sessionInfo) {
mHandler.obtainMessage(MSG_SET_ON_HOLD, connectionId).sendToTarget();
}
@Override
- public void setRingbackRequested(String connectionId, boolean ringback) {
+ public void setRingbackRequested(String connectionId, boolean ringback,
+ Session.Info sessionInfo) {
mHandler.obtainMessage(MSG_SET_RINGBACK_REQUESTED, ringback ? 1 : 0, 0, connectionId)
.sendToTarget();
}
@Override
- public void setConnectionCapabilities(String connectionId, int connectionCapabilities) {
+ public void setConnectionCapabilities(String connectionId, int connectionCapabilities,
+ Session.Info sessionInfo) {
mHandler.obtainMessage(
MSG_SET_CONNECTION_CAPABILITIES, connectionCapabilities, 0, connectionId)
.sendToTarget();
}
@Override
- public void setConnectionProperties(String connectionId, int connectionProperties) {
+ public void setConnectionProperties(String connectionId, int connectionProperties,
+ Session.Info sessionInfo) {
mHandler.obtainMessage(
MSG_SET_CONNECTION_PROPERTIES, connectionProperties, 0, connectionId)
.sendToTarget();
}
@Override
- public void setConferenceMergeFailed(String callId) {
+ public void setConferenceMergeFailed(String callId, Session.Info sessionInfo) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = callId;
mHandler.obtainMessage(MSG_SET_CONFERENCE_MERGE_FAILED, args).sendToTarget();
}
@Override
- public void setIsConferenced(String callId, String conferenceCallId) {
+ public void setIsConferenced(String callId, String conferenceCallId,
+ Session.Info sessionInfo) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = callId;
args.arg2 = conferenceCallId;
@@ -348,7 +372,8 @@
}
@Override
- public void addConferenceCall(String callId, ParcelableConference parcelableConference) {
+ public void addConferenceCall(String callId, ParcelableConference parcelableConference,
+ Session.Info sessionInfo) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = callId;
args.arg2 = parcelableConference;
@@ -356,12 +381,14 @@
}
@Override
- public void removeCall(String connectionId) {
+ public void removeCall(String connectionId,
+ Session.Info sessionInfo) {
mHandler.obtainMessage(MSG_REMOVE_CALL, connectionId).sendToTarget();
}
@Override
- public void onPostDialWait(String connectionId, String remainingDigits) {
+ public void onPostDialWait(String connectionId, String remainingDigits,
+ Session.Info sessionInfo) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = connectionId;
args.arg2 = remainingDigits;
@@ -369,7 +396,8 @@
}
@Override
- public void onPostDialChar(String connectionId, char nextChar) {
+ public void onPostDialChar(String connectionId, char nextChar,
+ Session.Info sessionInfo) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = connectionId;
args.argi1 = nextChar;
@@ -377,17 +405,20 @@
}
@Override
- public void queryRemoteConnectionServices(RemoteServiceCallback callback) {
+ public void queryRemoteConnectionServices(RemoteServiceCallback callback,
+ Session.Info sessionInfo) {
mHandler.obtainMessage(MSG_QUERY_REMOTE_CALL_SERVICES, callback).sendToTarget();
}
@Override
- public void setVideoState(String connectionId, int videoState) {
+ public void setVideoState(String connectionId, int videoState,
+ Session.Info sessionInfo) {
mHandler.obtainMessage(MSG_SET_VIDEO_STATE, videoState, 0, connectionId).sendToTarget();
}
@Override
- public void setVideoProvider(String connectionId, IVideoProvider videoProvider) {
+ public void setVideoProvider(String connectionId, IVideoProvider videoProvider,
+ Session.Info sessionInfo) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = connectionId;
args.arg2 = videoProvider;
@@ -395,13 +426,15 @@
}
@Override
- public final void setIsVoipAudioMode(String connectionId, boolean isVoip) {
+ public final void setIsVoipAudioMode(String connectionId, boolean isVoip,
+ Session.Info sessionInfo) {
mHandler.obtainMessage(MSG_SET_IS_VOIP_AUDIO_MODE, isVoip ? 1 : 0, 0,
connectionId).sendToTarget();
}
@Override
- public final void setStatusHints(String connectionId, StatusHints statusHints) {
+ public final void setStatusHints(String connectionId, StatusHints statusHints,
+ Session.Info sessionInfo) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = connectionId;
args.arg2 = statusHints;
@@ -409,7 +442,8 @@
}
@Override
- public final void setAddress(String connectionId, Uri address, int presentation) {
+ public final void setAddress(String connectionId, Uri address, int presentation,
+ Session.Info sessionInfo) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = connectionId;
args.arg2 = address;
@@ -419,7 +453,8 @@
@Override
public final void setCallerDisplayName(
- String connectionId, String callerDisplayName, int presentation) {
+ String connectionId, String callerDisplayName, int presentation,
+ Session.Info sessionInfo) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = connectionId;
args.arg2 = callerDisplayName;
@@ -428,8 +463,8 @@
}
@Override
- public final void setConferenceableConnections(
- String connectionId, List<String> conferenceableConnectionIds) {
+ public final void setConferenceableConnections(String connectionId,
+ List<String> conferenceableConnectionIds, Session.Info sessionInfo) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = connectionId;
args.arg2 = conferenceableConnectionIds;
@@ -437,8 +472,8 @@
}
@Override
- public final void addExistingConnection(
- String connectionId, ParcelableConnection connection) {
+ public final void addExistingConnection(String connectionId,
+ ParcelableConnection connection, Session.Info sessionInfo) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = connectionId;
args.arg2 = connection;
@@ -446,7 +481,7 @@
}
@Override
- public final void putExtras(String connectionId, Bundle extras) {
+ public final void putExtras(String connectionId, Bundle extras, Session.Info sessionInfo) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = connectionId;
args.arg2 = extras;
@@ -454,7 +489,8 @@
}
@Override
- public final void removeExtras(String connectionId, List<String> keys) {
+ public final void removeExtras(String connectionId, List<String> keys,
+ Session.Info sessionInfo) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = connectionId;
args.arg2 = keys;
@@ -462,7 +498,8 @@
}
@Override
- public final void onConnectionEvent(String connectionId, String event, Bundle extras) {
+ public final void onConnectionEvent(String connectionId, String event, Bundle extras,
+ Session.Info sessionInfo) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = connectionId;
args.arg2 = event;
diff --git a/telecomm/java/android/telecom/Log.java b/telecomm/java/android/telecom/Log.java
index 446bbbb..ced6627 100644
--- a/telecomm/java/android/telecom/Log.java
+++ b/telecomm/java/android/telecom/Log.java
@@ -48,13 +48,13 @@
// Generic tag for all Telecom logging
@VisibleForTesting
public static String TAG = "TelecomFramework";
+ public static boolean DEBUG = isLoggable(android.util.Log.DEBUG);
+ public static boolean INFO = isLoggable(android.util.Log.INFO);
+ public static boolean VERBOSE = isLoggable(android.util.Log.VERBOSE);
+ public static boolean WARN = isLoggable(android.util.Log.WARN);
+ public static boolean ERROR = isLoggable(android.util.Log.ERROR);
private static final boolean FORCE_LOGGING = false; /* STOP SHIP if true */
- public static final boolean DEBUG = isLoggable(android.util.Log.DEBUG);
- public static final boolean INFO = isLoggable(android.util.Log.INFO);
- public static final boolean VERBOSE = isLoggable(android.util.Log.VERBOSE);
- public static final boolean WARN = isLoggable(android.util.Log.WARN);
- public static final boolean ERROR = isLoggable(android.util.Log.ERROR);
// Used to synchronize singleton logging lazy initialization
private static final Object sSingletonSync = new Object();
@@ -340,6 +340,11 @@
public static void setTag(String tag) {
TAG = tag;
+ DEBUG = isLoggable(android.util.Log.DEBUG);
+ INFO = isLoggable(android.util.Log.INFO);
+ VERBOSE = isLoggable(android.util.Log.VERBOSE);
+ WARN = isLoggable(android.util.Log.WARN);
+ ERROR = isLoggable(android.util.Log.ERROR);
}
/**
diff --git a/telecomm/java/android/telecom/Logging/Runnable.java b/telecomm/java/android/telecom/Logging/Runnable.java
index 56c52bf..6e81053 100644
--- a/telecomm/java/android/telecom/Logging/Runnable.java
+++ b/telecomm/java/android/telecom/Logging/Runnable.java
@@ -27,7 +27,7 @@
private Session mSubsession;
private final String mSubsessionName;
- private final Object mLock = new Object();
+ private final Object mLock;
private final java.lang.Runnable mRunnable = new java.lang.Runnable() {
@Override
public void run() {
@@ -45,7 +45,18 @@
}
};
- public Runnable(String subsessionName) {
+ /**
+ * Creates a new Telecom Runnable that incorporates Session Logging into it. Useful for carrying
+ * Logging Sessions through different threads as well as through handlers.
+ * @param subsessionName The name that will be used in the Logs to mark this Session
+ * @param lock The synchronization lock that will be used to lock loggedRun().
+ */
+ public Runnable(String subsessionName, Object lock) {
+ if (lock == null) {
+ mLock = new Object();
+ } else {
+ mLock = lock;
+ }
mSubsessionName = subsessionName;
}
@@ -85,4 +96,4 @@
*/
abstract public void loggedRun();
-}
+}
\ No newline at end of file
diff --git a/telecomm/java/android/telecom/Logging/Session.java b/telecomm/java/android/telecom/Logging/Session.java
index 3a7b8c0..c45bd6b 100644
--- a/telecomm/java/android/telecom/Logging/Session.java
+++ b/telecomm/java/android/telecom/Logging/Session.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;
+import android.telecom.Log;
import android.text.TextUtils;
import com.android.internal.annotations.VisibleForTesting;
@@ -26,20 +27,23 @@
import java.util.ArrayList;
/**
- * The session that stores information about a thread's point of entry into the Telecom code that
- * persists until the thread exits Telecom.
+ * Stores information about a thread's point of entry into that should persist until that thread
+ * exits.
* @hide
*/
public class Session {
public static final String START_SESSION = "START_SESSION";
+ public static final String START_EXTERNAL_SESSION = "START_EXTERNAL_SESSION";
public static final String CREATE_SUBSESSION = "CREATE_SUBSESSION";
public static final String CONTINUE_SUBSESSION = "CONTINUE_SUBSESSION";
public static final String END_SUBSESSION = "END_SUBSESSION";
public static final String END_SESSION = "END_SESSION";
public static final String SUBSESSION_SEPARATION_CHAR = "->";
+ public static final String SESSION_SEPARATION_CHAR_CHILD = "_";
public static final String EXTERNAL_INDICATOR = "E-";
+ public static final String TRUNCATE_STRING = "...";
/**
* Initial value of mExecutionEndTimeMs and the final value of {@link #getLocalExecutionTime()}
@@ -49,15 +53,19 @@
public static class Info implements Parcelable {
public final String sessionId;
- public final String shortMethodName;
+ public final String methodPath;
- private Info(String id, String methodName) {
+ private Info(String id, String path) {
sessionId = id;
- shortMethodName = methodName;
+ methodPath = path;
}
public static Info getInfo (Session s) {
- return new Info(s.getFullSessionId(), s.getShortMethodName());
+ // Create Info based on the truncated method path if the session is external, so we do
+ // not get multiple stacking external sessions (unless we have DEBUG level logging or
+ // lower).
+ return new Info(s.getFullSessionId(), s.getFullMethodPath(
+ !Log.DEBUG && s.isSessionExternal()));
}
/** Responsible for creating Info objects for deserialized Parcels. */
@@ -86,7 +94,7 @@
@Override
public void writeToParcel(Parcel destination, int flags) {
destination.writeString(sessionId);
- destination.writeString(shortMethodName);
+ destination.writeString(methodPath);
}
}
@@ -226,7 +234,15 @@
if (parentSession == null) {
return mSessionId;
} else {
- return parentSession.getFullSessionId() + "_" + mSessionId;
+ if (Log.VERBOSE) {
+ return parentSession.getFullSessionId() +
+ // Append "_X" to subsession to show subsession designation.
+ SESSION_SEPARATION_CHAR_CHILD + mSessionId;
+ } else {
+ // Only worry about the base ID at the top of the tree.
+ return parentSession.getFullSessionId();
+ }
+
}
}
@@ -259,16 +275,18 @@
}
// Recursively concatenate mShortMethodName with the parent Sessions to create full method
- // path. Caches this string so that multiple calls for the path will be quick.
- public String getFullMethodPath() {
+ // path. if truncatePath is set to true, all other external sessions (except for the most
+ // recent) will be truncated to "..."
+ public String getFullMethodPath(boolean truncatePath) {
StringBuilder sb = new StringBuilder();
- getFullMethodPath(sb);
+ getFullMethodPath(sb, truncatePath);
return sb.toString();
}
- private synchronized void getFullMethodPath(StringBuilder sb) {
- // Don't calculate if we have already figured it out!
- if (!TextUtils.isEmpty(mFullMethodPathCache)) {
+ private synchronized void getFullMethodPath(StringBuilder sb, boolean truncatePath) {
+ // Return cached value for method path. When returning the truncated path, recalculate the
+ // full path without using the cached value.
+ if (!TextUtils.isEmpty(mFullMethodPathCache) && !truncatePath) {
sb.append(mFullMethodPathCache);
return;
}
@@ -278,25 +296,37 @@
// Check to see if the session has been renamed yet. If it has not, then the session
// has not been continued.
isSessionStarted = !mShortMethodName.equals(parentSession.mShortMethodName);
- parentSession.getFullMethodPath(sb);
+ parentSession.getFullMethodPath(sb, truncatePath);
sb.append(SUBSESSION_SEPARATION_CHAR);
}
// Encapsulate the external session's method name so it is obvious what part of the session
- // is external.
+ // is external or truncate it if we do not want the entire history.
if (isExternal()) {
- sb.append("(");
- sb.append(mShortMethodName);
- sb.append(")");
+ if (truncatePath) {
+ sb.append(TRUNCATE_STRING);
+ } else {
+ sb.append("(");
+ sb.append(mShortMethodName);
+ sb.append(")");
+ }
} else {
sb.append(mShortMethodName);
}
-
- if(isSessionStarted) {
+ // If we are returning the truncated path, do not save that path as the full path.
+ if (isSessionStarted && !truncatePath) {
// Cache this value so that we do not have to do this work next time!
// We do not cache the value if the session being evaluated hasn't been continued yet.
mFullMethodPathCache = sb.toString();
}
}
+ // Recursively move to the top of the tree to see if the parent session is external.
+ private boolean isSessionExternal() {
+ if (getParentSession() == null) {
+ return isExternal();
+ } else {
+ return getParentSession().isSessionExternal();
+ }
+ }
@Override
public int hashCode() {
@@ -350,7 +380,7 @@
return mParentSession.toString();
} else {
StringBuilder methodName = new StringBuilder();
- methodName.append(getFullMethodPath());
+ methodName.append(getFullMethodPath(false /*truncatePath*/));
if (mOwnerInfo != null && !mOwnerInfo.isEmpty()) {
methodName.append("(InCall package: ");
methodName.append(mOwnerInfo);
diff --git a/telecomm/java/android/telecom/Logging/SessionManager.java b/telecomm/java/android/telecom/Logging/SessionManager.java
index 8ced7f81..949f7b7 100644
--- a/telecomm/java/android/telecom/Logging/SessionManager.java
+++ b/telecomm/java/android/telecom/Logging/SessionManager.java
@@ -177,8 +177,9 @@
}
// Create Session from Info and add to the sessionMapper under this ID.
+ Log.d(LOGGING_TAG, Session.START_EXTERNAL_SESSION);
Session externalSession = new Session(Session.EXTERNAL_INDICATOR + sessionInfo.sessionId,
- sessionInfo.shortMethodName, System.currentTimeMillis(),
+ sessionInfo.methodPath, System.currentTimeMillis(),
false /*isStartedFromActiveSession*/, null);
externalSession.setIsExternal(true);
// Mark the external session as already completed, since we have no way of knowing when
@@ -190,8 +191,6 @@
// Create a subsession from this external Session parent node
Session childSession = createSubsession();
continueSession(childSession, shortMethodName);
-
- Log.d(LOGGING_TAG, Session.START_SESSION);
}
/**
diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java
index dbc2b0c..615ae49 100644
--- a/telecomm/java/android/telecom/PhoneAccount.java
+++ b/telecomm/java/android/telecom/PhoneAccount.java
@@ -114,7 +114,10 @@
public static final int CAPABILITY_SIM_SUBSCRIPTION = 0x4;
/**
- * Flag indicating that this {@code PhoneAccount} is capable of placing video calls.
+ * Flag indicating that this {@code PhoneAccount} is currently able to place video calls.
+ * <p>
+ * See also {@link #CAPABILITY_SUPPORTS_VIDEO_CALLING} which indicates whether the
+ * {@code PhoneAccount} supports placing video calls.
* <p>
* See {@link #getCapabilities}
*/
@@ -179,6 +182,23 @@
public static final int CAPABILITY_EMERGENCY_VIDEO_CALLING = 0x200;
/**
+ * Flag indicating that this {@link PhoneAccount} supports video calling.
+ * This is not an indication that the {@link PhoneAccount} is currently able to make a video
+ * call, but rather that it has the ability to make video calls (but not necessarily at this
+ * time).
+ * <p>
+ * Whether a {@link PhoneAccount} can make a video call is ultimately controlled by
+ * {@link #CAPABILITY_VIDEO_CALLING}, which indicates whether the {@link PhoneAccount} is
+ * currently capable of making a video call. Consider a case where, for example, a
+ * {@link PhoneAccount} supports making video calls (e.g.
+ * {@link #CAPABILITY_SUPPORTS_VIDEO_CALLING}), but a current lack of network connectivity
+ * prevents video calls from being made (e.g. {@link #CAPABILITY_VIDEO_CALLING}).
+ * <p>
+ * See {@link #getCapabilities}
+ */
+ public static final int CAPABILITY_SUPPORTS_VIDEO_CALLING = 0x400;
+
+ /**
* URI scheme for telephone number URIs.
*/
public static final String SCHEME_TEL = "tel";
@@ -716,6 +736,9 @@
*/
private String capabilitiesToString(int capabilities) {
StringBuilder sb = new StringBuilder();
+ if (hasCapabilities(CAPABILITY_SUPPORTS_VIDEO_CALLING)) {
+ sb.append("SuppVideo ");
+ }
if (hasCapabilities(CAPABILITY_VIDEO_CALLING)) {
sb.append("Video ");
}
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
index beb6868..18b56c9 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -21,6 +21,7 @@
import android.os.IBinder;
import android.os.IBinder.DeathRecipient;
import android.os.RemoteException;
+import android.telecom.Logging.Session;
import com.android.internal.telecom.IConnectionService;
import com.android.internal.telecom.IConnectionServiceAdapter;
@@ -54,7 +55,8 @@
public void handleCreateConnectionComplete(
String id,
ConnectionRequest request,
- ParcelableConnection parcel) {
+ ParcelableConnection parcel,
+ Session.Info info) {
RemoteConnection connection =
findConnectionForAction(id, "handleCreateConnectionSuccessful");
if (connection != NULL_CONNECTION && mPendingConnections.contains(connection)) {
@@ -95,7 +97,7 @@
}
@Override
- public void setActive(String callId) {
+ public void setActive(String callId, Session.Info sessionInfo) {
if (mConnectionById.containsKey(callId)) {
findConnectionForAction(callId, "setActive")
.setState(Connection.STATE_ACTIVE);
@@ -106,19 +108,20 @@
}
@Override
- public void setRinging(String callId) {
+ public void setRinging(String callId, Session.Info sessionInfo) {
findConnectionForAction(callId, "setRinging")
.setState(Connection.STATE_RINGING);
}
@Override
- public void setDialing(String callId) {
+ public void setDialing(String callId, Session.Info sessionInfo) {
findConnectionForAction(callId, "setDialing")
.setState(Connection.STATE_DIALING);
}
@Override
- public void setDisconnected(String callId, DisconnectCause disconnectCause) {
+ public void setDisconnected(String callId, DisconnectCause disconnectCause,
+ Session.Info sessionInfo) {
if (mConnectionById.containsKey(callId)) {
findConnectionForAction(callId, "setDisconnected")
.setDisconnected(disconnectCause);
@@ -129,7 +132,7 @@
}
@Override
- public void setOnHold(String callId) {
+ public void setOnHold(String callId, Session.Info sessionInfo) {
if (mConnectionById.containsKey(callId)) {
findConnectionForAction(callId, "setOnHold")
.setState(Connection.STATE_HOLDING);
@@ -140,13 +143,14 @@
}
@Override
- public void setRingbackRequested(String callId, boolean ringing) {
+ public void setRingbackRequested(String callId, boolean ringing, Session.Info sessionInfo) {
findConnectionForAction(callId, "setRingbackRequested")
.setRingbackRequested(ringing);
}
@Override
- public void setConnectionCapabilities(String callId, int connectionCapabilities) {
+ public void setConnectionCapabilities(String callId, int connectionCapabilities,
+ Session.Info sessionInfo) {
if (mConnectionById.containsKey(callId)) {
findConnectionForAction(callId, "setConnectionCapabilities")
.setConnectionCapabilities(connectionCapabilities);
@@ -157,7 +161,8 @@
}
@Override
- public void setConnectionProperties(String callId, int connectionProperties) {
+ public void setConnectionProperties(String callId, int connectionProperties,
+ Session.Info sessionInfo) {
if (mConnectionById.containsKey(callId)) {
findConnectionForAction(callId, "setConnectionProperties")
.setConnectionProperties(connectionProperties);
@@ -168,7 +173,8 @@
}
@Override
- public void setIsConferenced(String callId, String conferenceCallId) {
+ public void setIsConferenced(String callId, String conferenceCallId,
+ Session.Info sessionInfo) {
// Note: callId should not be null; conferenceCallId may be null
RemoteConnection connection =
findConnectionForAction(callId, "setIsConferenced");
@@ -189,7 +195,7 @@
}
@Override
- public void setConferenceMergeFailed(String callId) {
+ public void setConferenceMergeFailed(String callId, Session.Info sessionInfo) {
// Nothing to do here.
// The event has already been handled and there is no state to update
// in the underlying connection or conference objects
@@ -197,8 +203,7 @@
@Override
public void addConferenceCall(
- final String callId,
- ParcelableConference parcel) {
+ final String callId, ParcelableConference parcel, Session.Info sessionInfo) {
RemoteConference conference = new RemoteConference(callId,
mOutgoingConnectionServiceRpc);
@@ -231,7 +236,7 @@
}
@Override
- public void removeCall(String callId) {
+ public void removeCall(String callId, Session.Info sessionInfo) {
if (mConnectionById.containsKey(callId)) {
findConnectionForAction(callId, "removeCall")
.setDestroyed();
@@ -242,24 +247,26 @@
}
@Override
- public void onPostDialWait(String callId, String remaining) {
+ public void onPostDialWait(String callId, String remaining, Session.Info sessionInfo) {
findConnectionForAction(callId, "onPostDialWait")
.setPostDialWait(remaining);
}
@Override
- public void onPostDialChar(String callId, char nextChar) {
+ public void onPostDialChar(String callId, char nextChar, Session.Info sessionInfo) {
findConnectionForAction(callId, "onPostDialChar")
.onPostDialChar(nextChar);
}
@Override
- public void queryRemoteConnectionServices(RemoteServiceCallback callback) {
+ public void queryRemoteConnectionServices(RemoteServiceCallback callback,
+ Session.Info sessionInfo) {
// Not supported from remote connection service.
}
@Override
- public void setVideoProvider(String callId, IVideoProvider videoProvider) {
+ public void setVideoProvider(String callId, IVideoProvider videoProvider,
+ Session.Info sessionInfo) {
RemoteConnection.VideoProvider remoteVideoProvider = null;
if (videoProvider != null) {
remoteVideoProvider = new RemoteConnection.VideoProvider(videoProvider);
@@ -269,32 +276,34 @@
}
@Override
- public void setVideoState(String callId, int videoState) {
+ public void setVideoState(String callId, int videoState, Session.Info sessionInfo) {
findConnectionForAction(callId, "setVideoState")
.setVideoState(videoState);
}
@Override
- public void setIsVoipAudioMode(String callId, boolean isVoip) {
+ public void setIsVoipAudioMode(String callId, boolean isVoip, Session.Info sessionInfo) {
findConnectionForAction(callId, "setIsVoipAudioMode")
.setIsVoipAudioMode(isVoip);
}
@Override
- public void setStatusHints(String callId, StatusHints statusHints) {
+ public void setStatusHints(String callId, StatusHints statusHints,
+ Session.Info sessionInfo) {
findConnectionForAction(callId, "setStatusHints")
.setStatusHints(statusHints);
}
@Override
- public void setAddress(String callId, Uri address, int presentation) {
+ public void setAddress(String callId, Uri address, int presentation,
+ Session.Info sessionInfo) {
findConnectionForAction(callId, "setAddress")
.setAddress(address, presentation);
}
@Override
public void setCallerDisplayName(String callId, String callerDisplayName,
- int presentation) {
+ int presentation, Session.Info sessionInfo) {
findConnectionForAction(callId, "setCallerDisplayName")
.setCallerDisplayName(callerDisplayName, presentation);
}
@@ -305,8 +314,8 @@
}
@Override
- public final void setConferenceableConnections(
- String callId, List<String> conferenceableConnectionIds) {
+ public final void setConferenceableConnections(String callId,
+ List<String> conferenceableConnectionIds, Session.Info sessionInfo) {
List<RemoteConnection> conferenceable = new ArrayList<>();
for (String id : conferenceableConnectionIds) {
if (mConnectionById.containsKey(id)) {
@@ -324,7 +333,8 @@
}
@Override
- public void addExistingConnection(String callId, ParcelableConnection connection) {
+ public void addExistingConnection(String callId, ParcelableConnection connection,
+ Session.Info sessionInfo) {
// TODO: add contents of this method
RemoteConnection remoteConnction = new RemoteConnection(callId,
mOutgoingConnectionServiceRpc, connection);
@@ -333,7 +343,7 @@
}
@Override
- public void putExtras(String callId, Bundle extras) {
+ public void putExtras(String callId, Bundle extras, Session.Info sessionInfo) {
if (hasConnection(callId)) {
findConnectionForAction(callId, "putExtras").putExtras(extras);
} else {
@@ -342,7 +352,7 @@
}
@Override
- public void removeExtras(String callId, List<String> keys) {
+ public void removeExtras(String callId, List<String> keys, Session.Info sessionInfo) {
if (hasConnection(callId)) {
findConnectionForAction(callId, "removeExtra").removeExtras(keys);
} else {
@@ -351,7 +361,8 @@
}
@Override
- public void onConnectionEvent(String callId, String event, Bundle extras) {
+ public void onConnectionEvent(String callId, String event, Bundle extras,
+ Session.Info sessionInfo) {
if (mConnectionById.containsKey(callId)) {
findConnectionForAction(callId, "onConnectionEvent").onConnectionEvent(event,
extras);
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
index 9bc8ffe..35509d0 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
@@ -21,6 +21,7 @@
import android.os.Bundle;
import android.telecom.ConnectionRequest;
import android.telecom.DisconnectCause;
+import android.telecom.Logging.Session;
import android.telecom.ParcelableConnection;
import android.telecom.ParcelableConference;
import android.telecom.StatusHints;
@@ -39,57 +40,66 @@
void handleCreateConnectionComplete(
String callId,
in ConnectionRequest request,
- in ParcelableConnection connection);
+ in ParcelableConnection connection,
+ in Session.Info sessionInfo);
- void setActive(String callId);
+ void setActive(String callId, in Session.Info sessionInfo);
- void setRinging(String callId);
+ void setRinging(String callId, in Session.Info sessionInfo);
- void setDialing(String callId);
+ void setDialing(String callId, in Session.Info sessionInfo);
- void setDisconnected(String callId, in DisconnectCause disconnectCause);
+ void setDisconnected(String callId, in DisconnectCause disconnectCause,
+ in Session.Info sessionInfo);
- void setOnHold(String callId);
+ void setOnHold(String callId, in Session.Info sessionInfo);
- void setRingbackRequested(String callId, boolean ringing);
+ void setRingbackRequested(String callId, boolean ringing, in Session.Info sessionInfo);
- void setConnectionCapabilities(String callId, int connectionCapabilities);
+ void setConnectionCapabilities(String callId, int connectionCapabilities,
+ in Session.Info sessionInfo);
- void setConnectionProperties(String callId, int connectionProperties);
+ void setConnectionProperties(String callId, int connectionProperties,
+ in Session.Info sessionInfo);
- void setIsConferenced(String callId, String conferenceCallId);
+ void setIsConferenced(String callId, String conferenceCallId, in Session.Info sessionInfo);
- void setConferenceMergeFailed(String callId);
+ void setConferenceMergeFailed(String callId, in Session.Info sessionInfo);
- void addConferenceCall(String callId, in ParcelableConference conference);
+ void addConferenceCall(String callId, in ParcelableConference conference,
+ in Session.Info sessionInfo);
- void removeCall(String callId);
+ void removeCall(String callId, in Session.Info sessionInfo);
- void onPostDialWait(String callId, String remaining);
+ void onPostDialWait(String callId, String remaining, in Session.Info sessionInfo);
- void onPostDialChar(String callId, char nextChar);
+ void onPostDialChar(String callId, char nextChar, in Session.Info sessionInfo);
- void queryRemoteConnectionServices(RemoteServiceCallback callback);
+ void queryRemoteConnectionServices(RemoteServiceCallback callback, in Session.Info sessionInfo);
- void setVideoProvider(String callId, IVideoProvider videoProvider);
+ void setVideoProvider(String callId, IVideoProvider videoProvider, in Session.Info sessionInfo);
- void setVideoState(String callId, int videoState);
+ void setVideoState(String callId, int videoState, in Session.Info sessionInfo);
- void setIsVoipAudioMode(String callId, boolean isVoip);
+ void setIsVoipAudioMode(String callId, boolean isVoip, in Session.Info sessionInfo);
- void setStatusHints(String callId, in StatusHints statusHints);
+ void setStatusHints(String callId, in StatusHints statusHints, in Session.Info sessionInfo);
- void setAddress(String callId, in Uri address, int presentation);
+ void setAddress(String callId, in Uri address, int presentation, in Session.Info sessionInfo);
- void setCallerDisplayName(String callId, String callerDisplayName, int presentation);
+ void setCallerDisplayName(String callId, String callerDisplayName, int presentation,
+ in Session.Info sessionInfo);
- void setConferenceableConnections(String callId, in List<String> conferenceableCallIds);
+ void setConferenceableConnections(String callId, in List<String> conferenceableCallIds,
+ in Session.Info sessionInfo);
- void addExistingConnection(String callId, in ParcelableConnection connection);
+ void addExistingConnection(String callId, in ParcelableConnection connection,
+ in Session.Info sessionInfo);
- void putExtras(String callId, in Bundle extras);
+ void putExtras(String callId, in Bundle extras, in Session.Info sessionInfo);
- void removeExtras(String callId, in List<String> keys);
+ void removeExtras(String callId, in List<String> keys, in Session.Info sessionInfo);
- void onConnectionEvent(String callId, String event, in Bundle extras);
+ void onConnectionEvent(String callId, String event, in Bundle extras,
+ in Session.Info sessionInfo);
}
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index d064cd9..a1316a9 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -208,6 +208,14 @@
KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool";
/**
+ * List of RIL radio technologies (See {@link ServiceState} {@code RIL_RADIO_TECHNOLOGY_*}
+ * constants) which support only a single data connection at a time. Some carriers do not
+ * support multiple pdp on UMTS.
+ */
+ public static final String
+ KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY = "only_single_dc_allowed_int_array";
+
+ /**
* Override the platform's notion of a network operator being considered roaming.
* Value is string array of MCCMNCs to be considered roaming for 3GPP RATs.
*/
@@ -848,6 +856,15 @@
sDefaults.putStringArray(KEY_CARRIER_METERED_ROAMING_APN_TYPES_STRINGS,
new String[]{"default", "mms", "dun", "supl"});
+ sDefaults.putIntArray(KEY_ONLY_SINGLE_DC_ALLOWED_INT_ARRAY,
+ new int[]{
+ 4, /* IS95A */
+ 5, /* IS95B */
+ 6, /* 1xRTT */
+ 7, /* EVDO_0 */
+ 8, /* EVDO_A */
+ 12 /* EVDO_B */
+ });
sDefaults.putStringArray(KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY, null);
sDefaults.putStringArray(KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY, null);
sDefaults.putStringArray(KEY_CDMA_ROAMING_NETWORKS_STRING_ARRAY, null);
diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java
index addf7ef..4137853 100644
--- a/telephony/java/android/telephony/CellSignalStrengthGsm.java
+++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java
@@ -141,6 +141,16 @@
}
/**
+ * Get the GSM timing advance between 0..219 symbols (normally 0..63).
+ * Integer.MAX_VALUE is reported when there is no RR connection.
+ * Refer to 3GPP 45.010 Sec 5.8
+ * @return the current GSM timing advance, if available.
+ */
+ public int getTimingAdvance() {
+ return mTimingAdvance;
+ }
+
+ /**
* Get the signal strength as dBm
*/
@Override
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index 3c0a8d6..0d07a40 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -168,20 +168,34 @@
}
/**
- * @hide
+ * Get reference signal received quality
*/
public int getRsrq() {
return mRsrq;
}
/**
- * @hide
+ * Get reference signal signal-to-noise ratio
*/
public int getRssnr() {
return mRssnr;
}
/**
+ * Get reference signal received power
+ */
+ public int getRsrp() {
+ return mRsrp;
+ }
+
+ /**
+ * Get channel quality indicator
+ */
+ public int getCqi() {
+ return mCqi;
+ }
+
+ /**
* Get signal strength as dBm
*/
@Override
@@ -206,8 +220,10 @@
}
/**
- * Get the timing advance value for LTE.
- * See 3GPP xxxx
+ * Get the timing advance value for LTE, as a value between 0..63.
+ * Integer.MAX_VALUE is reported when there is no active RRC
+ * connection. Refer to 3GPP 36.213 Sec 4.2.3
+ * @return the LTE timing advance, if available.
*/
public int getTimingAdvance() {
return mTimingAdvance;
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index bb2b447..32f487b 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -233,7 +233,7 @@
* @hide
*/
/** @hide */
- protected int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ protected Integer mSubId;
private final Handler mHandler;
@@ -242,7 +242,7 @@
* This class requires Looper.myLooper() not return null.
*/
public PhoneStateListener() {
- this(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, Looper.myLooper());
+ this(null, Looper.myLooper());
}
/**
@@ -251,7 +251,7 @@
* @hide
*/
public PhoneStateListener(Looper looper) {
- this(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, looper);
+ this(null, looper);
}
/**
@@ -260,7 +260,7 @@
* own non-null Looper use PhoneStateListener(int subId, Looper looper) below.
* @hide
*/
- public PhoneStateListener(int subId) {
+ public PhoneStateListener(Integer subId) {
this(subId, Looper.myLooper());
}
@@ -269,7 +269,7 @@
* and non-null Looper.
* @hide
*/
- public PhoneStateListener(int subId, Looper looper) {
+ public PhoneStateListener(Integer subId, Looper looper) {
if (DBG) log("ctor: subId=" + subId + " looper=" + looper);
mSubId = subId;
mHandler = new Handler(looper) {
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index e87cba1..c484fd3 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -20,6 +20,7 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.telephony.Rlog;
+import android.util.Log;
import android.content.res.Resources;
/**
@@ -51,11 +52,6 @@
//Use int max, as -1 is a valid value in signal strength
public static final int INVALID = 0x7FFFFFFF;
- private static final int RSRP_THRESH_TYPE_STRICT = 0;
- private static final int[] RSRP_THRESH_STRICT = new int[] {-140, -115, -105, -95, -85, -44};
- private static final int[] RSRP_THRESH_LENIENT = new int[] {-140, -128, -118, -108, -98, -44};
-
-
private int mGsmSignalStrength; // Valid values are (0-31, 99) as defined in TS 27.007 8.5
private int mGsmBitErrorRate; // bit error rate (0-7, 99) as defined in TS 27.007 8.5
private int mCdmaDbm; // This value is the RSSI value
@@ -791,22 +787,20 @@
*/
int rssiIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN, rsrpIconLevel = -1, snrIconLevel = -1;
- int rsrpThreshType = Resources.getSystem().getInteger(com.android.internal.R.integer.
- config_LTE_RSRP_threshold_type);
- int[] threshRsrp;
- if (rsrpThreshType == RSRP_THRESH_TYPE_STRICT) {
- threshRsrp = RSRP_THRESH_STRICT;
+ int[] threshRsrp = Resources.getSystem().getIntArray(
+ com.android.internal.R.array.config_lteDbmThresholds);
+ if (threshRsrp.length != 6) {
+ Log.wtf(LOG_TAG, "getLteLevel - config_lteDbmThresholds has invalid num of elements."
+ + " Cannot evaluate RSRP signal.");
} else {
- threshRsrp = RSRP_THRESH_LENIENT;
+ if (mLteRsrp > threshRsrp[5]) rsrpIconLevel = -1;
+ else if (mLteRsrp >= threshRsrp[4]) rsrpIconLevel = SIGNAL_STRENGTH_GREAT;
+ else if (mLteRsrp >= threshRsrp[3]) rsrpIconLevel = SIGNAL_STRENGTH_GOOD;
+ else if (mLteRsrp >= threshRsrp[2]) rsrpIconLevel = SIGNAL_STRENGTH_MODERATE;
+ else if (mLteRsrp >= threshRsrp[1]) rsrpIconLevel = SIGNAL_STRENGTH_POOR;
+ else if (mLteRsrp >= threshRsrp[0]) rsrpIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
}
- if (mLteRsrp > threshRsrp[5]) rsrpIconLevel = -1;
- else if (mLteRsrp >= threshRsrp[4]) rsrpIconLevel = SIGNAL_STRENGTH_GREAT;
- else if (mLteRsrp >= threshRsrp[3]) rsrpIconLevel = SIGNAL_STRENGTH_GOOD;
- else if (mLteRsrp >= threshRsrp[2]) rsrpIconLevel = SIGNAL_STRENGTH_MODERATE;
- else if (mLteRsrp >= threshRsrp[1]) rsrpIconLevel = SIGNAL_STRENGTH_POOR;
- else if (mLteRsrp >= threshRsrp[0]) rsrpIconLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
-
/*
* Values are -200 dB to +300 (SNR*10dB) RS_SNR >= 13.0 dB =>4 bars 4.5
* dB <= RS_SNR < 13.0 dB => 3 bars 1.0 dB <= RS_SNR < 4.5 dB => 2 bars
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 4dab2bb..ca17c06 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -255,6 +255,22 @@
return new TelephonyManager(mContext, subId);
}
+ /**
+ * Create a new TelephonyManager object pinned to the subscription ID associated with the given
+ * phone account.
+ *
+ * @return a TelephonyManager that uses the given phone account for all calls, or {@code null}
+ * if the phone account does not correspond to a valid subscription ID.
+ */
+ @Nullable
+ public TelephonyManager createForPhoneAccountHandle(PhoneAccountHandle phoneAccountHandle) {
+ int subId = getSubIdForPhoneAccountHandle(phoneAccountHandle);
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
+ return null;
+ }
+ return new TelephonyManager(mContext, subId);
+ }
+
/** {@hide} */
public boolean isMultiSimEnabled() {
return (multiSimConfig.equals("dsds") || multiSimConfig.equals("dsda") ||
@@ -662,42 +678,49 @@
public static final String EXTRA_DATA_FAILURE_CAUSE = PhoneConstants.DATA_FAILURE_CAUSE_KEY;
/**
- * Broadcast intent action for letting custom component know to show voicemail notification.
- * @hide
+ * Broadcast intent action for letting the default dialer to know to show voicemail
+ * notification.
+ *
+ * <p>
+ * The {@link #EXTRA_NOTIFICATION_COUNT} extra indicates the total numbers of unheard
+ * voicemails.
+ * The {@link #EXTRA_VOICEMAIL_NUMBER} extra indicates the voicemail number if available.
+ * The {@link #EXTRA_CALL_VOICEMAIL_INTENT} extra is a {@link android.app.PendingIntent} that
+ * will call the voicemail number when sent. This extra will be empty if the voicemail number
+ * is not set, and {@link #EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT} will be set instead.
+ * The {@link #EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT} extra is a
+ * {@link android.app.PendingIntent} that will launch the voicemail settings. This extra is only
+ * available when the voicemail number is not set.
+ *
+ * @see #EXTRA_NOTIFICATION_COUNT
+ * @see #EXTRA_VOICEMAIL_NUMBER
+ * @see #EXTRA_CALL_VOICEMAIL_INTENT
+ * @see #EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT
*/
- @SystemApi
public static final String ACTION_SHOW_VOICEMAIL_NOTIFICATION =
"android.telephony.action.SHOW_VOICEMAIL_NOTIFICATION";
/**
* The number of voice messages associated with the notification.
- * @hide
*/
- @SystemApi
public static final String EXTRA_NOTIFICATION_COUNT =
"android.telephony.extra.NOTIFICATION_COUNT";
/**
* The voicemail number.
- * @hide
*/
- @SystemApi
public static final String EXTRA_VOICEMAIL_NUMBER =
"android.telephony.extra.VOICEMAIL_NUMBER";
/**
* The intent to call voicemail.
- * @hide
*/
- @SystemApi
public static final String EXTRA_CALL_VOICEMAIL_INTENT =
"android.telephony.extra.CALL_VOICEMAIL_INTENT";
/**
* The intent to launch voicemail settings.
- * @hide
*/
- @SystemApi
public static final String EXTRA_LAUNCH_VOICEMAIL_SETTINGS_INTENT =
"android.telephony.extra.LAUNCH_VOICEMAIL_SETTINGS_INTENT";
@@ -1118,7 +1141,7 @@
private int getPhoneTypeFromProperty(int phoneId) {
String type = getTelephonyProperty(phoneId,
TelephonyProperties.CURRENT_ACTIVE_PHONE, null);
- if (type == null || type.equals("")) {
+ if (type == null || type.isEmpty()) {
return getPhoneTypeFromNetworkType(phoneId);
}
return Integer.parseInt(type);
@@ -1134,7 +1157,7 @@
// use the system property for default network type.
// This is a fail safe, and can only happen at first boot.
String mode = getTelephonyProperty(phoneId, "ro.telephony.default_network", null);
- if (mode != null) {
+ if (mode != null && !mode.isEmpty()) {
return TelephonyManager.getPhoneType(Integer.parseInt(mode));
}
return TelephonyManager.PHONE_TYPE_NONE;
@@ -2783,6 +2806,12 @@
if (mContext == null) return;
try {
Boolean notifyNow = (getITelephony() != null);
+ // If the listener has not explicitly set the subId (for example, created with the
+ // default constructor), replace the subId so it will listen to the account the
+ // telephony manager is created with.
+ if (listener.mSubId == null) {
+ listener.mSubId = mSubId;
+ }
sRegistry.listenForSubscriber(listener.mSubId, getOpPackageName(),
listener.callback, events, notifyNow);
} catch (RemoteException ex) {
@@ -4528,10 +4557,19 @@
return false;
}
- /** @hide */
- @SystemApi
+ /**
+ * Turns mobile data on or off.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the
+ * calling app has carrier privileges.
+ *
+ * @param enable Whether to enable mobile data.
+ *
+ * @see #hasCarrierPrivileges
+ */
public void setDataEnabled(boolean enable) {
- setDataEnabled(SubscriptionManager.getDefaultDataSubscriptionId(), enable);
+ setDataEnabled(getSubId(), enable);
}
/** @hide */
@@ -4547,10 +4585,20 @@
}
}
- /** @hide */
- @SystemApi
+ /**
+ * Returns whether mobile data is enabled or not.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#ACCESS_NETWORK_STATE ACCESS_NETWORK_STATE},
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}, or that the
+ * calling app has carrier privileges.
+ *
+ * @return true if mobile data is enabled.
+ *
+ * @see #hasCarrierPrivileges
+ */
public boolean getDataEnabled() {
- return getDataEnabled(SubscriptionManager.getDefaultDataSubscriptionId());
+ return getDataEnabled(getSubId());
}
/** @hide */
@@ -5146,6 +5194,19 @@
return retval;
}
+ private int getSubIdForPhoneAccountHandle(PhoneAccountHandle phoneAccountHandle) {
+ int retval = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ try {
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ retval = getSubIdForPhoneAccount(service.getPhoneAccount(phoneAccountHandle));
+ }
+ } catch (RemoteException e) {
+ }
+
+ return retval;
+ }
+
/**
* Resets telephony manager settings back to factory defaults.
*
@@ -5195,6 +5256,16 @@
}
/**
+ * Returns the current {@link ServiceState} information.
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ */
+ public ServiceState getServiceState() {
+ return getServiceStateForSubscriber(getSubId());
+ }
+
+ /**
* Returns the service state information on specified subscription. Callers require
* either READ_PRIVILEGED_PHONE_STATE or READ_PHONE_STATE to retrieve the information.
* @hide
diff --git a/test-runner/src/junit/MODULE_LICENSE_CPL b/test-runner/src/junit/MODULE_LICENSE_CPL
deleted file mode 100644
index 541dbb5..0000000
--- a/test-runner/src/junit/MODULE_LICENSE_CPL
+++ /dev/null
@@ -1 +0,0 @@
-http://www.opensource.org/licenses/cpl1.0.php
diff --git a/test-runner/src/junit/runner/ClassPathTestCollector.java b/test-runner/src/junit/runner/ClassPathTestCollector.java
deleted file mode 100644
index f48ddee..0000000
--- a/test-runner/src/junit/runner/ClassPathTestCollector.java
+++ /dev/null
@@ -1,81 +0,0 @@
-package junit.runner;
-
-import java.util.*;
-import java.io.*;
-
-/**
- * An implementation of a TestCollector that consults the
- * class path. It considers all classes on the class path
- * excluding classes in JARs. It leaves it up to subclasses
- * to decide whether a class is a runnable Test.
- *
- * @see TestCollector
- * {@hide} - Not needed for 1.0 SDK
- */
-public abstract class ClassPathTestCollector implements TestCollector {
-
- static final int SUFFIX_LENGTH= ".class".length();
-
- public ClassPathTestCollector() {
- }
-
- public Enumeration collectTests() {
- String classPath= System.getProperty("java.class.path");
- Hashtable result = collectFilesInPath(classPath);
- return result.elements();
- }
-
- public Hashtable collectFilesInPath(String classPath) {
- Hashtable result= collectFilesInRoots(splitClassPath(classPath));
- return result;
- }
-
- Hashtable collectFilesInRoots(Vector roots) {
- Hashtable result= new Hashtable(100);
- Enumeration e= roots.elements();
- while (e.hasMoreElements())
- gatherFiles(new File((String)e.nextElement()), "", result);
- return result;
- }
-
- void gatherFiles(File classRoot, String classFileName, Hashtable result) {
- File thisRoot= new File(classRoot, classFileName);
- if (thisRoot.isFile()) {
- if (isTestClass(classFileName)) {
- String className= classNameFromFile(classFileName);
- result.put(className, className);
- }
- return;
- }
- String[] contents= thisRoot.list();
- if (contents != null) {
- for (int i= 0; i < contents.length; i++)
- gatherFiles(classRoot, classFileName+File.separatorChar+contents[i], result);
- }
- }
-
- Vector splitClassPath(String classPath) {
- Vector result= new Vector();
- String separator= System.getProperty("path.separator");
- StringTokenizer tokenizer= new StringTokenizer(classPath, separator);
- while (tokenizer.hasMoreTokens())
- result.addElement(tokenizer.nextToken());
- return result;
- }
-
- protected boolean isTestClass(String classFileName) {
- return
- classFileName.endsWith(".class") &&
- classFileName.indexOf('$') < 0 &&
- classFileName.indexOf("Test") > 0;
- }
-
- protected String classNameFromFile(String classFileName) {
- // convert /a/b.class to a.b
- String s= classFileName.substring(0, classFileName.length()-SUFFIX_LENGTH);
- String s2= s.replace(File.separatorChar, '.');
- if (s2.startsWith("."))
- return s2.substring(1);
- return s2;
- }
-}
diff --git a/test-runner/src/junit/runner/FailureDetailView.java b/test-runner/src/junit/runner/FailureDetailView.java
deleted file mode 100644
index c846191..0000000
--- a/test-runner/src/junit/runner/FailureDetailView.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package junit.runner;
-
-// The following line was removed for compatibility with Android libraries.
-//import java.awt.Component;
-
-import junit.framework.*;
-
-/**
- * A view to show a details about a failure
- * {@hide} - Not needed for 1.0 SDK
- */
-public interface FailureDetailView {
- // The following definition was removed for compatibility with Android
- // libraries.
- // /**
- // * Returns the component used to present the TraceView
- // */
- // public Component getComponent();
-
- /**
- * Shows details of a TestFailure
- */
- public void showFailure(TestFailure failure);
- /**
- * Clears the view
- */
- public void clear();
-}
diff --git a/test-runner/src/junit/runner/LoadingTestCollector.java b/test-runner/src/junit/runner/LoadingTestCollector.java
deleted file mode 100644
index 9101900..0000000
--- a/test-runner/src/junit/runner/LoadingTestCollector.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package junit.runner;
-
-import java.lang.reflect.*;
-import junit.framework.*;
-
-/**
- * An implementation of a TestCollector that loads
- * all classes on the class path and tests whether
- * it is assignable from Test or provides a static suite method.
- * @see TestCollector
- * {@hide} - Not needed for 1.0 SDK
- */
-public class LoadingTestCollector extends ClassPathTestCollector {
-
- TestCaseClassLoader fLoader;
-
- public LoadingTestCollector() {
- fLoader= new TestCaseClassLoader();
- }
-
- protected boolean isTestClass(String classFileName) {
- try {
- if (classFileName.endsWith(".class")) {
- Class testClass= classFromFile(classFileName);
- return (testClass != null) && isTestClass(testClass);
- }
- }
- catch (ClassNotFoundException expected) {
- }
- catch (NoClassDefFoundError notFatal) {
- }
- return false;
- }
-
- Class classFromFile(String classFileName) throws ClassNotFoundException {
- String className= classNameFromFile(classFileName);
- if (!fLoader.isExcluded(className))
- return fLoader.loadClass(className, false);
- return null;
- }
-
- boolean isTestClass(Class testClass) {
- if (hasSuiteMethod(testClass))
- return true;
- if (Test.class.isAssignableFrom(testClass) &&
- Modifier.isPublic(testClass.getModifiers()) &&
- hasPublicConstructor(testClass))
- return true;
- return false;
- }
-
- boolean hasSuiteMethod(Class testClass) {
- try {
- testClass.getMethod(BaseTestRunner.SUITE_METHODNAME, new Class[0]);
- } catch(Exception e) {
- return false;
- }
- return true;
- }
-
- boolean hasPublicConstructor(Class testClass) {
- try {
- TestSuite.getTestConstructor(testClass);
- } catch(NoSuchMethodException e) {
- return false;
- }
- return true;
- }
-}
diff --git a/test-runner/src/junit/runner/ReloadingTestSuiteLoader.java b/test-runner/src/junit/runner/ReloadingTestSuiteLoader.java
deleted file mode 100644
index c4d80d0..0000000
--- a/test-runner/src/junit/runner/ReloadingTestSuiteLoader.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package junit.runner;
-
-/**
- * A TestSuite loader that can reload classes.
- * {@hide} - Not needed for 1.0 SDK
- */
-public class ReloadingTestSuiteLoader implements TestSuiteLoader {
-
- public Class load(String suiteClassName) throws ClassNotFoundException {
- return createLoader().loadClass(suiteClassName, true);
- }
-
- public Class reload(Class aClass) throws ClassNotFoundException {
- return createLoader().loadClass(aClass.getName(), true);
- }
-
- protected TestCaseClassLoader createLoader() {
- return new TestCaseClassLoader();
- }
-}
diff --git a/test-runner/src/junit/runner/SimpleTestCollector.java b/test-runner/src/junit/runner/SimpleTestCollector.java
deleted file mode 100644
index 6cb0e19..0000000
--- a/test-runner/src/junit/runner/SimpleTestCollector.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package junit.runner;
-
-/**
- * An implementation of a TestCollector that considers
- * a class to be a test class when it contains the
- * pattern "Test" in its name
- * @see TestCollector
- * {@hide} - Not needed for 1.0 SDK
- */
-public class SimpleTestCollector extends ClassPathTestCollector {
-
- public SimpleTestCollector() {
- }
-
- protected boolean isTestClass(String classFileName) {
- return
- classFileName.endsWith(".class") &&
- classFileName.indexOf('$') < 0 &&
- classFileName.indexOf("Test") > 0;
- }
-}
diff --git a/test-runner/src/junit/runner/Sorter.java b/test-runner/src/junit/runner/Sorter.java
deleted file mode 100644
index 8d9341d..0000000
--- a/test-runner/src/junit/runner/Sorter.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package junit.runner;
-
-import java.util.*;
-
-/**
- * A custom quick sort with support to customize the swap behaviour.
- * NOTICE: We can't use the the sorting support from the JDK 1.2 collection
- * classes because of the JDK 1.1.7 compatibility.
- * {@hide} - Not needed for 1.0 SDK
- */
-public class Sorter {
- public static interface Swapper {
- public void swap(Vector values, int left, int right);
- }
-
- public static void sortStrings(Vector values , int left, int right, Swapper swapper) {
- int oleft= left;
- int oright= right;
- String mid= (String)values.elementAt((left + right) / 2);
- do {
- while (((String)(values.elementAt(left))).compareTo(mid) < 0)
- left++;
- while (mid.compareTo((String)(values.elementAt(right))) < 0)
- right--;
- if (left <= right) {
- swapper.swap(values, left, right);
- left++;
- right--;
- }
- } while (left <= right);
-
- if (oleft < right)
- sortStrings(values, oleft, right, swapper);
- if (left < oright)
- sortStrings(values, left, oright, swapper);
- }
-}
diff --git a/test-runner/src/junit/runner/TestCaseClassLoader.java b/test-runner/src/junit/runner/TestCaseClassLoader.java
deleted file mode 100644
index 09eec7f..0000000
--- a/test-runner/src/junit/runner/TestCaseClassLoader.java
+++ /dev/null
@@ -1,225 +0,0 @@
-package junit.runner;
-
-import java.util.*;
-import java.io.*;
-import java.net.URL;
-import java.util.zip.*;
-
-/**
- * A custom class loader which enables the reloading
- * of classes for each test run. The class loader
- * can be configured with a list of package paths that
- * should be excluded from loading. The loading
- * of these packages is delegated to the system class
- * loader. They will be shared across test runs.
- * <p>
- * The list of excluded package paths is specified in
- * a properties file "excluded.properties" that is located in
- * the same place as the TestCaseClassLoader class.
- * <p>
- * <b>Known limitation:</b> the TestCaseClassLoader cannot load classes
- * from jar files.
- * {@hide} - Not needed for 1.0 SDK
- */
-public class TestCaseClassLoader extends ClassLoader {
- /** scanned class path */
- private Vector fPathItems;
- /** default excluded paths */
- private String[] defaultExclusions= {
- "junit.framework.",
- "junit.extensions.",
- "junit.runner."
- };
- /** name of excluded properties file */
- static final String EXCLUDED_FILE= "excluded.properties";
- /** excluded paths */
- private Vector fExcluded;
-
- /**
- * Constructs a TestCaseLoader. It scans the class path
- * and the excluded package paths
- */
- public TestCaseClassLoader() {
- this(System.getProperty("java.class.path"));
- }
-
- /**
- * Constructs a TestCaseLoader. It scans the class path
- * and the excluded package paths
- */
- public TestCaseClassLoader(String classPath) {
- scanPath(classPath);
- readExcludedPackages();
- }
-
- private void scanPath(String classPath) {
- String separator= System.getProperty("path.separator");
- fPathItems= new Vector(10);
- StringTokenizer st= new StringTokenizer(classPath, separator);
- while (st.hasMoreTokens()) {
- fPathItems.addElement(st.nextToken());
- }
- }
-
- public URL getResource(String name) {
- return ClassLoader.getSystemResource(name);
- }
-
- public InputStream getResourceAsStream(String name) {
- return ClassLoader.getSystemResourceAsStream(name);
- }
-
- public boolean isExcluded(String name) {
- for (int i= 0; i < fExcluded.size(); i++) {
- if (name.startsWith((String) fExcluded.elementAt(i))) {
- return true;
- }
- }
- return false;
- }
-
- public synchronized Class loadClass(String name, boolean resolve)
- throws ClassNotFoundException {
-
- Class c= findLoadedClass(name);
- if (c != null)
- return c;
- //
- // Delegate the loading of excluded classes to the
- // standard class loader.
- //
- if (isExcluded(name)) {
- try {
- c= findSystemClass(name);
- return c;
- } catch (ClassNotFoundException e) {
- // keep searching
- }
- }
- if (c == null) {
- byte[] data= lookupClassData(name);
- if (data == null)
- throw new ClassNotFoundException();
- c= defineClass(name, data, 0, data.length);
- }
- if (resolve)
- resolveClass(c);
- return c;
- }
-
- private byte[] lookupClassData(String className) throws ClassNotFoundException {
- byte[] data= null;
- for (int i= 0; i < fPathItems.size(); i++) {
- String path= (String) fPathItems.elementAt(i);
- String fileName= className.replace('.', '/')+".class";
- if (isJar(path)) {
- data= loadJarData(path, fileName);
- } else {
- data= loadFileData(path, fileName);
- }
- if (data != null)
- return data;
- }
- throw new ClassNotFoundException(className);
- }
-
- boolean isJar(String pathEntry) {
- return pathEntry.endsWith(".jar") ||
- pathEntry.endsWith(".apk") ||
- pathEntry.endsWith(".zip");
- }
-
- private byte[] loadFileData(String path, String fileName) {
- File file= new File(path, fileName);
- if (file.exists()) {
- return getClassData(file);
- }
- return null;
- }
-
- private byte[] getClassData(File f) {
- try {
- FileInputStream stream= new FileInputStream(f);
- ByteArrayOutputStream out= new ByteArrayOutputStream(1000);
- byte[] b= new byte[1000];
- int n;
- while ((n= stream.read(b)) != -1)
- out.write(b, 0, n);
- stream.close();
- out.close();
- return out.toByteArray();
-
- } catch (IOException e) {
- }
- return null;
- }
-
- private byte[] loadJarData(String path, String fileName) {
- ZipFile zipFile= null;
- InputStream stream= null;
- File archive= new File(path);
- if (!archive.exists())
- return null;
- try {
- zipFile= new ZipFile(archive);
- } catch(IOException io) {
- return null;
- }
- ZipEntry entry= zipFile.getEntry(fileName);
- if (entry == null)
- return null;
- int size= (int) entry.getSize();
- try {
- stream= zipFile.getInputStream(entry);
- byte[] data= new byte[size];
- int pos= 0;
- while (pos < size) {
- int n= stream.read(data, pos, data.length - pos);
- pos += n;
- }
- zipFile.close();
- return data;
- } catch (IOException e) {
- } finally {
- try {
- if (stream != null)
- stream.close();
- } catch (IOException e) {
- }
- }
- return null;
- }
-
- private void readExcludedPackages() {
- fExcluded= new Vector(10);
- for (int i= 0; i < defaultExclusions.length; i++)
- fExcluded.addElement(defaultExclusions[i]);
-
- InputStream is= getClass().getResourceAsStream(EXCLUDED_FILE);
- if (is == null)
- return;
- Properties p= new Properties();
- try {
- p.load(is);
- }
- catch (IOException e) {
- return;
- } finally {
- try {
- is.close();
- } catch (IOException e) {
- }
- }
- for (Enumeration e= p.propertyNames(); e.hasMoreElements(); ) {
- String key= (String)e.nextElement();
- if (key.startsWith("excluded.")) {
- String path= p.getProperty(key);
- path= path.trim();
- if (path.endsWith("*"))
- path= path.substring(0, path.length()-1);
- if (path.length() > 0)
- fExcluded.addElement(path);
- }
- }
- }
-}
diff --git a/test-runner/src/junit/runner/TestCollector.java b/test-runner/src/junit/runner/TestCollector.java
deleted file mode 100644
index 3ac9d9e..0000000
--- a/test-runner/src/junit/runner/TestCollector.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package junit.runner;
-
-import java.util.*;
-
-
-/**
- * Collects Test class names to be presented
- * by the TestSelector.
- * @see TestSelector
- * {@hide} - Not needed for 1.0 SDK
- */
-public interface TestCollector {
- /**
- * Returns an enumeration of Strings with qualified class names
- */
- public Enumeration collectTests();
-}
diff --git a/test-runner/src/junit/runner/excluded.properties b/test-runner/src/junit/runner/excluded.properties
deleted file mode 100644
index 3284628..0000000
--- a/test-runner/src/junit/runner/excluded.properties
+++ /dev/null
@@ -1,12 +0,0 @@
-#
-# The list of excluded package paths for the TestCaseClassLoader
-#
-excluded.0=sun.*
-excluded.1=com.sun.*
-excluded.2=org.omg.*
-excluded.3=javax.*
-excluded.4=sunw.*
-excluded.5=java.*
-excluded.6=org.w3c.dom.*
-excluded.7=org.xml.sax.*
-excluded.8=net.jini.*
diff --git a/test-runner/src/junit/runner/package.html b/test-runner/src/junit/runner/package.html
deleted file mode 100644
index f08fa70..0000000
--- a/test-runner/src/junit/runner/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<HTML>
-<BODY>
-Utility classes supporting the junit test framework.
-</BODY>
-</HTML>
diff --git a/test-runner/src/junit/textui/package.html b/test-runner/src/junit/textui/package.html
deleted file mode 100644
index 723f2ae..0000000
--- a/test-runner/src/junit/textui/package.html
+++ /dev/null
@@ -1,6 +0,0 @@
-<HTML>
-<BODY>
-Utility classes supporting the junit test framework.
-{@hide} - Not needed for 1.0 SDK
-</BODY>
-</HTML>
diff --git a/tools/layoutlib/bridge/tests/Android.mk b/tools/layoutlib/bridge/tests/Android.mk
index 8a81d0b..4656476 100644
--- a/tools/layoutlib/bridge/tests/Android.mk
+++ b/tools/layoutlib/bridge/tests/Android.mk
@@ -28,7 +28,7 @@
layoutlib_api-prebuilt \
tools-common-prebuilt \
sdk-common \
- junit
+ junit-host
include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/tools/layoutlib/create/tests/Android.mk b/tools/layoutlib/create/tests/Android.mk
index dafb9c6..61e381d 100644
--- a/tools/layoutlib/create/tests/Android.mk
+++ b/tools/layoutlib/create/tests/Android.mk
@@ -23,7 +23,7 @@
LOCAL_MODULE := layoutlib-create-tests
LOCAL_MODULE_TAGS := optional
-LOCAL_JAVA_LIBRARIES := layoutlib_create junit
+LOCAL_JAVA_LIBRARIES := layoutlib_create junit-host
LOCAL_STATIC_JAVA_LIBRARIES := asm-5.0
include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/tools/preload2/Android.mk b/tools/preload2/Android.mk
index ce877b3..769db6b 100644
--- a/tools/preload2/Android.mk
+++ b/tools/preload2/Android.mk
@@ -5,14 +5,14 @@
LOCAL_SRC_FILES := $(call all-java-files-under,src)
# To connect to devices (and take hprof dumps).
-LOCAL_STATIC_JAVA_LIBRARIES := ddmlib-prebuilt
+LOCAL_STATIC_JAVA_LIBRARIES := ddmlib-prebuilt tools-common-prebuilt
# To process hprof dumps.
LOCAL_STATIC_JAVA_LIBRARIES += perflib-prebuilt trove-prebuilt guavalib
# For JDWP access we use the framework in the JDWP tests from Apache Harmony, for
# convenience (and to not depend on internal JDK APIs).
-LOCAL_STATIC_JAVA_LIBRARIES += apache-harmony-jdwp-tests-host junit
+LOCAL_STATIC_JAVA_LIBRARIES += apache-harmony-jdwp-tests-host junit-host
LOCAL_MODULE:= preload2
diff --git a/tools/preload2/preload-tool b/tools/preload2/preload-tool
index 36dbc1c..322b62f 100644
--- a/tools/preload2/preload-tool
+++ b/tools/preload2/preload-tool
@@ -34,4 +34,4 @@
PROG_DIR="$(cd "${PROG_NAME%/*}" ; pwd -P)"
ANDROID_ROOT=$PROG_DIR/..
-java -cp $ANDROID_ROOT/framework/preload2.jar com.android.preload.Main
+java -cp $ANDROID_ROOT/framework/preload2.jar com.android.preload.Main $@
diff --git a/tools/preload2/src/com/android/preload/Main.java b/tools/preload2/src/com/android/preload/Main.java
index ca5b0e0..c42a19b 100644
--- a/tools/preload2/src/com/android/preload/Main.java
+++ b/tools/preload2/src/com/android/preload/Main.java
@@ -32,12 +32,18 @@
import com.android.preload.classdataretrieval.ClassDataRetriever;
import com.android.preload.classdataretrieval.hprof.Hprof;
import com.android.preload.classdataretrieval.jdwp.JDWPClassDataRetriever;
-import com.android.preload.ui.UI;
+import com.android.preload.ui.IUI;
+import com.android.preload.ui.SequenceUI;
+import com.android.preload.ui.SwingUI;
+import java.io.File;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
+import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.NoSuchElementException;
import javax.swing.Action;
import javax.swing.DefaultListModel;
@@ -66,7 +72,7 @@
private DumpTableModel dataTableModel;
private DefaultListModel<Client> clientListModel;
- private UI ui;
+ private IUI ui;
// Actions that need to be updated once a device is selected.
private Collection<DeviceSpecific> deviceSpecificActions;
@@ -85,17 +91,30 @@
+ "android.webkit.WebViewClassic\\$1$" + "|" + "java.lang.ProcessManager$" + "|"
+ "(.*\\$NoPreloadHolder$)";
+ public final static String SCAN_ALL_CMD = "scan-all";
+ public final static String SCAN_PACKAGE_CMD = "scan";
+ public final static String COMPUTE_FILE_CMD = "comp";
+ public final static String EXPORT_CMD = "export";
+ public final static String IMPORT_CMD = "import";
+
/**
* @param args
*/
public static void main(String[] args) {
- Main m = new Main();
- top = m;
+ Main m;
+ if (args.length > 0 && args[0].equals("--seq")) {
+ m = createSequencedMain(args);
+ } else {
+ m = new Main(new SwingUI());
+ }
+ top = m;
m.startUp();
}
- public Main() {
+ public Main(IUI ui) {
+ this.ui = ui;
+
clientListModel = new DefaultListModel<Client>();
dataTableModel = new DumpTableModel();
@@ -124,11 +143,74 @@
}
}
- ui = new UI(clientListModel, dataTableModel, actions);
- ui.setVisible(true);
+ ui.prepare(clientListModel, dataTableModel, actions);
}
- public static UI getUI() {
+ /**
+ * @param args
+ * @return
+ */
+ private static Main createSequencedMain(String[] args) {
+ SequenceUI ui = new SequenceUI();
+ Main main = new Main(ui);
+
+ Iterator<String> it = Arrays.asList(args).iterator();
+ it.next(); // --seq
+ // Setup
+ ui.choice("#" + it.next()); // Device.
+ ui.confirmNo(); // Prepare: no.
+ // Actions
+ try {
+ while (it.hasNext()) {
+ String op = it.next();
+ // Operation: Scan a single package
+ if (SCAN_PACKAGE_CMD.equals(op)) {
+ System.out.println("Scanning package.");
+ ui.action(ScanPackageAction.class);
+ ui.client(it.next());
+ // Operation: Scan all packages
+ } else if (SCAN_ALL_CMD.equals(op)) {
+ System.out.println("Scanning all packages.");
+ ui.action(ScanAllPackagesAction.class);
+ // Operation: Export the output to a file
+ } else if (EXPORT_CMD.equals(op)) {
+ System.out.println("Exporting data.");
+ ui.action(ExportAction.class);
+ ui.output(new File(it.next()));
+ // Operation: Import the input from a file or directory
+ } else if (IMPORT_CMD.equals(op)) {
+ System.out.println("Importing data.");
+ File file = new File(it.next());
+ if (!file.exists()) {
+ throw new RuntimeException(
+ String.format("File does not exist, %s.", file.getAbsolutePath()));
+ } else if (file.isFile()) {
+ ui.action(ImportAction.class);
+ ui.input(file);
+ } else if (file.isDirectory()) {
+ for (File content : file.listFiles()) {
+ ui.action(ImportAction.class);
+ ui.input(content);
+ }
+ }
+ // Operation: Compute preloaded classes with specific threshold
+ } else if (COMPUTE_FILE_CMD.equals(op)) {
+ System.out.println("Compute preloaded classes.");
+ ui.action(ComputeThresholdXAction.class);
+ ui.input(it.next());
+ ui.confirmYes();
+ ui.output(new File(it.next()));
+ }
+ }
+ } catch (NoSuchElementException e) {
+ System.out.println("Failed to parse action sequence correctly.");
+ throw e;
+ }
+
+ return main;
+ }
+
+ public static IUI getUI() {
return top.ui;
}
@@ -176,6 +258,7 @@
new ReloadListAction(clientUtils, getDevice(), clientListModel).run();
getUI().hideWaitDialog();
+ getUI().ready();
}
private void initDevice() {
diff --git a/tools/preload2/src/com/android/preload/actions/AbstractThreadedAction.java b/tools/preload2/src/com/android/preload/actions/AbstractThreadedAction.java
index fbf83d2..5787d85 100644
--- a/tools/preload2/src/com/android/preload/actions/AbstractThreadedAction.java
+++ b/tools/preload2/src/com/android/preload/actions/AbstractThreadedAction.java
@@ -16,6 +16,7 @@
package com.android.preload.actions;
+import com.android.preload.Main;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
@@ -28,7 +29,11 @@
@Override
public void actionPerformed(ActionEvent e) {
- new Thread(this).start();
+ if (Main.getUI().isSingleThreaded()) {
+ run();
+ } else {
+ new Thread(this).start();
+ }
}
}
diff --git a/tools/preload2/src/com/android/preload/actions/ComputeThresholdAction.java b/tools/preload2/src/com/android/preload/actions/ComputeThresholdAction.java
index b524716..3a7f7f7 100644
--- a/tools/preload2/src/com/android/preload/actions/ComputeThresholdAction.java
+++ b/tools/preload2/src/com/android/preload/actions/ComputeThresholdAction.java
@@ -32,14 +32,13 @@
import java.util.regex.Pattern;
import javax.swing.AbstractAction;
-import javax.swing.JFileChooser;
/**
* Compute an intersection of classes from the given data. A class is in the intersection if it
* appears in at least the number of threshold given packages. An optional blacklist can be
* used to filter classes from the intersection.
*/
-public class ComputeThresholdAction extends AbstractAction implements Runnable {
+public class ComputeThresholdAction extends AbstractThreadedAction {
protected int threshold;
private Pattern blacklist;
private DumpTableModel dataTableModel;
@@ -72,7 +71,7 @@
return;
}
- new Thread(this).start();
+ super.actionPerformed(e);
}
@Override
@@ -92,10 +91,8 @@
boolean ret = Main.getUI().showConfirmDialog("Computed a set with " + result.size()
+ " classes, would you like to save to disk?", "Save?");
if (ret) {
- JFileChooser jfc = new JFileChooser();
- int ret2 = jfc.showSaveDialog(Main.getUI());
- if (ret2 == JFileChooser.APPROVE_OPTION) {
- File f = jfc.getSelectedFile();
+ File f = Main.getUI().showSaveDialog();
+ if (f != null) {
saveSet(result, f);
}
}
diff --git a/tools/preload2/src/com/android/preload/actions/ExportAction.java b/tools/preload2/src/com/android/preload/actions/ExportAction.java
index cb8b3df..848a568 100644
--- a/tools/preload2/src/com/android/preload/actions/ExportAction.java
+++ b/tools/preload2/src/com/android/preload/actions/ExportAction.java
@@ -19,14 +19,11 @@
import com.android.preload.DumpDataIO;
import com.android.preload.DumpTableModel;
import com.android.preload.Main;
-
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.PrintWriter;
-import javax.swing.AbstractAction;
-
-public class ExportAction extends AbstractAction implements Runnable {
+public class ExportAction extends AbstractThreadedAction {
private File lastSaveFile;
private DumpTableModel dataTableModel;
@@ -39,7 +36,7 @@
public void actionPerformed(ActionEvent e) {
lastSaveFile = Main.getUI().showSaveDialog();
if (lastSaveFile != null) {
- new Thread(this).start();
+ super.actionPerformed(e);
}
}
diff --git a/tools/preload2/src/com/android/preload/actions/ImportAction.java b/tools/preload2/src/com/android/preload/actions/ImportAction.java
index 5c19765..bfeeb83 100644
--- a/tools/preload2/src/com/android/preload/actions/ImportAction.java
+++ b/tools/preload2/src/com/android/preload/actions/ImportAction.java
@@ -27,7 +27,7 @@
import javax.swing.AbstractAction;
-public class ImportAction extends AbstractAction implements Runnable {
+public class ImportAction extends AbstractThreadedAction {
private File[] lastOpenFiles;
private DumpTableModel dataTableModel;
@@ -40,7 +40,7 @@
public void actionPerformed(ActionEvent e) {
lastOpenFiles = Main.getUI().showOpenDialog(true);
if (lastOpenFiles != null) {
- new Thread(this).start();
+ super.actionPerformed(e);
}
}
diff --git a/tools/preload2/src/com/android/preload/actions/RunMonkeyAction.java b/tools/preload2/src/com/android/preload/actions/RunMonkeyAction.java
index 385e857..29464fc 100644
--- a/tools/preload2/src/com/android/preload/actions/RunMonkeyAction.java
+++ b/tools/preload2/src/com/android/preload/actions/RunMonkeyAction.java
@@ -58,7 +58,12 @@
if (packages.isEmpty()) {
packages = DEFAULT_MONKEY_PACKAGES;
}
- new Thread(new RunMonkeyRunnable(packages)).start();
+ Runnable r = new RunMonkeyRunnable(packages);
+ if (Main.getUI().isSingleThreaded()) {
+ r.run();
+ } else {
+ new Thread(r).start();
+ }
}
private class RunMonkeyRunnable implements Runnable {
diff --git a/tools/preload2/src/com/android/preload/ui/IUI.java b/tools/preload2/src/com/android/preload/ui/IUI.java
new file mode 100644
index 0000000..9371463
--- /dev/null
+++ b/tools/preload2/src/com/android/preload/ui/IUI.java
@@ -0,0 +1,45 @@
+package com.android.preload.ui;
+
+import com.android.ddmlib.Client;
+import java.io.File;
+import java.util.List;
+import javax.swing.Action;
+import javax.swing.ListModel;
+import javax.swing.table.TableModel;
+
+/**
+ * UI abstraction for the tool. This allows a graphical mode, command line mode,
+ * or silent mode.
+ */
+public interface IUI {
+
+ void prepare(ListModel<Client> clientListModel, TableModel dataTableModel,
+ List<Action> actions);
+
+ void ready();
+
+ boolean isSingleThreaded();
+
+ Client getSelectedClient();
+
+ int getSelectedDataTableRow();
+
+ void showWaitDialog();
+
+ void updateWaitDialog(String s);
+
+ void hideWaitDialog();
+
+ void showMessageDialog(String s);
+
+ boolean showConfirmDialog(String title, String message);
+
+ String showInputDialog(String message);
+
+ <T> T showChoiceDialog(String title, String message, T[] choices);
+
+ File showSaveDialog();
+
+ File[] showOpenDialog(boolean multi);
+
+}
diff --git a/tools/preload2/src/com/android/preload/ui/SequenceUI.java b/tools/preload2/src/com/android/preload/ui/SequenceUI.java
new file mode 100644
index 0000000..dc6a4f3
--- /dev/null
+++ b/tools/preload2/src/com/android/preload/ui/SequenceUI.java
@@ -0,0 +1,222 @@
+package com.android.preload.ui;
+
+import com.android.ddmlib.Client;
+import com.android.ddmlib.ClientData;
+import java.io.File;
+import java.util.LinkedList;
+import java.util.List;
+import javax.swing.Action;
+import javax.swing.ListModel;
+import javax.swing.table.TableModel;
+
+public class SequenceUI implements IUI {
+
+ private ListModel<Client> clientListModel;
+ @SuppressWarnings("unused")
+ private TableModel dataTableModel;
+ private List<Action> actions;
+
+ private List<Object> sequence = new LinkedList<>();
+
+ public SequenceUI() {
+ }
+
+ @Override
+ public boolean isSingleThreaded() {
+ return true;
+ }
+
+ @Override
+ public void prepare(ListModel<Client> clientListModel, TableModel dataTableModel,
+ List<Action> actions) {
+ this.clientListModel = clientListModel;
+ this.dataTableModel = dataTableModel;
+ this.actions = actions;
+ }
+
+ public SequenceUI action(Action a) {
+ sequence.add(a);
+ return this;
+ }
+
+ public SequenceUI action(Class<? extends Action> actionClass) {
+ for (Action a : actions) {
+ if (actionClass.equals(a.getClass())) {
+ sequence.add(a);
+ return this;
+ }
+ }
+ throw new IllegalArgumentException("No action of class " + actionClass + " found.");
+ }
+
+ public SequenceUI confirmYes() {
+ sequence.add(Boolean.TRUE);
+ return this;
+ }
+
+ public SequenceUI confirmNo() {
+ sequence.add(Boolean.FALSE);
+ return this;
+ }
+
+ public SequenceUI input(String input) {
+ sequence.add(input);
+ return this;
+ }
+
+ public SequenceUI input(File... f) {
+ sequence.add(f);
+ return this;
+ }
+
+ public SequenceUI output(File f) {
+ sequence.add(f);
+ return this;
+ }
+
+ public SequenceUI tableRow(int i) {
+ sequence.add(i);
+ return this;
+ }
+
+ private class ClientSelector {
+ private String pkg;
+
+ public ClientSelector(String pkg) {
+ this.pkg = pkg;
+ }
+
+ public Client getClient() {
+ for (int i = 0; i < clientListModel.getSize(); i++) {
+ ClientData cd = clientListModel.getElementAt(i).getClientData();
+ if (cd != null) {
+ String s = cd.getClientDescription();
+ if (pkg.equals(s)) {
+ return clientListModel.getElementAt(i);
+ }
+ }
+ }
+ throw new RuntimeException("Didn't find client " + pkg);
+ }
+ }
+
+ public SequenceUI client(String pkg) {
+ sequence.add(new ClientSelector(pkg));
+ return this;
+ }
+
+ public SequenceUI choice(String pattern) {
+ sequence.add(pattern);
+ return this;
+ }
+
+ @Override
+ public void ready() {
+ // Run the actions.
+ // No iterator or foreach loop as the sequence will be emptied while running.
+ try {
+ while (!sequence.isEmpty()) {
+ Object next = sequence.remove(0);
+ if (next instanceof Action) {
+ ((Action)next).actionPerformed(null);
+ } else {
+ throw new IllegalStateException("Didn't expect a non-action: " + next);
+ }
+ }
+ } catch (Exception e) {
+ e.printStackTrace(System.out);
+ }
+
+ // Now shut down.
+ System.exit(0);
+ }
+
+ @Override
+ public Client getSelectedClient() {
+ Object next = sequence.remove(0);
+ if (next instanceof ClientSelector) {
+ return ((ClientSelector)next).getClient();
+ }
+ throw new IllegalStateException("Unexpected: " + next);
+ }
+
+ @Override
+ public int getSelectedDataTableRow() {
+ Object next = sequence.remove(0);
+ if (next instanceof Integer) {
+ return ((Integer)next).intValue();
+ }
+ throw new IllegalStateException("Unexpected: " + next);
+ }
+
+ @Override
+ public void showWaitDialog() {
+ }
+
+ @Override
+ public void updateWaitDialog(String s) {
+ System.out.println(s);
+ }
+
+ @Override
+ public void hideWaitDialog() {
+ }
+
+ @Override
+ public void showMessageDialog(String s) {
+ System.out.println(s);
+ }
+
+ @Override
+ public boolean showConfirmDialog(String title, String message) {
+ Object next = sequence.remove(0);
+ if (next instanceof Boolean) {
+ return ((Boolean)next).booleanValue();
+ }
+ throw new IllegalStateException("Unexpected: " + next);
+ }
+
+ @Override
+ public String showInputDialog(String message) {
+ Object next = sequence.remove(0);
+ if (next instanceof String) {
+ return (String)next;
+ }
+ throw new IllegalStateException("Unexpected: " + next);
+ }
+
+ @Override
+ public <T> T showChoiceDialog(String title, String message, T[] choices) {
+ Object next = sequence.remove(0);
+ if (next instanceof String) {
+ String s = (String)next;
+ for (T t : choices) {
+ if (t.toString().contains(s)) {
+ return t;
+ }
+ }
+ return null;
+ }
+ throw new IllegalStateException("Unexpected: " + next);
+ }
+
+ @Override
+ public File showSaveDialog() {
+ Object next = sequence.remove(0);
+ if (next instanceof File) {
+ System.out.println(next);
+ return (File)next;
+ }
+ throw new IllegalStateException("Unexpected: " + next);
+ }
+
+ @Override
+ public File[] showOpenDialog(boolean multi) {
+ Object next = sequence.remove(0);
+ if (next instanceof File[]) {
+ return (File[])next;
+ }
+ throw new IllegalStateException("Unexpected: " + next);
+ }
+
+}
diff --git a/tools/preload2/src/com/android/preload/ui/UI.java b/tools/preload2/src/com/android/preload/ui/SwingUI.java
similarity index 93%
rename from tools/preload2/src/com/android/preload/ui/UI.java
rename to tools/preload2/src/com/android/preload/ui/SwingUI.java
index 47174dd..cab3744 100644
--- a/tools/preload2/src/com/android/preload/ui/UI.java
+++ b/tools/preload2/src/com/android/preload/ui/SwingUI.java
@@ -41,7 +41,7 @@
import javax.swing.SwingUtilities;
import javax.swing.table.TableModel;
-public class UI extends JFrame {
+public class SwingUI extends JFrame implements IUI {
private JList<Client> clientList;
private JTable dataTable;
@@ -49,11 +49,18 @@
// Shared file chooser, means the directory is retained.
private JFileChooser jfc;
- public UI(ListModel<Client> clientListModel,
- TableModel dataTableModel,
- List<Action> actions) {
+ public SwingUI() {
super("Preloaded-classes computation");
+ }
+ @Override
+ public boolean isSingleThreaded() {
+ return false;
+ }
+
+ @Override
+ public void prepare(ListModel<Client> clientListModel, TableModel dataTableModel,
+ List<Action> actions) {
getContentPane().add(new JScrollPane(clientList = new JList<Client>(clientListModel)),
BorderLayout.WEST);
clientList.setCellRenderer(new ClientListCellRenderer());
@@ -74,18 +81,27 @@
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 800, 600);
+
+ setVisible(true);
}
+ @Override
+ public void ready() {
+ }
+
+ @Override
public Client getSelectedClient() {
return clientList.getSelectedValue();
}
+ @Override
public int getSelectedDataTableRow() {
return dataTable.getSelectedRow();
}
private JDialog currentWaitDialog = null;
+ @Override
public void showWaitDialog() {
if (currentWaitDialog == null) {
currentWaitDialog = new JDialog(this, "Please wait...", true);
@@ -111,6 +127,7 @@
});
}
+ @Override
public void updateWaitDialog(String s) {
if (currentWaitDialog != null) {
((JLabel) currentWaitDialog.getContentPane().getComponent(0)).setText(s);
@@ -124,6 +141,7 @@
}
}
+ @Override
public void hideWaitDialog() {
if (currentWaitDialog != null) {
currentWaitDialog.setVisible(false);
@@ -131,6 +149,7 @@
}
}
+ @Override
public void showMessageDialog(String s) {
// Hide the wait dialog...
if (currentWaitDialog != null) {
@@ -147,6 +166,7 @@
}
}
+ @Override
public boolean showConfirmDialog(String title, String message) {
// Hide the wait dialog...
if (currentWaitDialog != null) {
@@ -164,6 +184,7 @@
}
}
+ @Override
public String showInputDialog(String message) {
// Hide the wait dialog...
if (currentWaitDialog != null) {
@@ -180,6 +201,7 @@
}
}
+ @Override
@SuppressWarnings("unchecked")
public <T> T showChoiceDialog(String title, String message, T[] choices) {
// Hide the wait dialog...
@@ -203,6 +225,7 @@
}
}
+ @Override
public File showSaveDialog() {
// Hide the wait dialog...
if (currentWaitDialog != null) {
@@ -228,6 +251,7 @@
}
}
+ @Override
public File[] showOpenDialog(boolean multi) {
// Hide the wait dialog...
if (currentWaitDialog != null) {
diff --git a/tools/split-select/Android.mk b/tools/split-select/Android.mk
index 199fafa..4a1511e 100644
--- a/tools/split-select/Android.mk
+++ b/tools/split-select/Android.mk
@@ -73,7 +73,7 @@
LOCAL_MODULE_HOST_OS := darwin linux windows
LOCAL_SRC_FILES := $(sources)
-
+LOCAL_STATIC_LIBRARIES := $(hostStaticLibs)
LOCAL_C_INCLUDES := $(cIncludes)
LOCAL_CFLAGS := $(cFlags) -D_DARWIN_UNLIMITED_STREAMS
diff --git a/wifi/java/android/net/wifi/EAPConstants.java b/wifi/java/android/net/wifi/EAPConstants.java
new file mode 100644
index 0000000..b5f7c94
--- /dev/null
+++ b/wifi/java/android/net/wifi/EAPConstants.java
@@ -0,0 +1,57 @@
+/**
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+/**
+ * Utility class containing EAP (Extensible Authentication Protocol) Related constants.
+ *
+ * @hide
+ */
+public final class EAPConstants {
+ // Constant definition for EAP types. Refer to
+ // http://www.iana.org/assignments/eap-numbers/eap-numbers.xhtml for more info.
+ public static final int EAP_MD5 = 4;
+ public static final int EAP_OTP = 5;
+ public static final int EAP_RSA = 9;
+ public static final int EAP_KEA = 11;
+ public static final int EAP_KEA_VALIDATE = 12;
+ public static final int EAP_TLS = 13;
+ public static final int EAP_LEAP = 17;
+ public static final int EAP_SIM = 18;
+ public static final int EAP_TTLS = 21;
+ public static final int EAP_AKA = 23;
+ public static final int EAP_3Com = 24;
+ public static final int EAP_MSCHAPv2 = 26;
+ public static final int EAP_PEAP = 29;
+ public static final int EAP_POTP = 32;
+ public static final int EAP_ActiontecWireless = 35;
+ public static final int EAP_HTTPDigest = 38;
+ public static final int EAP_SPEKE = 41;
+ public static final int EAP_MOBAC = 42;
+ public static final int EAP_FAST = 43;
+ public static final int EAP_ZLXEAP = 44;
+ public static final int EAP_Link = 45;
+ public static final int EAP_PAX = 46;
+ public static final int EAP_PSK = 47;
+ public static final int EAP_SAKE = 48;
+ public static final int EAP_IKEv2 = 49;
+ public static final int EAP_AKA_PRIME = 50;
+ public static final int EAP_GPSK = 51;
+ public static final int EAP_PWD = 52;
+ public static final int EAP_EKE = 53;
+ public static final int EAP_TEAP = 55;
+}
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index a91c949..9e897bf 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -16,6 +16,7 @@
package android.net.wifi;
+import android.net.wifi.hotspot2.PasspointConfiguration;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.net.wifi.ScanSettings;
@@ -58,10 +59,11 @@
int addOrUpdateNetwork(in WifiConfiguration config);
- int addPasspointManagementObject(String mo);
+ boolean addPasspointConfiguration(in PasspointConfiguration config);
- int modifyPasspointManagementObject(String fqdn,
- in List<PasspointManagementObjectDefinition> mos);
+ boolean removePasspointConfiguration(in String fqdn);
+
+ List<PasspointConfiguration> getPasspointConfigurations();
void queryPasspointIcon(long bssid, String fileName);
@@ -89,7 +91,7 @@
WifiInfo getConnectionInfo();
- boolean setWifiEnabled(boolean enable);
+ boolean setWifiEnabled(String packageName, boolean enable);
int getWifiEnabledState();
@@ -125,8 +127,6 @@
WifiConfiguration getWifiApConfiguration();
- WifiConfiguration buildWifiConfig(String uriString, String mimeType, in byte[] data);
-
void setWifiApConfiguration(in WifiConfiguration wifiConfig);
Messenger getWifiServiceMessenger();
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index 465addf..da87135 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -66,6 +66,93 @@
* supported by the access point.
*/
public String capabilities;
+
+ /**
+ * @hide
+ * No security protocol.
+ */
+ public static final int PROTOCOL_NONE = 0;
+ /**
+ * @hide
+ * Security protocol type: WPA version 1.
+ */
+ public static final int PROTOCOL_WPA = 1;
+ /**
+ * @hide
+ * Security protocol type: WPA version 2, also called RSN.
+ */
+ public static final int PROTOCOL_WPA2 = 2;
+ /**
+ * @hide
+ * Security protocol type:
+ * OSU Server-only authenticated layer 2 Encryption Network.
+ * Used for Hotspot 2.0.
+ */
+ public static final int PROTOCOL_OSEN = 3;
+
+ /**
+ * @hide
+ * No security key management scheme.
+ */
+ public static final int KEY_MGMT_NONE = 0;
+ /**
+ * @hide
+ * Security key management scheme: PSK.
+ */
+ public static final int KEY_MGMT_PSK = 1;
+ /**
+ * @hide
+ * Security key management scheme: EAP.
+ */
+ public static final int KEY_MGMT_EAP = 2;
+ /**
+ * @hide
+ * Security key management scheme: FT_PSK.
+ */
+ public static final int KEY_MGMT_FT_PSK = 3;
+ /**
+ * @hide
+ * Security key management scheme: FT_EAP.
+ */
+ public static final int KEY_MGMT_FT_EAP = 4;
+ /**
+ * @hide
+ * Security key management scheme: PSK_SHA256
+ */
+ public static final int KEY_MGMT_PSK_SHA256 = 5;
+ /**
+ * @hide
+ * Security key management scheme: EAP_SHA256.
+ */
+ public static final int KEY_MGMT_EAP_SHA256 = 6;
+ /**
+ * @hide
+ * Security key management scheme: OSEN.
+ * Used for Hotspot 2.0.
+ */
+ public static final int KEY_MGMT_OSEN = 7;
+
+ /**
+ * @hide
+ * No cipher suite.
+ */
+ public static final int CIPHER_NONE = 0;
+ /**
+ * @hide
+ * No group addressed, only used for group data cipher.
+ */
+ public static final int CIPHER_NO_GROUP_ADDRESSED = 1;
+ /**
+ * @hide
+ * Cipher suite: TKIP
+ */
+ public static final int CIPHER_TKIP = 2;
+ /**
+ * @hide
+ * Cipher suite: CCMP
+ */
+ public static final int CIPHER_CCMP = 3;
+
/**
* The detected signal level in dBm, also known as the RSSI.
*
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 24cd275..82d41e3 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -685,12 +685,22 @@
/**
* @hide
* A hint about whether or not the network represented by this WifiConfiguration
- * is metered.
+ * is metered. This is hinted at via the meteredHint bit on DHCP results set in
+ * {@link com.android.server.wifi.WifiStateMachine}, or via a network score in
+ * {@link com.android.server.wifi.ExternalScoreEvaluator}.
*/
public boolean meteredHint;
/**
* @hide
+ * Indicates if a user has specified the WifiConfiguration to be metered. Users
+ * can toggle if a network is metered within Settings -> Data Usage -> Network
+ * Restrictions.
+ */
+ public boolean meteredOverride;
+
+ /**
+ * @hide
* Setting this value will force scan results associated with this configuration to
* be included in the bucket of networks that are externally scored.
* If not set, associated scan results will be treated as legacy saved networks and
@@ -1367,6 +1377,7 @@
didSelfAdd = false;
ephemeral = false;
meteredHint = false;
+ meteredOverride = false;
useExternalScores = false;
validatedInternetAccess = false;
mIpConfiguration = new IpConfiguration();
@@ -1470,9 +1481,11 @@
if (this.validatedInternetAccess) sbuf.append(" validatedInternetAccess");
if (this.ephemeral) sbuf.append(" ephemeral");
if (this.meteredHint) sbuf.append(" meteredHint");
+ if (this.meteredOverride) sbuf.append(" meteredOverride");
if (this.useExternalScores) sbuf.append(" useExternalScores");
if (this.didSelfAdd || this.selfAdded || this.validatedInternetAccess
- || this.ephemeral || this.meteredHint || this.useExternalScores) {
+ || this.ephemeral || this.meteredHint || this.meteredOverride
+ || this.useExternalScores) {
sbuf.append("\n");
}
sbuf.append(" KeyMgmt:");
@@ -1897,6 +1910,7 @@
validatedInternetAccess = source.validatedInternetAccess;
ephemeral = source.ephemeral;
meteredHint = source.meteredHint;
+ meteredOverride = source.meteredOverride;
useExternalScores = source.useExternalScores;
if (source.visibility != null) {
visibility = new Visibility(source.visibility);
@@ -1978,6 +1992,7 @@
dest.writeInt(validatedInternetAccess ? 1 : 0);
dest.writeInt(ephemeral ? 1 : 0);
dest.writeInt(meteredHint ? 1 : 0);
+ dest.writeInt(meteredOverride ? 1 : 0);
dest.writeInt(useExternalScores ? 1 : 0);
dest.writeInt(creatorUid);
dest.writeInt(lastConnectUid);
@@ -2049,6 +2064,7 @@
config.validatedInternetAccess = in.readInt() != 0;
config.ephemeral = in.readInt() != 0;
config.meteredHint = in.readInt() != 0;
+ config.meteredOverride = in.readInt() != 0;
config.useExternalScores = in.readInt() != 0;
config.creatorUid = in.readInt();
config.lastConnectUid = in.readInt();
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index 8d5efba..e48f7bdb 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -22,6 +22,7 @@
import android.net.NetworkUtils;
import android.text.TextUtils;
+import java.lang.Math;
import java.net.InetAddress;
import java.net.Inet4Address;
import java.net.UnknownHostException;
@@ -136,6 +137,15 @@
*/
public double rxSuccessRate;
+ private static final long RESET_TIME_STAMP = Long.MIN_VALUE;
+ private static final long FILTER_TIME_CONSTANT = 3000;
+ /**
+ * This factor is used to adjust the rate output under the new algorithm
+ * such that the result is comparable to the previous algorithm.
+ */
+ private static final long OUTPUT_SCALE_FACTOR = 5000;
+ private long mLastPacketCountUpdateTimeStamp;
+
/**
* @hide
*/
@@ -157,10 +167,9 @@
public int score;
/**
- * TODO: get actual timestamp and calculate true rates
* @hide
*/
- public void updatePacketRates(WifiLinkLayerStats stats) {
+ public void updatePacketRates(WifiLinkLayerStats stats, long timeStamp) {
if (stats != null) {
long txgood = stats.txmpdu_be + stats.txmpdu_bk + stats.txmpdu_vi + stats.txmpdu_vo;
long txretries = stats.retries_be + stats.retries_bk
@@ -169,18 +178,28 @@
long txbad = stats.lostmpdu_be + stats.lostmpdu_bk
+ stats.lostmpdu_vi + stats.lostmpdu_vo;
- if (txBad <= txbad
+ if (mLastPacketCountUpdateTimeStamp != RESET_TIME_STAMP
+ && mLastPacketCountUpdateTimeStamp < timeStamp
+ && txBad <= txbad
&& txSuccess <= txgood
&& rxSuccess <= rxgood
&& txRetries <= txretries) {
- txBadRate = (txBadRate * 0.5)
- + ((double) (txbad - txBad) * 0.5);
- txSuccessRate = (txSuccessRate * 0.5)
- + ((double) (txgood - txSuccess) * 0.5);
- rxSuccessRate = (rxSuccessRate * 0.5)
- + ((double) (rxgood - rxSuccess) * 0.5);
- txRetriesRate = (txRetriesRate * 0.5)
- + ((double) (txretries - txRetries) * 0.5);
+ long timeDelta = timeStamp - mLastPacketCountUpdateTimeStamp;
+ double lastSampleWeight = Math.exp(-1.0 * timeDelta / FILTER_TIME_CONSTANT);
+ double currentSampleWeight = 1.0 - lastSampleWeight;
+
+ txBadRate = txBadRate * lastSampleWeight
+ + (txbad - txBad) * OUTPUT_SCALE_FACTOR / timeDelta
+ * currentSampleWeight;
+ txSuccessRate = txSuccessRate * lastSampleWeight
+ + (txgood - txSuccess) * OUTPUT_SCALE_FACTOR / timeDelta
+ * currentSampleWeight;
+ rxSuccessRate = rxSuccessRate * lastSampleWeight
+ + (rxgood - rxSuccess) * OUTPUT_SCALE_FACTOR / timeDelta
+ * currentSampleWeight;
+ txRetriesRate = txRetriesRate * lastSampleWeight
+ + (txretries - txRetries) * OUTPUT_SCALE_FACTOR / timeDelta
+ * currentSampleWeight;
} else {
txBadRate = 0;
txSuccessRate = 0;
@@ -191,6 +210,7 @@
txSuccess = txgood;
rxSuccess = rxgood;
txRetries = txretries;
+ mLastPacketCountUpdateTimeStamp = timeStamp;
} else {
txBad = 0;
txSuccess = 0;
@@ -200,6 +220,7 @@
txSuccessRate = 0;
rxSuccessRate = 0;
txRetriesRate = 0;
+ mLastPacketCountUpdateTimeStamp = RESET_TIME_STAMP;
}
}
@@ -243,6 +264,7 @@
mRssi = INVALID_RSSI;
mLinkSpeed = -1;
mFrequency = -1;
+ mLastPacketCountUpdateTimeStamp = RESET_TIME_STAMP;
}
/** @hide */
@@ -268,6 +290,7 @@
badRssiCount = 0;
linkStuckCount = 0;
score = 0;
+ mLastPacketCountUpdateTimeStamp = RESET_TIME_STAMP;
}
/**
@@ -295,6 +318,8 @@
txRetriesRate = source.txRetriesRate;
txSuccessRate = source.txSuccessRate;
rxSuccessRate = source.rxSuccessRate;
+ mLastPacketCountUpdateTimeStamp =
+ source.mLastPacketCountUpdateTimeStamp;
score = source.score;
badRssiCount = source.badRssiCount;
lowRssiCount = source.lowRssiCount;
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index d04a60e..a42bef4 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -19,12 +19,14 @@
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemApi;
+import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.DhcpInfo;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
+import android.net.wifi.hotspot2.PasspointConfiguration;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
@@ -561,6 +563,28 @@
public static final String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK";
/**
+ * Activity Action: Show UI to get user approval to enable WiFi.
+ * <p>Input: {@link android.content.Intent#EXTRA_PACKAGE_NAME} string extra with
+ * the name of the app requesting the action.
+ * <p>Output: Nothing.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_REQUEST_ENABLE = "android.net.wifi.action.REQUEST_ENABLE";
+
+ /**
+ * Activity Action: Show UI to get user approval to disable WiFi.
+ * <p>Input: {@link android.content.Intent#EXTRA_PACKAGE_NAME} string extra with
+ * the name of the app requesting the action.
+ * <p>Output: Nothing.
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_REQUEST_DISABLE = "android.net.wifi.action.REQUEST_DISABLE";
+
+ /**
* Internally used Wi-Fi lock mode representing the case were no locks are held.
* @hide
*/
@@ -816,30 +840,50 @@
}
/**
- * Add a Hotspot 2.0 release 2 Management Object
- * @param mo The MO in XML form
- * @return -1 for failure
+ * Add a Passpoint configuration. The configuration provides a credential
+ * for connecting to Passpoint networks that are operated by the Passpoint
+ * service provider specified in the configuration.
+ *
+ * Each configuration is uniquely identified by its FQDN (Fully Qualified Domain
+ * Name). In the case when there is an existing configuration with the same base
+ * domain, the new configuration will replace the existing configuration.
+ *
+ * @param config The Passpoint configuration to be added
+ * @return true on success or false on failure
* @hide
*/
- public int addPasspointManagementObject(String mo) {
+ public boolean addPasspointConfiguration(PasspointConfiguration config) {
try {
- return mService.addPasspointManagementObject(mo);
+ return mService.addPasspointConfiguration(config);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
- * Modify a Hotspot 2.0 release 2 Management Object
- * @param fqdn The FQDN of the service provider
- * @param mos A List of MO definitions to be updated
- * @return the number of nodes updated, or -1 for failure
+ * Remove a Passpoint configuration identified by its FQDN (Fully Qualified Domain Name).
+ *
+ * @param fqdn The FQDN of the passpoint configuration to be removed
+ * @return true on success or false on failure
* @hide
*/
- public int modifyPasspointManagementObject(String fqdn,
- List<PasspointManagementObjectDefinition> mos) {
+ public boolean removePasspointConfiguration(String fqdn) {
try {
- return mService.modifyPasspointManagementObject(fqdn, mos);
+ return mService.removePasspointConfiguration(fqdn);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Return the list of installed Passpoint configurations.
+ *
+ * @return A list of PasspointConfiguration or null
+ * @hide
+ */
+ public List<PasspointConfiguration> getPasspointConfigurations() {
+ try {
+ return mService.getPasspointConfigurations();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1041,7 +1085,7 @@
/** @hide */
public static final int WIFI_FEATURE_SCANNER = 0x0020; // WifiScanner APIs
/** @hide */
- public static final int WIFI_FEATURE_NAN = 0x0040; // Neighbor Awareness Networking
+ public static final int WIFI_FEATURE_AWARE = 0x0040; // Wi-Fi AWare networking
/** @hide */
public static final int WIFI_FEATURE_D2D_RTT = 0x0080; // Device-to-device RTT
/** @hide */
@@ -1122,8 +1166,8 @@
* @return true if this adapter supports Neighbour Awareness Network APIs
* @hide
*/
- public boolean isNanSupported() {
- return isFeatureSupported(WIFI_FEATURE_NAN);
+ public boolean isWifiAwareSupported() {
+ return isFeatureSupported(WIFI_FEATURE_AWARE);
}
/**
@@ -1409,7 +1453,7 @@
*/
public boolean setWifiEnabled(boolean enabled) {
try {
- return mService.setWifiEnabled(enabled);
+ return mService.setWifiEnabled(mContext.getOpPackageName(), enabled);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1552,20 +1596,6 @@
}
/**
- * Builds a WifiConfiguration from Hotspot 2.0 MIME file.
- * @return AP details in WifiConfiguration
- *
- * @hide Dont open yet
- */
- public WifiConfiguration buildWifiConfig(String uriString, String mimeType, byte[] data) {
- try {
- return mService.buildWifiConfig(uriString, mimeType, data);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* Sets the Wi-Fi AP Configuration.
* @return {@code true} if the operation succeeded, {@code false} otherwise
*
diff --git a/wifi/java/android/net/wifi/nan/ConfigRequest.aidl b/wifi/java/android/net/wifi/aware/ConfigRequest.aidl
similarity index 95%
rename from wifi/java/android/net/wifi/nan/ConfigRequest.aidl
rename to wifi/java/android/net/wifi/aware/ConfigRequest.aidl
index 38dddc2..68a7c85 100644
--- a/wifi/java/android/net/wifi/nan/ConfigRequest.aidl
+++ b/wifi/java/android/net/wifi/aware/ConfigRequest.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.net.wifi.nan;
+package android.net.wifi.aware;
parcelable ConfigRequest;
diff --git a/wifi/java/android/net/wifi/nan/ConfigRequest.java b/wifi/java/android/net/wifi/aware/ConfigRequest.java
similarity index 96%
rename from wifi/java/android/net/wifi/nan/ConfigRequest.java
rename to wifi/java/android/net/wifi/aware/ConfigRequest.java
index bcd7932..4b21b15 100644
--- a/wifi/java/android/net/wifi/nan/ConfigRequest.java
+++ b/wifi/java/android/net/wifi/aware/ConfigRequest.java
@@ -14,15 +14,15 @@
* limitations under the License.
*/
-package android.net.wifi.nan;
+package android.net.wifi.aware;
import android.os.Parcel;
import android.os.Parcelable;
/**
- * Defines a request object to configure a Wi-Fi NAN network. Built using
+ * Defines a request object to configure a Wi-Fi Aware network. Built using
* {@link ConfigRequest.Builder}. Configuration is requested using
- * {@link WifiNanManager#attach(android.os.Handler, WifiNanAttachCallback)}.
+ * {@link WifiAwareManager#attach(WifiAwareAttachCallback, android.os.Handler)}.
* Note that the actual achieved configuration may be different from the
* requested configuration - since different applications may request different
* configurations.
@@ -221,7 +221,7 @@
}
/**
- * The Cluster ID is generated randomly for new NAN networks. Specify
+ * The Cluster ID is generated randomly for new Aware networks. Specify
* the lower range of the cluster ID. The upper range is specified using
* the {@link ConfigRequest.Builder#setClusterHigh(int)}. The permitted
* range is 0 (the default) to the value specified by
@@ -246,7 +246,7 @@
}
/**
- * The Cluster ID is generated randomly for new NAN networks. Specify
+ * The Cluster ID is generated randomly for new Aware networks. Specify
* the lower upper of the cluster ID. The lower range is specified using
* the {@link ConfigRequest.Builder#setClusterLow(int)}. The permitted
* range is the value specified by
diff --git a/wifi/java/android/net/wifi/nan/IWifiNanDiscoverySessionCallback.aidl b/wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl
similarity index 88%
rename from wifi/java/android/net/wifi/nan/IWifiNanDiscoverySessionCallback.aidl
rename to wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl
index f2e371d..8ff3842 100644
--- a/wifi/java/android/net/wifi/nan/IWifiNanDiscoverySessionCallback.aidl
+++ b/wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package android.net.wifi.nan;
+package android.net.wifi.aware;
/**
- * Callback interface that WifiNanManager implements
+ * Callback interface that WifiAwareManager implements
*
* {@hide}
*/
-oneway interface IWifiNanDiscoverySessionCallback
+oneway interface IWifiAwareDiscoverySessionCallback
{
void onSessionStarted(int discoverySessionId);
void onSessionConfigSuccess();
diff --git a/wifi/java/android/net/wifi/nan/IWifiNanEventCallback.aidl b/wifi/java/android/net/wifi/aware/IWifiAwareEventCallback.aidl
similarity index 85%
rename from wifi/java/android/net/wifi/nan/IWifiNanEventCallback.aidl
rename to wifi/java/android/net/wifi/aware/IWifiAwareEventCallback.aidl
index 9ac7bf2..30dd64d 100644
--- a/wifi/java/android/net/wifi/nan/IWifiNanEventCallback.aidl
+++ b/wifi/java/android/net/wifi/aware/IWifiAwareEventCallback.aidl
@@ -14,17 +14,17 @@
* limitations under the License.
*/
-package android.net.wifi.nan;
+package android.net.wifi.aware;
-import android.net.wifi.nan.ConfigRequest;
+import android.net.wifi.aware.ConfigRequest;
import android.net.wifi.RttManager;
/**
- * Callback interface that WifiNanManager implements
+ * Callback interface that WifiAwareManager implements
*
* {@hide}
*/
-oneway interface IWifiNanEventCallback
+oneway interface IWifiAwareEventCallback
{
void onConnectSuccess(int clientId);
void onConnectFail(int reason);
diff --git a/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl b/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl
similarity index 71%
rename from wifi/java/android/net/wifi/nan/IWifiNanManager.aidl
rename to wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl
index 5485824..9c92807 100644
--- a/wifi/java/android/net/wifi/nan/IWifiNanManager.aidl
+++ b/wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl
@@ -14,40 +14,40 @@
* limitations under the License.
*/
-package android.net.wifi.nan;
+package android.net.wifi.aware;
import android.app.PendingIntent;
-import android.net.wifi.nan.ConfigRequest;
-import android.net.wifi.nan.IWifiNanDiscoverySessionCallback;
-import android.net.wifi.nan.IWifiNanEventCallback;
-import android.net.wifi.nan.PublishConfig;
-import android.net.wifi.nan.SubscribeConfig;
-import android.net.wifi.nan.WifiNanCharacteristics;
+import android.net.wifi.aware.ConfigRequest;
+import android.net.wifi.aware.IWifiAwareDiscoverySessionCallback;
+import android.net.wifi.aware.IWifiAwareEventCallback;
+import android.net.wifi.aware.PublishConfig;
+import android.net.wifi.aware.SubscribeConfig;
+import android.net.wifi.aware.WifiAwareCharacteristics;
import android.net.wifi.RttManager;
/**
- * Interface that WifiNanService implements
+ * Interface that WifiAwareService implements
*
* {@hide}
*/
-interface IWifiNanManager
+interface IWifiAwareManager
{
- // NAN API
+ // Aware API
void enableUsage();
void disableUsage();
boolean isUsageEnabled();
- WifiNanCharacteristics getCharacteristics();
+ WifiAwareCharacteristics getCharacteristics();
// client API
- void connect(in IBinder binder, in String callingPackage, in IWifiNanEventCallback callback,
+ void connect(in IBinder binder, in String callingPackage, in IWifiAwareEventCallback callback,
in ConfigRequest configRequest, boolean notifyOnIdentityChanged);
void disconnect(int clientId, in IBinder binder);
void publish(int clientId, in PublishConfig publishConfig,
- in IWifiNanDiscoverySessionCallback callback);
+ in IWifiAwareDiscoverySessionCallback callback);
void subscribe(int clientId, in SubscribeConfig subscribeConfig,
- in IWifiNanDiscoverySessionCallback callback);
+ in IWifiAwareDiscoverySessionCallback callback);
// session API
void updatePublish(int clientId, int discoverySessionId, in PublishConfig publishConfig);
diff --git a/wifi/java/android/net/wifi/nan/PublishConfig.aidl b/wifi/java/android/net/wifi/aware/PublishConfig.aidl
similarity index 95%
rename from wifi/java/android/net/wifi/nan/PublishConfig.aidl
rename to wifi/java/android/net/wifi/aware/PublishConfig.aidl
index 5f66d16..2e6dd00 100644
--- a/wifi/java/android/net/wifi/nan/PublishConfig.aidl
+++ b/wifi/java/android/net/wifi/aware/PublishConfig.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.net.wifi.nan;
+package android.net.wifi.aware;
parcelable PublishConfig;
diff --git a/wifi/java/android/net/wifi/nan/PublishConfig.java b/wifi/java/android/net/wifi/aware/PublishConfig.java
similarity index 87%
rename from wifi/java/android/net/wifi/nan/PublishConfig.java
rename to wifi/java/android/net/wifi/aware/PublishConfig.java
index 30c5bc0..3925bd7 100644
--- a/wifi/java/android/net/wifi/nan/PublishConfig.java
+++ b/wifi/java/android/net/wifi/aware/PublishConfig.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.net.wifi.nan;
+package android.net.wifi.aware;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -28,15 +28,16 @@
import java.lang.annotation.RetentionPolicy;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
+import java.util.List;
/**
- * Defines the configuration of a NAN publish session. Built using
+ * Defines the configuration of a Aware publish session. Built using
* {@link PublishConfig.Builder}. A publish session is created using
- * {@link WifiNanSession#publish(android.os.Handler, PublishConfig, WifiNanDiscoverySessionCallback)}
- * or updated using
- * {@link WifiNanPublishDiscoverySession#updatePublish(PublishConfig)}.
+ * {@link WifiAwareSession#publish(PublishConfig, WifiAwareDiscoverySessionCallback,
+ * android.os.Handler)} or updated using
+ * {@link WifiAwarePublishDiscoverySession#updatePublish(PublishConfig)}.
*
- * @hide PROPOSED_NAN_API
+ * @hide PROPOSED_AWARE_API
*/
public final class PublishConfig implements Parcelable {
/** @hide */
@@ -84,7 +85,8 @@
/** @hide */
public final boolean mEnableTerminateNotification;
- private PublishConfig(byte[] serviceName, byte[] serviceSpecificInfo, byte[] matchFilter,
+ /** @hide */
+ public PublishConfig(byte[] serviceName, byte[] serviceSpecificInfo, byte[] matchFilter,
int publishType, int publichCount, int ttlSec, boolean enableTerminateNotification) {
mServiceName = serviceName;
mServiceSpecificInfo = serviceSpecificInfo;
@@ -99,9 +101,9 @@
public String toString() {
return "PublishConfig [mServiceName='" + mServiceName + ", mServiceSpecificInfo='" + (
(mServiceSpecificInfo == null) ? "null" : HexEncoding.encode(mServiceSpecificInfo))
- + ", mTxFilter=" + (new LvBufferUtils.LvIterable(1, mMatchFilter)).toString()
- + ", mPublishType=" + mPublishType + ", mPublishCount=" + mPublishCount
- + ", mTtlSec=" + mTtlSec + ", mEnableTerminateNotification="
+ + ", mMatchFilter=" + (new TlvBufferUtils.TlvIterable(0, 1,
+ mMatchFilter)).toString() + ", mPublishType=" + mPublishType + ", mPublishCount="
+ + mPublishCount + ", mTtlSec=" + mTtlSec + ", mEnableTerminateNotification="
+ mEnableTerminateNotification + "]";
}
@@ -182,10 +184,11 @@
*
* @hide
*/
- public void assertValid(WifiNanCharacteristics characteristics) throws IllegalArgumentException {
- WifiNanUtils.validateServiceName(mServiceName);
+ public void assertValid(WifiAwareCharacteristics characteristics)
+ throws IllegalArgumentException {
+ WifiAwareUtils.validateServiceName(mServiceName);
- if (!LvBufferUtils.isValid(mMatchFilter, 1)) {
+ if (!TlvBufferUtils.isValid(mMatchFilter, 0, 1)) {
throw new IllegalArgumentException(
"Invalid txFilter configuration - LV fields do not match up to length");
}
@@ -280,18 +283,17 @@
* The match filter for a publish session. Used to determine whether a service
* discovery occurred - in addition to relying on the service name.
* <p>
- * Format is an LV byte array: a single byte Length field followed by L bytes (the value of
- * the Length field) of a value blob.
- * <p>
* Optional. Empty by default.
*
- * @param matchFilter The byte-array containing the LV formatted match filter.
+ * @param matchFilter A list of match filter entries (each of which is an arbitrary byte
+ * array).
*
* @return The builder to facilitate chaining
* {@code builder.setXXX(..).setXXX(..)}.
*/
- public Builder setMatchFilter(@Nullable byte[] matchFilter) {
- mMatchFilter = matchFilter;
+ public Builder setMatchFilter(@Nullable List<byte[]> matchFilter) {
+ mMatchFilter = new TlvBufferUtils.TlvConstructor(0, 1).allocateAndPut(
+ matchFilter).getArray();
return this;
}
@@ -320,12 +322,12 @@
* Sets the number of times an unsolicited (configured using
* {@link PublishConfig.Builder#setPublishType(int)}) publish session
* will be broadcast. When the count is reached an event will be
- * generated for {@link WifiNanDiscoverySessionCallback#onSessionTerminated(int)}
- * with {@link WifiNanDiscoverySessionCallback#TERMINATE_REASON_DONE} [unless
+ * generated for {@link WifiAwareDiscoverySessionCallback#onSessionTerminated(int)}
+ * with {@link WifiAwareDiscoverySessionCallback#TERMINATE_REASON_DONE} [unless
* {@link #setTerminateNotificationEnabled(boolean)} disables the callback].
* <p>
* Optional. 0 by default - indicating the session doesn't terminate on its own.
- * Session will be terminated when {@link WifiNanDiscoveryBaseSession#destroy()} is
+ * Session will be terminated when {@link WifiAwareDiscoveryBaseSession#destroy()} is
* called.
*
* @param publishCount Number of publish packets to broadcast.
@@ -346,12 +348,12 @@
* {@link PublishConfig.Builder#setPublishType(int)}) publish session
* will be alive - broadcasting a packet. When the TTL is reached
* an event will be generated for
- * {@link WifiNanDiscoverySessionCallback#onSessionTerminated(int)} with
- * {@link WifiNanDiscoverySessionCallback#TERMINATE_REASON_DONE} [unless
+ * {@link WifiAwareDiscoverySessionCallback#onSessionTerminated(int)} with
+ * {@link WifiAwareDiscoverySessionCallback#TERMINATE_REASON_DONE} [unless
* {@link #setTerminateNotificationEnabled(boolean)} disables the callback].
* <p>
* Optional. 0 by default - indicating the session doesn't terminate on its own.
- * Session will be terminated when {@link WifiNanDiscoveryBaseSession#destroy()} is
+ * Session will be terminated when {@link WifiAwareDiscoveryBaseSession#destroy()} is
* called.
*
* @param ttlSec Lifetime of a publish session in seconds.
@@ -369,7 +371,7 @@
/**
* Configure whether a publish terminate notification
- * {@link WifiNanDiscoverySessionCallback#onSessionTerminated(int)} is reported
+ * {@link WifiAwareDiscoverySessionCallback#onSessionTerminated(int)} is reported
* back to the callback.
*
* @param enable If true the terminate callback will be called when the
diff --git a/wifi/java/android/net/wifi/nan/SubscribeConfig.aidl b/wifi/java/android/net/wifi/aware/SubscribeConfig.aidl
similarity index 95%
rename from wifi/java/android/net/wifi/nan/SubscribeConfig.aidl
rename to wifi/java/android/net/wifi/aware/SubscribeConfig.aidl
index 92344a4..bd73d5e 100644
--- a/wifi/java/android/net/wifi/nan/SubscribeConfig.aidl
+++ b/wifi/java/android/net/wifi/aware/SubscribeConfig.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.net.wifi.nan;
+package android.net.wifi.aware;
parcelable SubscribeConfig;
diff --git a/wifi/java/android/net/wifi/nan/SubscribeConfig.java b/wifi/java/android/net/wifi/aware/SubscribeConfig.java
similarity index 87%
rename from wifi/java/android/net/wifi/nan/SubscribeConfig.java
rename to wifi/java/android/net/wifi/aware/SubscribeConfig.java
index ea7b8e4..bf35445 100644
--- a/wifi/java/android/net/wifi/nan/SubscribeConfig.java
+++ b/wifi/java/android/net/wifi/aware/SubscribeConfig.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.net.wifi.nan;
+package android.net.wifi.aware;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -28,15 +28,16 @@
import java.lang.annotation.RetentionPolicy;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
+import java.util.List;
/**
- * Defines the configuration of a NAN subscribe session. Built using
+ * Defines the configuration of a Aware subscribe session. Built using
* {@link SubscribeConfig.Builder}. Subscribe is done using
- * {@link WifiNanSession#subscribe(android.os.Handler, SubscribeConfig, WifiNanDiscoverySessionCallback)}
- * or
- * {@link WifiNanSubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}.
+ * {@link WifiAwareSession#subscribe(SubscribeConfig, WifiAwareDiscoverySessionCallback,
+ * android.os.Handler)} or
+ * {@link WifiAwareSubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}.
*
- * @hide PROPOSED_NAN_API
+ * @hide PROPOSED_AWARE_API
*/
public final class SubscribeConfig implements Parcelable {
/** @hide */
@@ -106,7 +107,8 @@
/** @hide */
public final boolean mEnableTerminateNotification;
- private SubscribeConfig(byte[] serviceName, byte[] serviceSpecificInfo, byte[] matchFilter,
+ /** @hide */
+ public SubscribeConfig(byte[] serviceName, byte[] serviceSpecificInfo, byte[] matchFilter,
int subscribeType, int publichCount, int ttlSec, int matchStyle,
boolean enableTerminateNotification) {
mServiceName = serviceName;
@@ -123,10 +125,11 @@
public String toString() {
return "SubscribeConfig [mServiceName='" + mServiceName + ", mServiceSpecificInfo='" + (
(mServiceSpecificInfo == null) ? "null" : HexEncoding.encode(mServiceSpecificInfo))
- + ", mMatchFilter=" + (new LvBufferUtils.LvIterable(1, mMatchFilter)).toString()
- + ", mSubscribeType=" + mSubscribeType + ", mSubscribeCount=" + mSubscribeCount
- + ", mTtlSec=" + mTtlSec + ", mMatchType=" + mMatchStyle
- + ", mEnableTerminateNotification=" + mEnableTerminateNotification + "]";
+ + ", mMatchFilter=" + (new TlvBufferUtils.TlvIterable(0, 1,
+ mMatchFilter)).toString() + ", mSubscribeType=" + mSubscribeType
+ + ", mSubscribeCount=" + mSubscribeCount + ", mTtlSec=" + mTtlSec + ", mMatchType="
+ + mMatchStyle + ", mEnableTerminateNotification=" + mEnableTerminateNotification
+ + "]";
}
@Override
@@ -209,10 +212,11 @@
*
* @hide
*/
- public void assertValid(WifiNanCharacteristics characteristics) throws IllegalArgumentException {
- WifiNanUtils.validateServiceName(mServiceName);
+ public void assertValid(WifiAwareCharacteristics characteristics)
+ throws IllegalArgumentException {
+ WifiAwareUtils.validateServiceName(mServiceName);
- if (!LvBufferUtils.isValid(mMatchFilter, 1)) {
+ if (!TlvBufferUtils.isValid(mMatchFilter, 0, 1)) {
throw new IllegalArgumentException(
"Invalid matchFilter configuration - LV fields do not match up to length");
}
@@ -312,18 +316,17 @@
* The match filter for a subscribe session. Used to determine whether a service
* discovery occurred - in addition to relying on the service name.
* <p>
- * Format is an LV byte array: a single byte Length field followed by L bytes (the value of
- * the Length field) of a value blob.
- * <p>
* Optional. Empty by default.
*
- * @param matchFilter The byte-array containing the LV formatted match filter.
+ * @param matchFilter A list of match filter entries (each of which is an arbitrary byte
+ * array).
*
* @return The builder to facilitate chaining
* {@code builder.setXXX(..).setXXX(..)}.
*/
- public Builder setMatchFilter(@Nullable byte[] matchFilter) {
- mMatchFilter = matchFilter;
+ public Builder setMatchFilter(@Nullable List<byte[]> matchFilter) {
+ mMatchFilter = new TlvBufferUtils.TlvConstructor(0, 1).allocateAndPut(
+ matchFilter).getArray();
return this;
}
@@ -352,11 +355,11 @@
* Sets the number of times an active (
* {@link SubscribeConfig.Builder#setSubscribeType(int)}) subscribe session
* will broadcast. When the count is reached an event will be
- * generated for {@link WifiNanDiscoverySessionCallback#onSessionTerminated(int)}
- * with {@link WifiNanDiscoverySessionCallback#TERMINATE_REASON_DONE}.
+ * generated for {@link WifiAwareDiscoverySessionCallback#onSessionTerminated(int)}
+ * with {@link WifiAwareDiscoverySessionCallback#TERMINATE_REASON_DONE}.
* <p>
* Optional. 0 by default - indicating the session doesn't terminate on its own.
- * Session will be terminated when {@link WifiNanDiscoveryBaseSession#destroy()} is
+ * Session will be terminated when {@link WifiAwareDiscoveryBaseSession#destroy()} is
* called.
*
* @param subscribeCount Number of subscribe packets to broadcast.
@@ -377,11 +380,11 @@
* {@link SubscribeConfig.Builder#setSubscribeType(int)}) subscribe session
* will be alive - i.e. broadcasting a packet. When the TTL is reached
* an event will be generated for
- * {@link WifiNanDiscoverySessionCallback#onSessionTerminated(int)} with
- * {@link WifiNanDiscoverySessionCallback#TERMINATE_REASON_DONE}.
+ * {@link WifiAwareDiscoverySessionCallback#onSessionTerminated(int)} with
+ * {@link WifiAwareDiscoverySessionCallback#TERMINATE_REASON_DONE}.
* <p>
* Optional. 0 by default - indicating the session doesn't terminate on its own.
- * Session will be terminated when {@link WifiNanDiscoveryBaseSession#destroy()} is
+ * Session will be terminated when {@link WifiAwareDiscoveryBaseSession#destroy()} is
* called.
*
* @param ttlSec Lifetime of a subscribe session in seconds.
@@ -401,8 +404,8 @@
* Sets the match style of the subscription - how are matches from a
* single match session (corresponding to the same publish action on the
* peer) reported to the host (using the
- * {@link WifiNanDiscoverySessionCallback#onServiceDiscovered(Object, byte[], byte[])}
- * ). The options are: only report the first match and ignore the rest
+ * {@link WifiAwareDiscoverySessionCallback#onServiceDiscovered(WifiAwareManager.PeerHandle,
+ * byte[], byte[])}). The options are: only report the first match and ignore the rest
* {@link SubscribeConfig#MATCH_STYLE_FIRST_ONLY} or report every single
* match {@link SubscribeConfig#MATCH_STYLE_ALL} (the default).
*
@@ -421,7 +424,7 @@
/**
* Configure whether a subscribe terminate notification
- * {@link WifiNanDiscoverySessionCallback#onSessionTerminated(int)} is reported
+ * {@link WifiAwareDiscoverySessionCallback#onSessionTerminated(int)} is reported
* back to the callback.
*
* @param enable If true the terminate callback will be called when the
diff --git a/wifi/java/android/net/wifi/nan/TlvBufferUtils.java b/wifi/java/android/net/wifi/aware/TlvBufferUtils.java
similarity index 92%
rename from wifi/java/android/net/wifi/nan/TlvBufferUtils.java
rename to wifi/java/android/net/wifi/aware/TlvBufferUtils.java
index 2c5aab4..29f10e9 100644
--- a/wifi/java/android/net/wifi/nan/TlvBufferUtils.java
+++ b/wifi/java/android/net/wifi/aware/TlvBufferUtils.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.net.wifi.nan;
+package android.net.wifi.aware;
import android.annotation.Nullable;
@@ -22,8 +22,10 @@
import java.nio.BufferOverflowException;
import java.nio.ByteOrder;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
+import java.util.List;
import java.util.NoSuchElementException;
/**
@@ -32,7 +34,7 @@
* the Type field and the Length field. A Type field size of 0 is allowed -
* allowing usage for LV (no T) array formats.
*
- * @hide PROPOSED_NAN_API
+ * @hide
*/
public class TlvBufferUtils {
private TlvBufferUtils() {
@@ -111,6 +113,31 @@
}
/**
+ * Creates a TLV array (of the previously specified Type and Length sizes) from the input
+ * list. Allocates an array matching the contents (and required Type and Length
+ * fields), copies the contents, and set the Length fields. The Type field is set to 0.
+ *
+ * @param list A list of fields to be added to the TLV buffer.
+ * @return The constructor of the TLV.
+ */
+ public TlvConstructor allocateAndPut(@Nullable List<byte[]> list) {
+ if (list != null) {
+ int size = 0;
+ for (byte[] field : list) {
+ size += mTypeSize + mLengthSize;
+ if (field != null) {
+ size += field.length;
+ }
+ }
+ allocate(size);
+ for (byte[] field : list) {
+ putByteArray(0, field);
+ }
+ }
+ return this;
+ }
+
+ /**
* Copies a byte into the TLV with the indicated type. For an LV
* formatted structure (i.e. typeLength=0 in {@link TlvConstructor
* TlvConstructor(int, int)} ) the type field is ignored.
@@ -319,6 +346,10 @@
this.length = length;
this.refArray = refArray;
this.offset = offset;
+
+ if (offset + length > refArray.length) {
+ throw new BufferOverflowException();
+ }
}
/**
@@ -393,7 +424,7 @@
* @param typeSize Number of bytes used for the Type (T) field. Valid
* values are 0 (i.e. indicating the format is LV rather than
* TLV), 1, and 2 bytes.
- * @param lengthSize Number of bytes sued for the Length (L) field.
+ * @param lengthSize Number of bytes used for the Length (L) field.
* Values values are 1 or 2 bytes.
* @param array The TLV formatted byte-array to parse.
*/
@@ -450,6 +481,18 @@
}
/**
+ * Returns a List with the raw contents (no types) of the iterator.
+ */
+ public List<byte[]> toList() {
+ List<byte[]> list = new ArrayList<>();
+ for (TlvElement tlv : this) {
+ list.add(Arrays.copyOfRange(tlv.refArray, tlv.offset, tlv.offset + tlv.length));
+ }
+
+ return list;
+ }
+
+ /**
* Returns an iterator to step through a TLV formatted byte-array. The
* individual elements returned by the iterator are {@link TlvElement}.
*/
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareAttachCallback.java b/wifi/java/android/net/wifi/aware/WifiAwareAttachCallback.java
new file mode 100644
index 0000000..1e8dbd9
--- /dev/null
+++ b/wifi/java/android/net/wifi/aware/WifiAwareAttachCallback.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.aware;
+
+/**
+ * Base class for Aware attach callbacks. Should be extended by applications and set when calling
+ * {@link WifiAwareManager#attach(WifiAwareAttachCallback, android.os.Handler)}. These are callbacks
+ * applying to the Aware connection as a whole - not to specific publish or subscribe sessions -
+ * for that see {@link WifiAwareDiscoverySessionCallback}.
+ *
+ * @hide PROPOSED_AWARE_API
+ */
+public class WifiAwareAttachCallback {
+ /**
+ * Called when Aware attach operation
+ * {@link WifiAwareManager#attach(WifiAwareAttachCallback, android.os.Handler)}
+ * is completed and that we can now start discovery sessions or connections.
+ *
+ * @param session The Aware object on which we can execute further Aware operations - e.g.
+ * discovery, connections.
+ */
+ public void onAttached(WifiAwareSession session) {
+ /* empty */
+ }
+
+ /**
+ * Called when Aware attach operation
+ * {@link WifiAwareManager#attach(WifiAwareAttachCallback, android.os.Handler)} failed.
+ */
+ public void onAttachFailed() {
+ /* empty */
+ }
+}
diff --git a/wifi/java/android/net/wifi/nan/PublishConfig.aidl b/wifi/java/android/net/wifi/aware/WifiAwareCharacteristics.aidl
similarity index 89%
copy from wifi/java/android/net/wifi/nan/PublishConfig.aidl
copy to wifi/java/android/net/wifi/aware/WifiAwareCharacteristics.aidl
index 5f66d16..a35e71d 100644
--- a/wifi/java/android/net/wifi/nan/PublishConfig.aidl
+++ b/wifi/java/android/net/wifi/aware/WifiAwareCharacteristics.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.net.wifi.nan;
+package android.net.wifi.aware;
-parcelable PublishConfig;
+parcelable WifiAwareCharacteristics;
diff --git a/wifi/java/android/net/wifi/nan/WifiNanCharacteristics.java b/wifi/java/android/net/wifi/aware/WifiAwareCharacteristics.java
similarity index 68%
rename from wifi/java/android/net/wifi/nan/WifiNanCharacteristics.java
rename to wifi/java/android/net/wifi/aware/WifiAwareCharacteristics.java
index f43ed4d..95d128d 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanCharacteristics.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareCharacteristics.java
@@ -14,18 +14,18 @@
* limitations under the License.
*/
-package android.net.wifi.nan;
+package android.net.wifi.aware;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
/**
- * The characteristics of the Wi-Fi NAN implementation.
+ * The characteristics of the Wi-Fi Aware implementation.
*
- * @hide PROPOSED_NAN_API
+ * @hide PROPOSED_AWARE_API
*/
-public class WifiNanCharacteristics implements Parcelable {
+public class WifiAwareCharacteristics implements Parcelable {
/** @hide */
public static final String KEY_MAX_SERVICE_NAME_LENGTH = "key_max_service_name_length";
/** @hide */
@@ -37,41 +37,43 @@
private Bundle mCharacteristics = new Bundle();
/** @hide : should not be created by apps */
- public WifiNanCharacteristics(Bundle characteristics) {
+ public WifiAwareCharacteristics(Bundle characteristics) {
mCharacteristics = characteristics;
}
/**
- * Returns the maximum string length that can be used to specify a NAN service name. Restricts
+ * Returns the maximum string length that can be used to specify a Aware service name. Restricts
* the parameters of the {@link PublishConfig.Builder#setServiceName(String)} and
* {@link SubscribeConfig.Builder#setServiceName(String)}.
*
- * @return A positive integer, maximum string length of NAN service name.
+ * @return A positive integer, maximum string length of Aware service name.
*/
public int getMaxServiceNameLength() {
return mCharacteristics.getInt(KEY_MAX_SERVICE_NAME_LENGTH);
}
/**
- * Returns the maximum length of byte array that can be used to specify a NAN service specific
- * information field: the arbitrary load used in discovery or the message length of NAN
+ * Returns the maximum length of byte array that can be used to specify a Aware service specific
+ * information field: the arbitrary load used in discovery or the message length of Aware
* message exchange. Restricts the parameters of the
* {@link PublishConfig.Builder#setServiceSpecificInfo(byte[])},
* {@link SubscribeConfig.Builder#setServiceSpecificInfo(byte[])}, and
- * {@link WifiNanDiscoveryBaseSession#sendMessage(Object, int, byte[])} variants.
+ * {@link WifiAwareDiscoveryBaseSession#sendMessage(WifiAwareManager.PeerHandle, int, byte[])}
+ * variants.
*
- * @return A positive integer, maximum length of byte array for NAN messaging.
+ * @return A positive integer, maximum length of byte array for Aware messaging.
*/
public int getMaxServiceSpecificInfoLength() {
return mCharacteristics.getInt(KEY_MAX_SERVICE_SPECIFIC_INFO_LENGTH);
}
/**
- * Returns the maximum length of byte array that can be used to specify a NAN match filter.
- * Restricts the parameters of the {@link PublishConfig.Builder#setMatchFilter(byte[])} and
- * {@link SubscribeConfig.Builder#setMatchFilter(byte[])}.
+ * Returns the maximum length of byte array that can be used to specify a Aware match filter.
+ * Restricts the parameters of the
+ * {@link PublishConfig.Builder#setMatchFilter(java.util.List<byte[]>)} and
+ * {@link SubscribeConfig.Builder#setMatchFilter(java.util.List<byte[]>)}.
*
- * @return A positive integer, maximum legngth of byte array for NAN discovery match filter.
+ * @return A positive integer, maximum legngth of byte array for Aware discovery match filter.
*/
public int getMaxMatchFilterLength() {
return mCharacteristics.getInt(KEY_MAX_MATCH_FILTER_LENGTH);
@@ -87,17 +89,17 @@
return 0;
}
- public static final Creator<WifiNanCharacteristics> CREATOR =
- new Creator<WifiNanCharacteristics>() {
+ public static final Creator<WifiAwareCharacteristics> CREATOR =
+ new Creator<WifiAwareCharacteristics>() {
@Override
- public WifiNanCharacteristics createFromParcel(Parcel in) {
- WifiNanCharacteristics c = new WifiNanCharacteristics(in.readBundle());
+ public WifiAwareCharacteristics createFromParcel(Parcel in) {
+ WifiAwareCharacteristics c = new WifiAwareCharacteristics(in.readBundle());
return c;
}
@Override
- public WifiNanCharacteristics[] newArray(int size) {
- return new WifiNanCharacteristics[size];
+ public WifiAwareCharacteristics[] newArray(int size) {
+ return new WifiAwareCharacteristics[size];
}
};
}
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareDiscoveryBaseSession.java b/wifi/java/android/net/wifi/aware/WifiAwareDiscoveryBaseSession.java
new file mode 100644
index 0000000..451d8a5
--- /dev/null
+++ b/wifi/java/android/net/wifi/aware/WifiAwareDiscoveryBaseSession.java
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.aware;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.net.wifi.RttManager;
+import android.util.Log;
+
+import dalvik.system.CloseGuard;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * A class representing a single publish or subscribe Aware session. This object
+ * will not be created directly - only its child classes are available:
+ * {@link WifiAwarePublishDiscoverySession} and {@link WifiAwareSubscribeDiscoverySession}. This
+ * class provides functionality common to both publish and subscribe discovery sessions:
+ * <ul>
+ * <li>Sending messages: {@link #sendMessage(WifiAwareManager.PeerHandle, int, byte[])} or
+ * {@link #sendMessage(WifiAwareManager.PeerHandle, int, byte[], int)} methods.
+ * <li>Creating a network-specifier when requesting a Aware connection:
+ * {@link #createNetworkSpecifier(WifiAwareManager.PeerHandle, byte[])}.
+ * </ul>
+ * The {@link #destroy()} method must be called to destroy discovery sessions once they are
+ * no longer needed.
+ *
+ * @hide PROPOSED_AWARE_API
+ */
+public class WifiAwareDiscoveryBaseSession {
+ private static final String TAG = "WifiAwareDiscBaseSsn";
+ private static final boolean DBG = false;
+ private static final boolean VDBG = false; // STOPSHIP if true
+
+ private static final int MAX_SEND_RETRY_COUNT = 5;
+
+ /** @hide */
+ protected WeakReference<WifiAwareManager> mMgr;
+ /** @hide */
+ protected final int mClientId;
+ /** @hide */
+ protected final int mSessionId;
+ /** @hide */
+ protected boolean mTerminated = false;
+
+ private final CloseGuard mCloseGuard = CloseGuard.get();
+
+ /**
+ * Return the maximum permitted retry count when sending messages using
+ * {@link #sendMessage(WifiAwareManager.PeerHandle, int, byte[], int)}.
+ *
+ * @return Maximum retry count when sending messages.
+ */
+ public static int getMaxSendRetryCount() {
+ return MAX_SEND_RETRY_COUNT;
+ }
+
+ /** @hide */
+ public WifiAwareDiscoveryBaseSession(WifiAwareManager manager, int clientId, int sessionId) {
+ if (VDBG) {
+ Log.v(TAG, "New discovery session created: manager=" + manager + ", clientId="
+ + clientId + ", sessionId=" + sessionId);
+ }
+
+ mMgr = new WeakReference<>(manager);
+ mClientId = clientId;
+ mSessionId = sessionId;
+
+ mCloseGuard.open("destroy");
+ }
+
+ /**
+ * Destroy the publish or subscribe session - free any resources, and stop
+ * transmitting packets on-air (for an active session) or listening for
+ * matches (for a passive session). The session may not be used for any
+ * additional operations after its destruction.
+ * <p>
+ * This operation must be done on a session which is no longer needed. Otherwise system
+ * resources will continue to be utilized until the application exits. The only
+ * exception is a session for which we received a termination callback,
+ * {@link WifiAwareDiscoverySessionCallback#onSessionTerminated(int)}.
+ */
+ public void destroy() {
+ WifiAwareManager mgr = mMgr.get();
+ if (mgr == null) {
+ Log.w(TAG, "destroy: called post GC on WifiAwareManager");
+ return;
+ }
+ mgr.terminateSession(mClientId, mSessionId);
+ mTerminated = true;
+ mMgr.clear();
+ mCloseGuard.close();
+ }
+
+ /**
+ * Sets the status of the session to terminated - i.e. an indication that
+ * already terminated rather than executing a termination.
+ *
+ * @hide
+ */
+ public void setTerminated() {
+ if (mTerminated) {
+ Log.w(TAG, "terminate: already terminated.");
+ return;
+ }
+ mTerminated = true;
+ mMgr.clear();
+ mCloseGuard.close();
+ }
+
+ /** @hide */
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ if (!mTerminated) {
+ mCloseGuard.warnIfOpen();
+ destroy();
+ }
+ } finally {
+ super.finalize();
+ }
+ }
+
+ /**
+ * Sends a message to the specified destination. Aware messages are transmitted in the context
+ * of a discovery session - executed subsequent to a publish/subscribe
+ * {@link WifiAwareDiscoverySessionCallback#onServiceDiscovered(WifiAwareManager.PeerHandle,
+ * byte[], java.util.List<byte[]>)} event.
+ * <p>
+ * Aware messages are not guaranteed delivery. Callbacks on
+ * {@link WifiAwareDiscoverySessionCallback} indicate message was transmitted successfully,
+ * {@link WifiAwareDiscoverySessionCallback#onMessageSendSucceeded(int)}, or transmission
+ * failed (possibly after several retries) -
+ * {@link WifiAwareDiscoverySessionCallback#onMessageSendFailed(int)}.
+ * <p>
+ * The peer will get a callback indicating a message was received using
+ * {@link WifiAwareDiscoverySessionCallback#onMessageReceived(WifiAwareManager.PeerHandle,
+ * byte[])}.
+ *
+ * @param peerHandle The peer's handle for the message. Must be a result of an
+ * {@link WifiAwareDiscoverySessionCallback#onServiceDiscovered(WifiAwareManager.PeerHandle,
+ * byte[], java.util.List<byte[]>)} or
+ * {@link WifiAwareDiscoverySessionCallback#onMessageReceived(WifiAwareManager.PeerHandle,
+ * byte[])} events.
+ * @param messageId An arbitrary integer used by the caller to identify the message. The same
+ * integer ID will be returned in the callbacks indicating message send success or
+ * failure. The {@code messageId} is not used internally by the Aware service - it
+ * can be arbitrary and non-unique.
+ * @param message The message to be transmitted.
+ * @param retryCount An integer specifying how many additional service-level (as opposed to PHY
+ * or MAC level) retries should be attempted if there is no ACK from the receiver
+ * (note: no retransmissions are attempted in other failure cases). A value of 0
+ * indicates no retries. Max permitted value is {@link #getMaxSendRetryCount()}.
+ */
+ public void sendMessage(@NonNull WifiAwareManager.PeerHandle peerHandle, int messageId,
+ @Nullable byte[] message, int retryCount) {
+ if (mTerminated) {
+ Log.w(TAG, "sendMessage: called on terminated session");
+ return;
+ } else {
+ WifiAwareManager mgr = mMgr.get();
+ if (mgr == null) {
+ Log.w(TAG, "sendMessage: called post GC on WifiAwareManager");
+ return;
+ }
+
+ mgr.sendMessage(mClientId, mSessionId, peerHandle, message, messageId, retryCount);
+ }
+ }
+
+ /**
+ * Sends a message to the specified destination. Aware messages are transmitted in the context
+ * of a discovery session - executed subsequent to a publish/subscribe
+ * {@link WifiAwareDiscoverySessionCallback#onServiceDiscovered(WifiAwareManager.PeerHandle,
+ * byte[], java.util.List<byte[]>)} event.
+ * <p>
+ * Aware messages are not guaranteed delivery. Callbacks on
+ * {@link WifiAwareDiscoverySessionCallback} indicate message was transmitted successfully,
+ * {@link WifiAwareDiscoverySessionCallback#onMessageSendSucceeded(int)}, or transmission
+ * failed (possibly after several retries) -
+ * {@link WifiAwareDiscoverySessionCallback#onMessageSendFailed(int)}.
+ * <p>
+ * The peer will get a callback indicating a message was received using
+ * {@link WifiAwareDiscoverySessionCallback#onMessageReceived(WifiAwareManager.PeerHandle,
+ * byte[])}.
+ * Equivalent to {@link #sendMessage(WifiAwareManager.PeerHandle, int, byte[], int)}
+ * with a {@code retryCount} of 0.
+ *
+ * @param peerHandle The peer's handle for the message. Must be a result of an
+ * {@link WifiAwareDiscoverySessionCallback#onServiceDiscovered(WifiAwareManager.PeerHandle,
+ * byte[], java.util.List<byte[]>)} or
+ * {@link WifiAwareDiscoverySessionCallback#onMessageReceived(WifiAwareManager.PeerHandle,
+ * byte[])} events.
+ * @param messageId An arbitrary integer used by the caller to identify the message. The same
+ * integer ID will be returned in the callbacks indicating message send success or
+ * failure. The {@code messageId} is not used internally by the Aware service - it
+ * can be arbitrary and non-unique.
+ * @param message The message to be transmitted.
+ */
+ public void sendMessage(@NonNull WifiAwareManager.PeerHandle peerHandle, int messageId,
+ @Nullable byte[] message) {
+ sendMessage(peerHandle, messageId, message, 0);
+ }
+
+ /**
+ * Start a ranging operation with the specified peers. The peer IDs are obtained from an
+ * {@link WifiAwareDiscoverySessionCallback#onServiceDiscovered(WifiAwareManager.PeerHandle,
+ * byte[], java.util.List<byte[]>)} or
+ * {@link WifiAwareDiscoverySessionCallback#onMessageReceived(WifiAwareManager.PeerHandle,
+ * byte[])} operation - can
+ * only range devices which are part of an ongoing discovery session.
+ *
+ * @param params RTT parameters - each corresponding to a specific peer ID (the array sizes
+ * must be identical). The
+ * {@link android.net.wifi.RttManager.RttParams#bssid} member must be set to
+ * a peer ID - not to a MAC address.
+ * @param listener The listener to receive the results of the ranging session.
+ * @hide PROPOSED_AWARE_SYSTEM_API
+ * [TODO: b/28847998 - track RTT API & visilibity]
+ */
+ public void startRanging(RttManager.RttParams[] params, RttManager.RttListener listener) {
+ if (mTerminated) {
+ Log.w(TAG, "startRanging: called on terminated session");
+ return;
+ } else {
+ WifiAwareManager mgr = mMgr.get();
+ if (mgr == null) {
+ Log.w(TAG, "startRanging: called post GC on WifiAwareManager");
+ return;
+ }
+
+ mgr.startRanging(mClientId, mSessionId, params, listener);
+ }
+ }
+
+ /**
+ * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for a
+ * WiFi Aware connection to the specified peer. The
+ * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to
+ * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}.
+ * <p>
+ * This method should be used when setting up a connection with a peer discovered through Aware
+ * discovery or communication (in such scenarios the MAC address of the peer is shielded by
+ * an opaque peer ID handle). If a Aware connection is needed to a peer discovered using other
+ * OOB (out-of-band) mechanism then use the alternative
+ * {@link WifiAwareSession#createNetworkSpecifier(int, byte[], byte[])} method - which uses the
+ * peer's MAC address.
+ * <p>
+ * Note: per the Wi-Fi Aware specification the roles are fixed - a Subscriber is an INITIATOR
+ * and a Publisher is a RESPONDER.
+ *
+ * @param peerHandle The peer's handle obtained through
+ * {@link WifiAwareDiscoverySessionCallback#onServiceDiscovered(WifiAwareManager.PeerHandle,
+ * byte[], java.util.List<byte[]>)} or
+ * {@link WifiAwareDiscoverySessionCallback#onMessageReceived(WifiAwareManager.PeerHandle,
+ * byte[])}. On a RESPONDER this value is used to gate the acceptance of a connection request
+ * from only that peer. A RESPONDER may specified a null - indicating that
+ * it will accept connection requests from any device.
+ * @param token An arbitrary token (message) to be used to match connection initiation request
+ * to a responder setup. A RESPONDER is set up with a {@code token} which must
+ * be matched by the token provided by the INITIATOR. A null token is permitted
+ * on the RESPONDER and matches any peer token. An empty ({@code ""}) token is
+ * not the same as a null token and requires the peer token to be empty as well.
+ *
+ * @return A string to be used to construct
+ * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to
+ * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest,
+ * android.net.ConnectivityManager.NetworkCallback)}
+ * [or other varieties of that API].
+ */
+ public String createNetworkSpecifier(@Nullable WifiAwareManager.PeerHandle peerHandle,
+ @Nullable byte[] token) {
+ if (mTerminated) {
+ Log.w(TAG, "createNetworkSpecifier: called on terminated session");
+ return null;
+ } else {
+ WifiAwareManager mgr = mMgr.get();
+ if (mgr == null) {
+ Log.w(TAG, "createNetworkSpecifier: called post GC on WifiAwareManager");
+ return null;
+ }
+
+ int role = this instanceof WifiAwareSubscribeDiscoverySession
+ ? WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
+ : WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER;
+
+ return mgr.createNetworkSpecifier(mClientId, role, mSessionId, peerHandle, token);
+ }
+ }
+}
diff --git a/wifi/java/android/net/wifi/aware/WifiAwareDiscoverySessionCallback.java b/wifi/java/android/net/wifi/aware/WifiAwareDiscoverySessionCallback.java
new file mode 100644
index 0000000..fdf0d01
--- /dev/null
+++ b/wifi/java/android/net/wifi/aware/WifiAwareDiscoverySessionCallback.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.aware;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+
+/**
+ * Base class for Aware session events callbacks. Should be extended by
+ * applications wanting notifications. The callbacks are set when a
+ * publish or subscribe session is created using
+ * {@link WifiAwareSession#publish(PublishConfig, WifiAwareDiscoverySessionCallback,
+ * android.os.Handler)} or
+ * {@link WifiAwareSession#subscribe(SubscribeConfig, WifiAwareDiscoverySessionCallback,
+ * android.os.Handler)}.
+ * <p>
+ * A single callback is set at session creation - it cannot be replaced.
+ *
+ * @hide PROPOSED_AWARE_API
+ */
+public class WifiAwareDiscoverySessionCallback {
+ /** @hide */
+ @IntDef({
+ TERMINATE_REASON_DONE, TERMINATE_REASON_FAIL })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SessionTerminateCodes {
+ }
+
+ /**
+ * Indicates that publish or subscribe session is done - all the
+ * requested operations (per {@link PublishConfig} or
+ * {@link SubscribeConfig}) have been executed. Failure reason flag for
+ * {@link WifiAwareDiscoverySessionCallback#onSessionTerminated(int)} callback.
+ */
+ public static final int TERMINATE_REASON_DONE = 100;
+
+ /**
+ * Indicates that publish or subscribe session is terminated due to a
+ * failure.
+ * Failure reason flag for
+ * {@link WifiAwareDiscoverySessionCallback#onSessionTerminated(int)} callback.
+ */
+ public static final int TERMINATE_REASON_FAIL = 101;
+
+ /**
+ * Called when a publish operation is started successfully in response to a
+ * {@link WifiAwareSession#publish(PublishConfig, WifiAwareDiscoverySessionCallback,
+ * android.os.Handler)} operation.
+ *
+ * @param session The {@link WifiAwarePublishDiscoverySession} used to control the
+ * discovery session.
+ */
+ public void onPublishStarted(@NonNull WifiAwarePublishDiscoverySession session) {
+ /* empty */
+ }
+
+ /**
+ * Called when a subscribe operation is started successfully in response to a
+ * {@link WifiAwareSession#subscribe(SubscribeConfig, WifiAwareDiscoverySessionCallback,
+ * android.os.Handler)} operation.
+ *
+ * @param session The {@link WifiAwareSubscribeDiscoverySession} used to control the
+ * discovery session.
+ */
+ public void onSubscribeStarted(@NonNull WifiAwareSubscribeDiscoverySession session) {
+ /* empty */
+ }
+
+ /**
+ * Called when a publish or subscribe discovery session configuration update request
+ * succeeds. Called in response to
+ * {@link WifiAwarePublishDiscoverySession#updatePublish(PublishConfig)} or
+ * {@link WifiAwareSubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}.
+ */
+ public void onSessionConfigUpdated() {
+ /* empty */
+ }
+
+ /**
+ * Called when a publish or subscribe discovery session cannot be created:
+ * {@link WifiAwareSession#publish(PublishConfig, WifiAwareDiscoverySessionCallback,
+ * android.os.Handler)} or
+ * {@link WifiAwareSession#subscribe(SubscribeConfig, WifiAwareDiscoverySessionCallback,
+ * android.os.Handler)}, or when a configuration update fails:
+ * {@link WifiAwarePublishDiscoverySession#updatePublish(PublishConfig)} or
+ * {@link WifiAwareSubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}.
+ * <p>
+ * For discovery session updates failure leaves the session running with its previous
+ * configuration - the discovery session is not terminated.
+ */
+ public void onSessionConfigFailed() {
+ /* empty */
+ }
+
+ /**
+ * Called when a discovery session (publish or subscribe) terminates. Termination may be due
+ * to user-request (either directly through {@link WifiAwareDiscoveryBaseSession#destroy()} or
+ * application-specified expiration, e.g. {@link PublishConfig.Builder#setPublishCount(int)}
+ * or {@link SubscribeConfig.Builder#setTtlSec(int)}) or due to a failure.
+ *
+ * @param reason The termination reason using
+ * {@code WifiAwareDiscoverySessionCallback.TERMINATE_*} codes.
+ */
+ public void onSessionTerminated(@SessionTerminateCodes int reason) {
+ /* empty */
+ }
+
+ /**
+ * Called when a discovery (publish or subscribe) operation results in a
+ * service discovery.
+ *
+ * @param peerHandle An opaque handle to the peer matching our discovery operation.
+ * @param serviceSpecificInfo The service specific information (arbitrary
+ * byte array) provided by the peer as part of its discovery
+ * configuration.
+ * @param matchFilter The filter which resulted in this service discovery.
+ */
+ public void onServiceDiscovered(WifiAwareManager.PeerHandle peerHandle,
+ byte[] serviceSpecificInfo, List<byte[]> matchFilter) {
+ /* empty */
+ }
+
+ /**
+ * Called in response to
+ * {@link WifiAwareDiscoveryBaseSession#sendMessage(WifiAwareManager.PeerHandle, int, byte[])}
+ * when a message is transmitted successfully - i.e. when it was received successfully by the
+ * peer (corresponds to an ACK being received).
+ * <p>
+ * Note that either this callback or
+ * {@link WifiAwareDiscoverySessionCallback#onMessageSendFailed(int)} will be
+ * received - never both.
+ *
+ * @param messageId The arbitrary message ID specified when sending the message.
+ */
+ public void onMessageSendSucceeded(@SuppressWarnings("unused") int messageId) {
+ /* empty */
+ }
+
+ /**
+ * Called when message transmission fails - when no ACK is received from the peer.
+ * Retries when ACKs are not received are done by hardware, MAC, and in the Aware stack (using
+ * the {@link WifiAwareDiscoveryBaseSession#sendMessage(WifiAwareManager.PeerHandle, int,
+ * byte[], int)} method) - this event is received after all retries are exhausted.
+ * <p>
+ * Note that either this callback or
+ * {@link WifiAwareDiscoverySessionCallback#onMessageSendSucceeded(int)} will be received
+ * - never both.
+ *
+ * @param messageId The arbitrary message ID specified when sending the message.
+ */
+ public void onMessageSendFailed(@SuppressWarnings("unused") int messageId) {
+ /* empty */
+ }
+
+ /**
+ * Called when a message is received from a discovery session peer - in response to the
+ * peer's {@link WifiAwareDiscoveryBaseSession#sendMessage(WifiAwareManager.PeerHandle, int,
+ * byte[])} or {@link WifiAwareDiscoveryBaseSession#sendMessage(WifiAwareManager.PeerHandle,
+ * int, byte[], int)}.
+ *
+ * @param peerHandle An opaque handle to the peer matching our discovery operation.
+ * @param message A byte array containing the message.
+ */
+ public void onMessageReceived(WifiAwareManager.PeerHandle peerHandle, byte[] message) {
+ /* empty */
+ }
+}
diff --git a/wifi/java/android/net/wifi/nan/WifiNanIdentityChangedListener.java b/wifi/java/android/net/wifi/aware/WifiAwareIdentityChangedListener.java
similarity index 75%
rename from wifi/java/android/net/wifi/nan/WifiNanIdentityChangedListener.java
rename to wifi/java/android/net/wifi/aware/WifiAwareIdentityChangedListener.java
index 7cb928f..e8f52cd4 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanIdentityChangedListener.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareIdentityChangedListener.java
@@ -14,23 +14,23 @@
* limitations under the License.
*/
-package android.net.wifi.nan;
+package android.net.wifi.aware;
/**
- * Base class for a listener which is called with the MAC address of the NAN interface whenever
+ * Base class for a listener which is called with the MAC address of the Aware interface whenever
* it is changed. Change may be due to device joining a cluster, starting a cluster, or discovery
* interface change (addresses are randomized at regular intervals). The implication is that
* peers you've been communicating with may no longer recognize you and you need to re-establish
* your identity - e.g. by starting a discovery session. This actual MAC address of the
- * interface may also be useful if the application uses alternative (non-NAN) discovery but needs
- * to set up a NAN connection. The provided NAN discovery interface MAC address can then be used
- * in {@link WifiNanSession#createNetworkSpecifier(int, byte[], byte[])}.
+ * interface may also be useful if the application uses alternative (non-Aware) discovery but needs
+ * to set up a Aware connection. The provided Aware discovery interface MAC address can then be used
+ * in {@link WifiAwareSession#createNetworkSpecifier(int, byte[], byte[])}.
*
- * @hide PROPOSED_NAN_API
+ * @hide PROPOSED_AWARE_API
*/
-public class WifiNanIdentityChangedListener {
+public class WifiAwareIdentityChangedListener {
/**
- * @param mac The MAC address of the NAN discovery interface. The application must have the
+ * @param mac The MAC address of the Aware discovery interface. The application must have the
* {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} to get the actual MAC address,
* otherwise all 0's will be provided.
*/
diff --git a/wifi/java/android/net/wifi/nan/WifiNanManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
similarity index 72%
rename from wifi/java/android/net/wifi/nan/WifiNanManager.java
rename to wifi/java/android/net/wifi/aware/WifiAwareManager.java
index 002b953..029794d 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanManager.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.net.wifi.nan;
+package android.net.wifi.aware;
import android.annotation.IntDef;
import android.annotation.NonNull;
@@ -45,83 +45,90 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
+import java.nio.BufferOverflowException;
import java.util.Arrays;
+import java.util.List;
/**
- * This class provides the primary API for managing Wi-Fi NAN operations:
+ * This class provides the primary API for managing Wi-Fi Aware operations:
* discovery and peer-to-peer data connections. Get an instance of this class by calling
* {@link android.content.Context#getSystemService(String)
- * Context.getSystemService(Context.WIFI_NAN_SERVICE)}.
+ * Context.getSystemService(Context.WIFI_AWARE_SERVICE)}.
* <p>
* The class provides access to:
* <ul>
- * <li>Initialize a NAN cluster (peer-to-peer synchronization). Refer to
- * {@link #attach(Handler, WifiNanAttachCallback)}.
+ * <li>Initialize a Aware cluster (peer-to-peer synchronization). Refer to
+ * {@link #attach(WifiAwareAttachCallback, Handler)}.
* <li>Create discovery sessions (publish or subscribe sessions). Refer to
- * {@link WifiNanSession#publish(Handler, PublishConfig, WifiNanDiscoverySessionCallback)} and
- * {@link WifiNanSession#subscribe(Handler, SubscribeConfig, WifiNanDiscoverySessionCallback)}.
- * <li>Create a NAN network specifier to be used with
+ * {@link WifiAwareSession#publish(PublishConfig, WifiAwareDiscoverySessionCallback, Handler)} and
+ * {@link WifiAwareSession#subscribe(SubscribeConfig, WifiAwareDiscoverySessionCallback, Handler)}.
+ * <li>Create a Aware network specifier to be used with
* {@link ConnectivityManager#requestNetwork(NetworkRequest, ConnectivityManager.NetworkCallback)}
- * to set-up a NAN connection with a peer. Refer to
- * {@link WifiNanDiscoveryBaseSession#createNetworkSpecifier(int, Object, byte[])} and
- * {@link WifiNanSession#createNetworkSpecifier(int, byte[], byte[])}.
+ * to set-up a Aware connection with a peer. Refer to
+ * {@link WifiAwareDiscoveryBaseSession#createNetworkSpecifier(PeerHandle, byte[])} and
+ * {@link WifiAwareSession#createNetworkSpecifier(int, byte[], byte[])}.
* </ul>
* <p>
- * NAN may not be usable when Wi-Fi is disabled (and other conditions). To validate that
+ * Aware may not be usable when Wi-Fi is disabled (and other conditions). To validate that
* the functionality is available use the {@link #isAvailable()} function. To track
- * changes in NAN usability register for the {@link #ACTION_WIFI_NAN_STATE_CHANGED} broadcast.
- * Note that this broadcast is not sticky - you should register for it and then check the
- * above API to avoid a race condition.
+ * changes in Aware usability register for the {@link #ACTION_WIFI_AWARE_STATE_CHANGED}
+ * broadcast. Note that this broadcast is not sticky - you should register for it and then
+ * check the above API to avoid a race condition.
* <p>
- * An application must use {@link #attach(Handler, WifiNanAttachCallback)} to initialize a NAN
- * cluster - before making any other NAN operation. NAN cluster membership is a device-wide
- * operation - the API guarantees that the device is in a cluster or joins a NAN cluster (or
- * starts one if none can be found). Information about attach success (or failure) are
- * returned in callbacks of {@link WifiNanAttachCallback}. Proceed with NAN discovery or
- * connection setup only after receiving confirmation that NAN attach succeeded -
- * {@link WifiNanAttachCallback#onAttached(WifiNanSession)}. When an application is
- * finished using NAN it <b>must</b> use the {@link WifiNanSession#destroy()} API
- * to indicate to the NAN service that the device may detach from the NAN cluster. The
- * device will actually disable NAN once the last application detaches.
+ * An application must use {@link #attach(WifiAwareAttachCallback, Handler)} to initialize a
+ * Aware cluster - before making any other Aware operation. Aware cluster membership is a
+ * device-wide operation - the API guarantees that the device is in a cluster or joins a
+ * Aware cluster (or starts one if none can be found). Information about attach success (or
+ * failure) are returned in callbacks of {@link WifiAwareAttachCallback}. Proceed with Aware
+ * discovery or connection setup only after receiving confirmation that Aware attach
+ * succeeded - {@link WifiAwareAttachCallback#onAttached(WifiAwareSession)}. When an
+ * application is finished using Aware it <b>must</b> use the
+ * {@link WifiAwareSession#destroy()} API to indicate to the Aware service that the device
+ * may detach from the Aware cluster. The device will actually disable Aware once the last
+ * application detaches.
* <p>
- * Once a NAN attach is confirmed use the
- * {@link WifiNanSession#publish(Handler, PublishConfig, WifiNanDiscoverySessionCallback)} or
- * {@link WifiNanSession#subscribe(Handler, SubscribeConfig, WifiNanDiscoverySessionCallback)}
- * to create publish or subscribe NAN discovery sessions. Events are called on the provided
- * callback object {@link WifiNanDiscoverySessionCallback}. Specifically, the
- * {@link WifiNanDiscoverySessionCallback#onPublishStarted(WifiNanPublishDiscoverySession)}
+ * Once a Aware attach is confirmed use the
+ * {@link WifiAwareSession#publish(PublishConfig, WifiAwareDiscoverySessionCallback, Handler)}
+ * or
+ * {@link WifiAwareSession#subscribe(SubscribeConfig, WifiAwareDiscoverySessionCallback,
+ * Handler)} to create publish or subscribe Aware discovery sessions. Events are called on the
+ * provided callback object {@link WifiAwareDiscoverySessionCallback}. Specifically, the
+ * {@link WifiAwareDiscoverySessionCallback#onPublishStarted(WifiAwarePublishDiscoverySession)}
* and
- * {@link WifiNanDiscoverySessionCallback#onSubscribeStarted(WifiNanSubscribeDiscoverySession)}
- * return {@link WifiNanPublishDiscoverySession} and {@link WifiNanSubscribeDiscoverySession}
+ * {@link WifiAwareDiscoverySessionCallback#onSubscribeStarted(
+ * WifiAwareSubscribeDiscoverySession)}
+ * return {@link WifiAwarePublishDiscoverySession} and
+ * {@link WifiAwareSubscribeDiscoverySession}
* objects respectively on which additional session operations can be performed, e.g. updating
- * the session {@link WifiNanPublishDiscoverySession#updatePublish(PublishConfig)} and
- * {@link WifiNanSubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}. Sessions can also
- * be used to send messages using the
- * {@link WifiNanDiscoveryBaseSession#sendMessage(Object, int, byte[])} APIs. When an
+ * the session {@link WifiAwarePublishDiscoverySession#updatePublish(PublishConfig)} and
+ * {@link WifiAwareSubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}. Sessions can
+ * also be used to send messages using the
+ * {@link WifiAwareDiscoveryBaseSession#sendMessage(PeerHandle, int, byte[])} APIs. When an
* application is finished with a discovery session it <b>must</b> terminate it using the
- * {@link WifiNanDiscoveryBaseSession#destroy()} API.
+ * {@link WifiAwareDiscoveryBaseSession#destroy()} API.
* <p>
- * Creating connections between NAN devices is managed by the standard
- * {@link ConnectivityManager#requestNetwork(NetworkRequest, ConnectivityManager.NetworkCallback)}.
+ * Creating connections between Aware devices is managed by the standard
+ * {@link ConnectivityManager#requestNetwork(NetworkRequest,
+ * ConnectivityManager.NetworkCallback)}.
* The {@link NetworkRequest} object should be constructed with:
* <ul>
* <li>{@link NetworkRequest.Builder#addTransportType(int)} of
- * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_NAN}.
+ * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}.
* <li>{@link NetworkRequest.Builder#setNetworkSpecifier(String)} using
- * {@link WifiNanSession#createNetworkSpecifier(int, byte[], byte[])} or
- * {@link WifiNanDiscoveryBaseSession#createNetworkSpecifier(int, Object, byte[])}.
+ * {@link WifiAwareSession#createNetworkSpecifier(int, byte[], byte[])} or
+ * {@link WifiAwareDiscoveryBaseSession#createNetworkSpecifier(PeerHandle, byte[])}.
* </ul>
*
- * @hide PROPOSED_NAN_API
+ * @hide PROPOSED_AWARE_API
*/
-public class WifiNanManager {
- private static final String TAG = "WifiNanManager";
+public class WifiAwareManager {
+ private static final String TAG = "WifiAwareManager";
private static final boolean DBG = false;
private static final boolean VDBG = false; // STOPSHIP if true
/**
- * Keys used to generate a Network Specifier for the NAN network request. The network specifier
- * is formatted as a JSON string.
+ * Keys used to generate a Network Specifier for the Aware network request. The network
+ * specifier is formatted as a JSON string.
*/
/**
@@ -197,44 +204,44 @@
public static final String NETWORK_SPECIFIER_KEY_TOKEN = "token";
/**
- * Broadcast intent action to indicate that the state of Wi-Fi NAN availability has changed.
+ * Broadcast intent action to indicate that the state of Wi-Fi Aware availability has changed.
* Use the {@link #isAvailable()} to query the current status.
* This broadcast is <b>not</b> sticky, use the {@link #isAvailable()} API after registering
- * the broadcast to check the current state of Wi-Fi NAN.
+ * the broadcast to check the current state of Wi-Fi Aware.
* <p>Note: The broadcast is only delivered to registered receivers - no manifest registered
* components will be launched.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String ACTION_WIFI_NAN_STATE_CHANGED =
- "android.net.wifi.nan.action.WIFI_NAN_STATE_CHANGED";
+ public static final String ACTION_WIFI_AWARE_STATE_CHANGED =
+ "android.net.wifi.aware.action.WIFI_AWARE_STATE_CHANGED";
/** @hide */
@IntDef({
- WIFI_NAN_DATA_PATH_ROLE_INITIATOR, WIFI_NAN_DATA_PATH_ROLE_RESPONDER})
+ WIFI_AWARE_DATA_PATH_ROLE_INITIATOR, WIFI_AWARE_DATA_PATH_ROLE_RESPONDER})
@Retention(RetentionPolicy.SOURCE)
public @interface DataPathRole {
}
/**
* Connection creation role is that of INITIATOR. Used to create a network specifier string
- * when requesting a NAN network.
+ * when requesting a Aware network.
*
- * @see WifiNanDiscoveryBaseSession#createNetworkSpecifier(int, Object, byte[])
- * @see WifiNanSession#createNetworkSpecifier(int, byte[], byte[])
+ * @see WifiAwareDiscoveryBaseSession#createNetworkSpecifier(PeerHandle, byte[])
+ * @see WifiAwareSession#createNetworkSpecifier(int, byte[], byte[])
*/
- public static final int WIFI_NAN_DATA_PATH_ROLE_INITIATOR = 0;
+ public static final int WIFI_AWARE_DATA_PATH_ROLE_INITIATOR = 0;
/**
* Connection creation role is that of RESPONDER. Used to create a network specifier string
- * when requesting a NAN network.
+ * when requesting a Aware network.
*
- * @see WifiNanDiscoveryBaseSession#createNetworkSpecifier(int, Object, byte[])
- * @see WifiNanSession#createNetworkSpecifier(int, byte[], byte[])
+ * @see WifiAwareDiscoveryBaseSession#createNetworkSpecifier(PeerHandle, byte[])
+ * @see WifiAwareSession#createNetworkSpecifier(int, byte[], byte[])
*/
- public static final int WIFI_NAN_DATA_PATH_ROLE_RESPONDER = 1;
+ public static final int WIFI_AWARE_DATA_PATH_ROLE_RESPONDER = 1;
private final Context mContext;
- private final IWifiNanManager mService;
+ private final IWifiAwareManager mService;
private final Object mLock = new Object(); // lock access to the following vars
@@ -242,14 +249,14 @@
private SparseArray<RttManager.RttListener> mRangingListeners = new SparseArray<>();
/** @hide */
- public WifiNanManager(Context context, IWifiNanManager service) {
+ public WifiAwareManager(Context context, IWifiAwareManager service) {
mContext = context;
mService = service;
}
/**
- * Enable the usage of the NAN API. Doesn't actually turn on NAN cluster formation - that
- * only happens when an attach is attempted. {@link #ACTION_WIFI_NAN_STATE_CHANGED} broadcast
+ * Enable the usage of the Aware API. Doesn't actually turn on Aware cluster formation - that
+ * only happens when an attach is attempted. {@link #ACTION_WIFI_AWARE_STATE_CHANGED} broadcast
* will be triggered.
*
* @hide
@@ -263,9 +270,9 @@
}
/**
- * Disable the usage of the NAN API. All attempts to attach() will be rejected. All open
- * connections and sessions will be terminated. {@link #ACTION_WIFI_NAN_STATE_CHANGED} broadcast
- * will be triggered.
+ * Disable the usage of the Aware API. All attempts to attach() will be rejected. All open
+ * connections and sessions will be terminated. {@link #ACTION_WIFI_AWARE_STATE_CHANGED}
+ * broadcast will be triggered.
*
* @hide
*/
@@ -278,10 +285,11 @@
}
/**
- * Returns the current status of NAN API: whether or not NAN is available. To track changes
- * in the state of NAN API register for the {@link #ACTION_WIFI_NAN_STATE_CHANGED} broadcast.
+ * Returns the current status of Aware API: whether or not Aware is available. To track
+ * changes in the state of Aware API register for the
+ * {@link #ACTION_WIFI_AWARE_STATE_CHANGED} broadcast.
*
- * @return A boolean indicating whether the app can use the NAN API at this time (true) or
+ * @return A boolean indicating whether the app can use the Aware API at this time (true) or
* not (false).
*/
public boolean isAvailable() {
@@ -293,12 +301,13 @@
}
/**
- * Returns the characteristics of the Wi-Fi NAN interface: a set of parameters which specify
+ * Returns the characteristics of the Wi-Fi Aware interface: a set of parameters which specify
* limitations on configurations, e.g. the maximum service name length.
*
- * @return An object specifying configuration limitations of NAN.
+ * @return An object specifying configuration limitations of Aware.
+ * @hide PROPOSED_AWARE_API
*/
- public WifiNanCharacteristics getCharacteristics() {
+ public WifiAwareCharacteristics getCharacteristics() {
try {
return mService.getCharacteristics();
} catch (RemoteException e) {
@@ -307,64 +316,65 @@
}
/**
- * Attach to the Wi-Fi NAN service - enabling the application to create discovery sessions or
+ * Attach to the Wi-Fi Aware service - enabling the application to create discovery sessions or
* create connections to peers. The device will attach to an existing cluster if it can find
- * one or create a new cluster (if it is the first to enable NAN in its vicinity). Results
+ * one or create a new cluster (if it is the first to enable Aware in its vicinity). Results
* (e.g. successful attach to a cluster) are provided to the {@code attachCallback} object.
- * An application <b>must</b> call {@link WifiNanSession#destroy()} when done with the
- * Wi-Fi NAN object.
+ * An application <b>must</b> call {@link WifiAwareSession#destroy()} when done with the
+ * Wi-Fi Aware object.
* <p>
- * Note: a NAN cluster is a shared resource - if the device is already attached to a cluster
+ * Note: a Aware cluster is a shared resource - if the device is already attached to a cluster
* then this function will simply indicate success immediately using the same {@code
* attachCallback}.
*
+ * @param attachCallback A callback for attach events, extended from
+ * {@link WifiAwareAttachCallback}.
* @param handler The Handler on whose thread to execute the callbacks of the {@code
* attachCallback} object. If a null is provided then the application's main thread will be
* used.
- * @param attachCallback A callback for attach events, extended from
- * {@link WifiNanAttachCallback}.
*/
- public void attach(@Nullable Handler handler, @NonNull WifiNanAttachCallback attachCallback) {
+ public void attach(@NonNull WifiAwareAttachCallback attachCallback, @Nullable Handler handler) {
attach(handler, null, attachCallback, null);
}
/**
- * Attach to the Wi-Fi NAN service - enabling the application to create discovery sessions or
+ * Attach to the Wi-Fi Aware service - enabling the application to create discovery sessions or
* create connections to peers. The device will attach to an existing cluster if it can find
- * one or create a new cluster (if it is the first to enable NAN in its vicinity). Results
+ * one or create a new cluster (if it is the first to enable Aware in its vicinity). Results
* (e.g. successful attach to a cluster) are provided to the {@code attachCallback} object.
- * An application <b>must</b> call {@link WifiNanSession#destroy()} when done with the
- * Wi-Fi NAN object.
+ * An application <b>must</b> call {@link WifiAwareSession#destroy()} when done with the
+ * Wi-Fi Aware object.
* <p>
- * Note: a NAN cluster is a shared resource - if the device is already attached to a cluster
+ * Note: a Aware cluster is a shared resource - if the device is already attached to a cluster
* then this function will simply indicate success immediately using the same {@code
* attachCallback}.
* <p>
- * This version of the API attaches a listener to receive the MAC address of the NAN interface
+ * This version of the API attaches a listener to receive the MAC address of the Aware interface
* on startup and whenever it is updated (it is randomized at regular intervals for privacy).
* The application must have the {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}
* permission to execute this attach request. Otherwise, use the
- * {@link #attach(Handler, WifiNanAttachCallback)} version. Note that aside from permission
+ * {@link #attach(WifiAwareAttachCallback, Handler)} version. Note that aside from permission
* requirements this listener will wake up the host at regular intervals causing higher power
* consumption, do not use it unless the information is necessary (e.g. for OOB discovery).
*
+ * @param attachCallback A callback for attach events, extended from
+ * {@link WifiAwareAttachCallback}.
+ * @param identityChangedListener A listener for changed identity, extended from
+ * {@link WifiAwareIdentityChangedListener}.
* @param handler The Handler on whose thread to execute the callbacks of the {@code
* attachCallback} and {@code identityChangedListener} objects. If a null is provided then the
* application's main thread will be used.
- * @param attachCallback A callback for attach events, extended from
- * {@link WifiNanAttachCallback}.
- * @param identityChangedListener A listener for changed identity, extended from
- * {@link WifiNanIdentityChangedListener}.
*/
- public void attach(@Nullable Handler handler, @NonNull WifiNanAttachCallback attachCallback,
- @NonNull WifiNanIdentityChangedListener identityChangedListener) {
+ public void attach(@NonNull WifiAwareAttachCallback attachCallback,
+ @NonNull WifiAwareIdentityChangedListener identityChangedListener,
+ @Nullable Handler handler) {
attach(handler, null, attachCallback, identityChangedListener);
}
/** @hide */
public void attach(Handler handler, ConfigRequest configRequest,
- WifiNanAttachCallback attachCallback,
- WifiNanIdentityChangedListener identityChangedListener) {
+ WifiAwareAttachCallback attachCallback,
+ WifiAwareIdentityChangedListener identityChangedListener) {
if (VDBG) {
Log.v(TAG, "attach(): handler=" + handler + ", callback=" + attachCallback
+ ", configRequest=" + configRequest + ", identityChangedListener="
@@ -377,7 +387,7 @@
try {
Binder binder = new Binder();
mService.connect(binder, mContext.getOpPackageName(),
- new WifiNanEventCallbackProxy(this, looper, binder, attachCallback,
+ new WifiAwareEventCallbackProxy(this, looper, binder, attachCallback,
identityChangedListener), configRequest,
identityChangedListener != null);
} catch (RemoteException e) {
@@ -399,12 +409,12 @@
/** @hide */
public void publish(int clientId, Looper looper, PublishConfig publishConfig,
- WifiNanDiscoverySessionCallback callback) {
+ WifiAwareDiscoverySessionCallback callback) {
if (VDBG) Log.v(TAG, "publish(): clientId=" + clientId + ", config=" + publishConfig);
try {
mService.publish(clientId, publishConfig,
- new WifiNanDiscoverySessionCallbackProxy(this, looper, true, callback,
+ new WifiAwareDiscoverySessionCallbackProxy(this, looper, true, callback,
clientId));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -427,7 +437,7 @@
/** @hide */
public void subscribe(int clientId, Looper looper, SubscribeConfig subscribeConfig,
- WifiNanDiscoverySessionCallback callback) {
+ WifiAwareDiscoverySessionCallback callback) {
if (VDBG) {
if (VDBG) {
Log.v(TAG,
@@ -437,7 +447,7 @@
try {
mService.subscribe(clientId, subscribeConfig,
- new WifiNanDiscoverySessionCallbackProxy(this, looper, false, callback,
+ new WifiAwareDiscoverySessionCallbackProxy(this, looper, false, callback,
clientId));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -473,7 +483,7 @@
}
/** @hide */
- public void sendMessage(int clientId, int sessionId, Object peerHandle, byte[] message,
+ public void sendMessage(int clientId, int sessionId, PeerHandle peerHandle, byte[] message,
int messageId, int retryCount) {
if (peerHandle == null) {
throw new IllegalArgumentException(
@@ -482,13 +492,13 @@
if (VDBG) {
Log.v(TAG, "sendMessage(): clientId=" + clientId + ", sessionId=" + sessionId
- + ", peerHandle=" + ((OpaquePeerHandle) peerHandle).peerId + ", messageId="
+ + ", peerHandle=" + peerHandle.peerId + ", messageId="
+ messageId + ", retryCount=" + retryCount);
}
try {
- mService.sendMessage(clientId, sessionId, ((OpaquePeerHandle) peerHandle).peerId,
- message, messageId, retryCount);
+ mService.sendMessage(clientId, sessionId, peerHandle.peerId, message, messageId,
+ retryCount);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -516,12 +526,12 @@
}
/** @hide */
- public String createNetworkSpecifier(int clientId, int role, int sessionId, Object peerHandle,
- byte[] token) {
+ public String createNetworkSpecifier(int clientId, int role, int sessionId,
+ PeerHandle peerHandle, byte[] token) {
if (VDBG) {
Log.v(TAG, "createNetworkSpecifier: role=" + role + ", sessionId=" + sessionId
- + ", peerHandle=" + ((peerHandle == null) ? peerHandle
- : ((OpaquePeerHandle) peerHandle).peerId) + ", token=" + token);
+ + ", peerHandle=" + ((peerHandle == null) ? peerHandle : peerHandle.peerId)
+ + ", token=" + token);
}
int type;
@@ -535,13 +545,13 @@
type = NETWORK_SPECIFIER_TYPE_1D;
}
- if (role != WIFI_NAN_DATA_PATH_ROLE_INITIATOR
- && role != WIFI_NAN_DATA_PATH_ROLE_RESPONDER) {
+ if (role != WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
+ && role != WIFI_AWARE_DATA_PATH_ROLE_RESPONDER) {
throw new IllegalArgumentException(
"createNetworkSpecifier: Invalid 'role' argument when creating a network "
+ "specifier");
}
- if (role == WIFI_NAN_DATA_PATH_ROLE_INITIATOR) {
+ if (role == WIFI_AWARE_DATA_PATH_ROLE_INITIATOR) {
if (token == null) {
throw new IllegalArgumentException(
"createNetworkSpecifier: Invalid null token - not permitted on INITIATOR");
@@ -561,7 +571,7 @@
json.put(NETWORK_SPECIFIER_KEY_CLIENT_ID, clientId);
json.put(NETWORK_SPECIFIER_KEY_SESSION_ID, sessionId);
if (peerHandle != null) {
- json.put(NETWORK_SPECIFIER_KEY_PEER_ID, ((OpaquePeerHandle) peerHandle).peerId);
+ json.put(NETWORK_SPECIFIER_KEY_PEER_ID, peerHandle.peerId);
}
if (token != null) {
json.put(NETWORK_SPECIFIER_KEY_TOKEN,
@@ -592,13 +602,13 @@
type = NETWORK_SPECIFIER_TYPE_2D;
}
- if (role != WIFI_NAN_DATA_PATH_ROLE_INITIATOR
- && role != WIFI_NAN_DATA_PATH_ROLE_RESPONDER) {
+ if (role != WIFI_AWARE_DATA_PATH_ROLE_INITIATOR
+ && role != WIFI_AWARE_DATA_PATH_ROLE_RESPONDER) {
throw new IllegalArgumentException(
"createNetworkSpecifier: Invalid 'role' argument when creating a network "
+ "specifier");
}
- if (role == WIFI_NAN_DATA_PATH_ROLE_INITIATOR) {
+ if (role == WIFI_AWARE_DATA_PATH_ROLE_INITIATOR) {
if (peer == null || peer.length != 6) {
throw new IllegalArgumentException(
"createNetworkSpecifier: Invalid peer MAC address");
@@ -634,7 +644,7 @@
return json.toString();
}
- private static class WifiNanEventCallbackProxy extends IWifiNanEventCallback.Stub {
+ private static class WifiAwareEventCallbackProxy extends IWifiAwareEventCallback.Stub {
private static final int CALLBACK_CONNECT_SUCCESS = 0;
private static final int CALLBACK_CONNECT_FAIL = 1;
private static final int CALLBACK_IDENTITY_CHANGED = 2;
@@ -643,12 +653,12 @@
private static final int CALLBACK_RANGING_ABORTED = 5;
private final Handler mHandler;
- private final WeakReference<WifiNanManager> mNanManager;
+ private final WeakReference<WifiAwareManager> mAwareManager;
private final Binder mBinder;
private final Looper mLooper;
RttManager.RttListener getAndRemoveRangingListener(int rangingId) {
- WifiNanManager mgr = mNanManager.get();
+ WifiAwareManager mgr = mAwareManager.get();
if (mgr == null) {
Log.w(TAG, "getAndRemoveRangingListener: called post GC");
return null;
@@ -662,39 +672,40 @@
}
/**
- * Constructs a {@link WifiNanAttachCallback} using the specified looper.
+ * Constructs a {@link WifiAwareAttachCallback} using the specified looper.
* All callbacks will delivered on the thread of the specified looper.
*
* @param looper The looper on which to execute the callbacks.
*/
- WifiNanEventCallbackProxy(WifiNanManager mgr, Looper looper, Binder binder,
- final WifiNanAttachCallback attachCallback,
- final WifiNanIdentityChangedListener identityChangedListener) {
- mNanManager = new WeakReference<>(mgr);
+ WifiAwareEventCallbackProxy(WifiAwareManager mgr, Looper looper, Binder binder,
+ final WifiAwareAttachCallback attachCallback,
+ final WifiAwareIdentityChangedListener identityChangedListener) {
+ mAwareManager = new WeakReference<>(mgr);
mLooper = looper;
mBinder = binder;
- if (VDBG) Log.v(TAG, "WifiNanEventCallbackProxy ctor: looper=" + looper);
+ if (VDBG) Log.v(TAG, "WifiAwareEventCallbackProxy ctor: looper=" + looper);
mHandler = new Handler(looper) {
@Override
public void handleMessage(Message msg) {
if (DBG) {
- Log.d(TAG, "WifiNanEventCallbackProxy: What=" + msg.what + ", msg=" + msg);
+ Log.d(TAG, "WifiAwareEventCallbackProxy: What=" + msg.what + ", msg="
+ + msg);
}
- WifiNanManager mgr = mNanManager.get();
+ WifiAwareManager mgr = mAwareManager.get();
if (mgr == null) {
- Log.w(TAG, "WifiNanEventCallbackProxy: handleMessage post GC");
+ Log.w(TAG, "WifiAwareEventCallbackProxy: handleMessage post GC");
return;
}
switch (msg.what) {
case CALLBACK_CONNECT_SUCCESS:
attachCallback.onAttached(
- new WifiNanSession(mgr, mBinder, msg.arg1));
+ new WifiAwareSession(mgr, mBinder, msg.arg1));
break;
case CALLBACK_CONNECT_FAIL:
- mNanManager.clear();
+ mAwareManager.clear();
attachCallback.onAttachFailed();
break;
case CALLBACK_IDENTITY_CHANGED:
@@ -801,8 +812,8 @@
}
}
- private static class WifiNanDiscoverySessionCallbackProxy extends
- IWifiNanDiscoverySessionCallback.Stub {
+ private static class WifiAwareDiscoverySessionCallbackProxy extends
+ IWifiAwareDiscoverySessionCallback.Stub {
private static final int CALLBACK_SESSION_STARTED = 0;
private static final int CALLBACK_SESSION_CONFIG_SUCCESS = 1;
private static final int CALLBACK_SESSION_CONFIG_FAIL = 2;
@@ -815,23 +826,24 @@
private static final String MESSAGE_BUNDLE_KEY_MESSAGE = "message";
private static final String MESSAGE_BUNDLE_KEY_MESSAGE2 = "message2";
- private final WeakReference<WifiNanManager> mNanManager;
+ private final WeakReference<WifiAwareManager> mAwareManager;
private final boolean mIsPublish;
- private final WifiNanDiscoverySessionCallback mOriginalCallback;
+ private final WifiAwareDiscoverySessionCallback mOriginalCallback;
private final int mClientId;
private final Handler mHandler;
- private WifiNanDiscoveryBaseSession mSession;
+ private WifiAwareDiscoveryBaseSession mSession;
- WifiNanDiscoverySessionCallbackProxy(WifiNanManager mgr, Looper looper, boolean isPublish,
- WifiNanDiscoverySessionCallback originalCallback, int clientId) {
- mNanManager = new WeakReference<>(mgr);
+ WifiAwareDiscoverySessionCallbackProxy(WifiAwareManager mgr, Looper looper,
+ boolean isPublish, WifiAwareDiscoverySessionCallback originalCallback,
+ int clientId) {
+ mAwareManager = new WeakReference<>(mgr);
mIsPublish = isPublish;
mOriginalCallback = originalCallback;
mClientId = clientId;
if (VDBG) {
- Log.v(TAG, "WifiNanDiscoverySessionCallbackProxy ctor: isPublish=" + isPublish);
+ Log.v(TAG, "WifiAwareDiscoverySessionCallbackProxy ctor: isPublish=" + isPublish);
}
mHandler = new Handler(looper) {
@@ -839,8 +851,8 @@
public void handleMessage(Message msg) {
if (DBG) Log.d(TAG, "What=" + msg.what + ", msg=" + msg);
- if (mNanManager.get() == null) {
- Log.w(TAG, "WifiNanDiscoverySessionCallbackProxy: handleMessage post GC");
+ if (mAwareManager.get() == null) {
+ Log.w(TAG, "WifiAwareDiscoverySessionCallbackProxy: handleMessage post GC");
return;
}
@@ -858,26 +870,36 @@
* creation failed (as opposed to update
* failing)
*/
- mNanManager.clear();
+ mAwareManager.clear();
}
break;
case CALLBACK_SESSION_TERMINATED:
onProxySessionTerminated(msg.arg1);
break;
- case CALLBACK_MATCH:
- mOriginalCallback.onServiceDiscovered(
- new OpaquePeerHandle(msg.arg1),
+ case CALLBACK_MATCH: {
+ List<byte[]> matchFilter = null;
+ byte[] arg = msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE2);
+ try {
+ matchFilter = new TlvBufferUtils.TlvIterable(0, 1, arg).toList();
+ } catch (BufferOverflowException e) {
+ matchFilter = null;
+ Log.e(TAG, "onServiceDiscovered: invalid match filter byte array '"
+ + new String(HexEncoding.encode(arg))
+ + "' - cannot be parsed: e=" + e);
+ }
+ mOriginalCallback.onServiceDiscovered(new PeerHandle(msg.arg1),
msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE),
- msg.getData().getByteArray(MESSAGE_BUNDLE_KEY_MESSAGE2));
+ matchFilter);
break;
+ }
case CALLBACK_MESSAGE_SEND_SUCCESS:
- mOriginalCallback.onMessageSent(msg.arg1);
+ mOriginalCallback.onMessageSendSucceeded(msg.arg1);
break;
case CALLBACK_MESSAGE_SEND_FAIL:
mOriginalCallback.onMessageSendFailed(msg.arg1);
break;
case CALLBACK_MESSAGE_RECEIVED:
- mOriginalCallback.onMessageReceived(new OpaquePeerHandle(msg.arg1),
+ mOriginalCallback.onMessageReceived(new PeerHandle(msg.arg1),
(byte[]) msg.obj);
break;
}
@@ -956,7 +978,7 @@
@Override
public void onMessageReceived(int peerId, byte[] message) {
if (VDBG) {
- Log.v(TAG, "onMessageReceived: peerId='" + peerId);
+ Log.v(TAG, "onMessageReceived: peerId=" + peerId);
}
Message msg = mHandler.obtainMessage(CALLBACK_MESSAGE_RECEIVED);
@@ -977,20 +999,20 @@
"onSessionStarted: sessionId=" + sessionId + ": session already created!?");
}
- WifiNanManager mgr = mNanManager.get();
+ WifiAwareManager mgr = mAwareManager.get();
if (mgr == null) {
Log.w(TAG, "onProxySessionStarted: mgr GC'd");
return;
}
if (mIsPublish) {
- WifiNanPublishDiscoverySession session = new WifiNanPublishDiscoverySession(mgr,
+ WifiAwarePublishDiscoverySession session = new WifiAwarePublishDiscoverySession(mgr,
mClientId, sessionId);
mSession = session;
mOriginalCallback.onPublishStarted(session);
} else {
- WifiNanSubscribeDiscoverySession
- session = new WifiNanSubscribeDiscoverySession(mgr, mClientId, sessionId);
+ WifiAwareSubscribeDiscoverySession
+ session = new WifiAwareSubscribeDiscoverySession(mgr, mClientId, sessionId);
mSession = session;
mOriginalCallback.onSubscribeStarted(session);
}
@@ -1004,17 +1026,19 @@
} else {
Log.w(TAG, "Proxy: onSessionTerminated called but mSession is null!?");
}
- mNanManager.clear();
+ mAwareManager.clear();
mOriginalCallback.onSessionTerminated(reason);
}
}
- /** @hide */
- public static class OpaquePeerHandle {
- public OpaquePeerHandle(int peerId) {
+ /** @hide PROPOSED_AWARE_API */
+ public static class PeerHandle {
+ /** @hide */
+ public PeerHandle(int peerId) {
this.peerId = peerId;
}
+ /** @hide */
public int peerId;
}
}
diff --git a/wifi/java/android/net/wifi/nan/WifiNanPublishDiscoverySession.java b/wifi/java/android/net/wifi/aware/WifiAwarePublishDiscoverySession.java
similarity index 63%
rename from wifi/java/android/net/wifi/nan/WifiNanPublishDiscoverySession.java
rename to wifi/java/android/net/wifi/aware/WifiAwarePublishDiscoverySession.java
index 75c6cb7..68786d1 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanPublishDiscoverySession.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwarePublishDiscoverySession.java
@@ -14,39 +14,39 @@
* limitations under the License.
*/
-package android.net.wifi.nan;
+package android.net.wifi.aware;
import android.annotation.NonNull;
import android.util.Log;
/**
- * A class representing a NAN publish session. Created when
- * {@link WifiNanSession#publish(android.os.Handler, PublishConfig, WifiNanDiscoverySessionCallback)}
- * is called and a discovery session is created and returned in
- * {@link WifiNanDiscoverySessionCallback#onPublishStarted(WifiNanPublishDiscoverySession)}. See
- * baseline functionality of all discovery sessions in {@link WifiNanDiscoveryBaseSession}. This
+ * A class representing a Aware publish session. Created when
+ * {@link WifiAwareSession#publish(PublishConfig, WifiAwareDiscoverySessionCallback,
+ * android.os.Handler)} is called and a discovery session is created and returned in
+ * {@link WifiAwareDiscoverySessionCallback#onPublishStarted(WifiAwarePublishDiscoverySession)}. See
+ * baseline functionality of all discovery sessions in {@link WifiAwareDiscoveryBaseSession}. This
* object allows updating an existing/running publish discovery session using
* {@link #updatePublish(PublishConfig)}.
*
- * @hide PROPOSED_NAN_API
+ * @hide PROPOSED_AWARE_API
*/
-public class WifiNanPublishDiscoverySession extends WifiNanDiscoveryBaseSession {
- private static final String TAG = "WifiNanPublishDiscSsn";
+public class WifiAwarePublishDiscoverySession extends WifiAwareDiscoveryBaseSession {
+ private static final String TAG = "WifiAwarePublishDiscSsn";
/** @hide */
- public WifiNanPublishDiscoverySession(WifiNanManager manager, int clientId, int sessionId) {
+ public WifiAwarePublishDiscoverySession(WifiAwareManager manager, int clientId, int sessionId) {
super(manager, clientId, sessionId);
}
/**
* Re-configure the currently active publish session. The
- * {@link WifiNanDiscoverySessionCallback} is not replaced - the same listener used
+ * {@link WifiAwareDiscoverySessionCallback} is not replaced - the same listener used
* at creation is still used. The results of the configuration are returned using
- * {@link WifiNanDiscoverySessionCallback}:
+ * {@link WifiAwareDiscoverySessionCallback}:
* <ul>
- * <li>{@link WifiNanDiscoverySessionCallback#onSessionConfigUpdated()}: configuration
+ * <li>{@link WifiAwareDiscoverySessionCallback#onSessionConfigUpdated()}: configuration
* update succeeded.
- * <li>{@link WifiNanDiscoverySessionCallback#onSessionConfigFailed()}: configuration
+ * <li>{@link WifiAwareDiscoverySessionCallback#onSessionConfigFailed()}: configuration
* update failed. The publish discovery session is still running using its previous
* configuration (i.e. update failure does not terminate the session).
* </ul>
@@ -58,9 +58,9 @@
Log.w(TAG, "updatePublish: called on terminated session");
return;
} else {
- WifiNanManager mgr = mMgr.get();
+ WifiAwareManager mgr = mMgr.get();
if (mgr == null) {
- Log.w(TAG, "updatePublish: called post GC on WifiNanManager");
+ Log.w(TAG, "updatePublish: called post GC on WifiAwareManager");
return;
}
diff --git a/wifi/java/android/net/wifi/nan/WifiNanSession.java b/wifi/java/android/net/wifi/aware/WifiAwareSession.java
similarity index 63%
rename from wifi/java/android/net/wifi/nan/WifiNanSession.java
rename to wifi/java/android/net/wifi/aware/WifiAwareSession.java
index df5e3c1..005895a 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanSession.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareSession.java
@@ -14,11 +14,10 @@
* limitations under the License.
*/
-package android.net.wifi.nan;
+package android.net.wifi.aware;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.net.NetworkRequest;
import android.os.Binder;
import android.os.Handler;
import android.os.Looper;
@@ -29,17 +28,17 @@
import java.lang.ref.WeakReference;
/**
- * This class represents a Wi-Fi NAN session - an attachment to the Wi-Fi NAN service through
+ * This class represents a Wi-Fi Aware session - an attachment to the Wi-Fi Aware service through
* which the app can execute discovery operations.
*
- * @hide PROPOSED_NAN_API
+ * @hide PROPOSED_AWARE_API
*/
-public class WifiNanSession {
- private static final String TAG = "WifiNanSession";
+public class WifiAwareSession {
+ private static final String TAG = "WifiAwareSession";
private static final boolean DBG = false;
private static final boolean VDBG = false; // STOPSHIP if true
- private final WeakReference<WifiNanManager> mMgr;
+ private final WeakReference<WifiAwareManager> mMgr;
private final Binder mBinder;
private final int mClientId;
@@ -47,7 +46,7 @@
private final CloseGuard mCloseGuard = CloseGuard.get();
/** @hide */
- public WifiNanSession(WifiNanManager manager, Binder binder, int clientId) {
+ public WifiAwareSession(WifiAwareManager manager, Binder binder, int clientId) {
if (VDBG) Log.v(TAG, "New session created: manager=" + manager + ", clientId=" + clientId);
mMgr = new WeakReference<>(manager);
@@ -59,19 +58,19 @@
}
/**
- * Destroy the Wi-Fi NAN service session and, if no other applications are attached to NAN,
- * also disable NAN. This method destroys all outstanding operations - i.e. all publish and
+ * Destroy the Wi-Fi Aware service session and, if no other applications are attached to Aware,
+ * also disable Aware. This method destroys all outstanding operations - i.e. all publish and
* subscribes are terminated, and any outstanding data-links are shut-down. However, it is
* good practice to destroy these discovery sessions and connections explicitly before a
* session-wide destroy.
* <p>
* An application may re-attach after a destroy using
- * {@link WifiNanManager#attach(Handler, WifiNanAttachCallback)} .
+ * {@link WifiAwareManager#attach(WifiAwareAttachCallback, Handler)} .
*/
public void destroy() {
- WifiNanManager mgr = mMgr.get();
+ WifiAwareManager mgr = mMgr.get();
if (mgr == null) {
- Log.w(TAG, "destroy: called post GC on WifiNanManager");
+ Log.w(TAG, "destroy: called post GC on WifiAwareManager");
return;
}
mgr.disconnect(mClientId, mBinder);
@@ -94,40 +93,41 @@
}
/**
- * Issue a request to the NAN service to create a new NAN publish discovery session, using
+ * Issue a request to the Aware service to create a new Aware publish discovery session, using
* the specified {@code publishConfig} configuration. The results of the publish operation
- * are routed to the callbacks of {@link WifiNanDiscoverySessionCallback}:
+ * are routed to the callbacks of {@link WifiAwareDiscoverySessionCallback}:
* <ul>
* <li>
- * {@link WifiNanDiscoverySessionCallback#onPublishStarted(WifiNanPublishDiscoverySession)}
+ * {@link WifiAwareDiscoverySessionCallback#onPublishStarted(
+ * WifiAwarePublishDiscoverySession)}
* is called when the publish session is created and provides a handle to the session.
* Further operations on the publish session can be executed on that object.
- * <li>{@link WifiNanDiscoverySessionCallback#onSessionConfigFailed()} is called if the
+ * <li>{@link WifiAwareDiscoverySessionCallback#onSessionConfigFailed()} is called if the
* publish operation failed.
* </ul>
* <p>
* Other results of the publish session operations will also be routed to callbacks
* on the {@code callback} object. The resulting publish session can be modified using
- * {@link WifiNanPublishDiscoverySession#updatePublish(PublishConfig)}.
+ * {@link WifiAwarePublishDiscoverySession#updatePublish(PublishConfig)}.
* <p>
- * An application must use the {@link WifiNanDiscoveryBaseSession#destroy()} to
+ * An application must use the {@link WifiAwareDiscoveryBaseSession#destroy()} to
* terminate the publish discovery session once it isn't needed. This will free
* resources as well terminate any on-air transmissions.
* <p>The application must have the {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}
* permission to start a publish discovery session.
*
- * @param handler The Handler on whose thread to execute the callbacks of the {@code
- * callback} object. If a null is provided then the application's main thread will be used.
* @param publishConfig The {@link PublishConfig} specifying the
* configuration of the requested publish session.
- * @param callback A {@link WifiNanDiscoverySessionCallback} derived object to be used for
+ * @param callback A {@link WifiAwareDiscoverySessionCallback} derived object to be used for
* session event callbacks.
+ * @param handler The Handler on whose thread to execute the callbacks of the {@code
+ * callback} object. If a null is provided then the application's main thread will be used.
*/
- public void publish(@Nullable Handler handler, @NonNull PublishConfig publishConfig,
- @NonNull WifiNanDiscoverySessionCallback callback) {
- WifiNanManager mgr = mMgr.get();
+ public void publish(@NonNull PublishConfig publishConfig,
+ @NonNull WifiAwareDiscoverySessionCallback callback, @Nullable Handler handler) {
+ WifiAwareManager mgr = mMgr.get();
if (mgr == null) {
- Log.e(TAG, "publish: called post GC on WifiNanManager");
+ Log.e(TAG, "publish: called post GC on WifiAwareManager");
return;
}
if (mTerminated) {
@@ -139,40 +139,41 @@
}
/**
- * Issue a request to the NAN service to create a new NAN subscribe discovery session, using
+ * Issue a request to the Aware service to create a new Aware subscribe discovery session, using
* the specified {@code subscribeConfig} configuration. The results of the subscribe
- * operation are routed to the callbacks of {@link WifiNanDiscoverySessionCallback}:
+ * operation are routed to the callbacks of {@link WifiAwareDiscoverySessionCallback}:
* <ul>
* <li>
- * {@link WifiNanDiscoverySessionCallback#onSubscribeStarted(WifiNanSubscribeDiscoverySession)}
+ * {@link WifiAwareDiscoverySessionCallback#onSubscribeStarted(
+ * WifiAwareSubscribeDiscoverySession)}
* is called when the subscribe session is created and provides a handle to the session.
* Further operations on the subscribe session can be executed on that object.
- * <li>{@link WifiNanDiscoverySessionCallback#onSessionConfigFailed()} is called if the
+ * <li>{@link WifiAwareDiscoverySessionCallback#onSessionConfigFailed()} is called if the
* subscribe operation failed.
* </ul>
* <p>
* Other results of the subscribe session operations will also be routed to callbacks
* on the {@code callback} object. The resulting subscribe session can be modified using
- * {@link WifiNanSubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}.
+ * {@link WifiAwareSubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}.
* <p>
- * An application must use the {@link WifiNanDiscoveryBaseSession#destroy()} to
+ * An application must use the {@link WifiAwareDiscoveryBaseSession#destroy()} to
* terminate the subscribe discovery session once it isn't needed. This will free
* resources as well terminate any on-air transmissions.
* <p>The application must have the {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}
* permission to start a subscribe discovery session.
*
- * @param handler The Handler on whose thread to execute the callbacks of the {@code
- * callback} object. If a null is provided then the application's main thread will be used.
* @param subscribeConfig The {@link SubscribeConfig} specifying the
* configuration of the requested subscribe session.
- * @param callback A {@link WifiNanDiscoverySessionCallback} derived object to be used for
+ * @param callback A {@link WifiAwareDiscoverySessionCallback} derived object to be used for
* session event callbacks.
+ * @param handler The Handler on whose thread to execute the callbacks of the {@code
+ * callback} object. If a null is provided then the application's main thread will be used.
*/
- public void subscribe(@Nullable Handler handler, @NonNull SubscribeConfig subscribeConfig,
- @NonNull WifiNanDiscoverySessionCallback callback) {
- WifiNanManager mgr = mMgr.get();
+ public void subscribe(@NonNull SubscribeConfig subscribeConfig,
+ @NonNull WifiAwareDiscoverySessionCallback callback, @Nullable Handler handler) {
+ WifiAwareManager mgr = mMgr.get();
if (mgr == null) {
- Log.e(TAG, "publish: called post GC on WifiNanManager");
+ Log.e(TAG, "publish: called post GC on WifiAwareManager");
return;
}
if (mTerminated) {
@@ -184,20 +185,21 @@
}
/**
- * Create a {@link NetworkRequest.Builder#setNetworkSpecifier(String)} for a
- * WiFi NAN connection to the specified peer. The
- * {@link NetworkRequest.Builder#addTransportType(int)} should be set to
- * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_NAN}.
+ * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for a
+ * WiFi Aware connection to the specified peer. The
+ * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to
+ * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}.
* <p>
* This API is targeted for applications which can obtain the peer MAC address using OOB
- * (out-of-band) discovery. NAN discovery does not provide the MAC address of the peer -
- * when using NAN discovery use the alternative network specifier method -
- * {@link WifiNanDiscoveryBaseSession#createNetworkSpecifier(int, Object, byte[])}.
+ * (out-of-band) discovery. Aware discovery does not provide the MAC address of the peer -
+ * when using Aware discovery use the alternative network specifier method -
+ * {@link WifiAwareDiscoveryBaseSession#createNetworkSpecifier(WifiAwareManager.PeerHandle,
+ * byte[])}.
*
* @param role The role of this device:
- * {@link WifiNanManager#WIFI_NAN_DATA_PATH_ROLE_INITIATOR} or
- * {@link WifiNanManager#WIFI_NAN_DATA_PATH_ROLE_RESPONDER}
- * @param peer The MAC address of the peer's NAN discovery interface. On a RESPONDER this
+ * {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_INITIATOR} or
+ * {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_RESPONDER}
+ * @param peer The MAC address of the peer's Aware discovery interface. On a RESPONDER this
* value is used to gate the acceptance of a connection request from only that
* peer. A RESPONDER may specified a null - indicating that it will accept
* connection requests from any device.
@@ -209,14 +211,15 @@
*
* @return A string to be used to construct
* {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to
- * {@link android.net.ConnectivityManager#requestNetwork(NetworkRequest, android.net.ConnectivityManager.NetworkCallback)}
+ * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest,
+ * android.net.ConnectivityManager.NetworkCallback)}
* [or other varieties of that API].
*/
- public String createNetworkSpecifier(@WifiNanManager.DataPathRole int role, @Nullable byte[] peer,
- @Nullable byte[] token) {
- WifiNanManager mgr = mMgr.get();
+ public String createNetworkSpecifier(@WifiAwareManager.DataPathRole int role,
+ @Nullable byte[] peer, @Nullable byte[] token) {
+ WifiAwareManager mgr = mMgr.get();
if (mgr == null) {
- Log.e(TAG, "createNetworkSpecifier: called post GC on WifiNanManager");
+ Log.e(TAG, "createNetworkSpecifier: called post GC on WifiAwareManager");
return "";
}
if (mTerminated) {
diff --git a/wifi/java/android/net/wifi/nan/WifiNanSubscribeDiscoverySession.java b/wifi/java/android/net/wifi/aware/WifiAwareSubscribeDiscoverySession.java
similarity index 65%
rename from wifi/java/android/net/wifi/nan/WifiNanSubscribeDiscoverySession.java
rename to wifi/java/android/net/wifi/aware/WifiAwareSubscribeDiscoverySession.java
index f5b4c0c..a0ec809 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanSubscribeDiscoverySession.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareSubscribeDiscoverySession.java
@@ -14,41 +14,43 @@
* limitations under the License.
*/
-package android.net.wifi.nan;
+package android.net.wifi.aware;
import android.annotation.NonNull;
import android.util.Log;
/**
- * A class representing a NAN subscribe session. Created when
- * {@link WifiNanSession#subscribe(android.os.Handler, SubscribeConfig, WifiNanDiscoverySessionCallback)}
+ * A class representing a Aware subscribe session. Created when
+ * {@link WifiAwareSession#subscribe(SubscribeConfig,
+ * WifiAwareDiscoverySessionCallback, android.os.Handler)}
* is called and a discovery session is created and returned in
- * {@link WifiNanDiscoverySessionCallback#onSubscribeStarted(WifiNanSubscribeDiscoverySession)}.
- * See baseline functionality of all discovery sessions in {@link WifiNanDiscoveryBaseSession}.
+ * {@link WifiAwareDiscoverySessionCallback#onSubscribeStarted(WifiAwareSubscribeDiscoverySession)}.
+ * See baseline functionality of all discovery sessions in {@link WifiAwareDiscoveryBaseSession}.
* This object allows updating an existing/running subscribe discovery session using
* {@link #updateSubscribe(SubscribeConfig)}.
*
- * @hide PROPOSED_NAN_API
+ * @hide PROPOSED_AWARE_API
*/
-public class WifiNanSubscribeDiscoverySession extends WifiNanDiscoveryBaseSession {
- private static final String TAG = "WifiNanSubscribeDiscSsn";
+public class WifiAwareSubscribeDiscoverySession extends WifiAwareDiscoveryBaseSession {
+ private static final String TAG = "WifiAwareSubsDiscSsn";
/**
* {@hide}
*/
- public WifiNanSubscribeDiscoverySession(WifiNanManager manager, int clientId, int sessionId) {
+ public WifiAwareSubscribeDiscoverySession(WifiAwareManager manager, int clientId,
+ int sessionId) {
super(manager, clientId, sessionId);
}
/**
* Re-configure the currently active subscribe session. The
- * {@link WifiNanDiscoverySessionCallback} is not replaced - the same listener used
+ * {@link WifiAwareDiscoverySessionCallback} is not replaced - the same listener used
* at creation is still used. The results of the configuration are returned using
- * {@link WifiNanDiscoverySessionCallback}:
+ * {@link WifiAwareDiscoverySessionCallback}:
* <ul>
- * <li>{@link WifiNanDiscoverySessionCallback#onSessionConfigUpdated()}: configuration
+ * <li>{@link WifiAwareDiscoverySessionCallback#onSessionConfigUpdated()}: configuration
* update succeeded.
- * <li>{@link WifiNanDiscoverySessionCallback#onSessionConfigFailed()}: configuration
+ * <li>{@link WifiAwareDiscoverySessionCallback#onSessionConfigFailed()}: configuration
* update failed. The subscribe discovery session is still running using its previous
* configuration (i.e. update failure does not terminate the session).
* </ul>
@@ -61,9 +63,9 @@
Log.w(TAG, "updateSubscribe: called on terminated session");
return;
} else {
- WifiNanManager mgr = mMgr.get();
+ WifiAwareManager mgr = mMgr.get();
if (mgr == null) {
- Log.w(TAG, "updateSubscribe: called post GC on WifiNanManager");
+ Log.w(TAG, "updateSubscribe: called post GC on WifiAwareManager");
return;
}
diff --git a/wifi/java/android/net/wifi/nan/WifiNanUtils.java b/wifi/java/android/net/wifi/aware/WifiAwareUtils.java
similarity index 93%
rename from wifi/java/android/net/wifi/nan/WifiNanUtils.java
rename to wifi/java/android/net/wifi/aware/WifiAwareUtils.java
index c0f36b4..4083388 100644
--- a/wifi/java/android/net/wifi/nan/WifiNanUtils.java
+++ b/wifi/java/android/net/wifi/aware/WifiAwareUtils.java
@@ -14,14 +14,14 @@
* limitations under the License.
*/
-package android.net.wifi.nan;
+package android.net.wifi.aware;
/**
- * Provides utilities for the Wifi NAN manager/service.
+ * Provides utilities for the Wifi Aware manager/service.
*
* @hide
*/
-public class WifiNanUtils {
+public class WifiAwareUtils {
/**
* Per spec: The Service Name is a UTF-8 encoded string from 1 to 255 bytes in length. The
* only acceptable single-byte UTF-8 symbols for a Service Name are alphanumeric values (A-Z,
diff --git a/wifi/java/android/net/wifi/aware/package.html b/wifi/java/android/net/wifi/aware/package.html
new file mode 100644
index 0000000..1a990d8
--- /dev/null
+++ b/wifi/java/android/net/wifi/aware/package.html
@@ -0,0 +1,43 @@
+<HTML>
+<BODY>
+<p>Provides classes which allow applications to use Wi-Fi Aware to discover peers and create
+ connections to them.</p>
+<p>Using the Wi-Fi Aware APIs, applications can advertise services, discover peers which are
+ advertising services, and connect to them.
+ Wi-Fi Aware is independent of Wi-Fi infrastructure (i.e. a device may or may
+ not be associated with an AP concurrent to using Wi-Fi Aware). </p>
+<p>The primary entry point to Wi-Fi Aware capabilities is the
+ {@link android.net.wifi.aware.WifiAwareManager} class, which is acquired by calling
+ {@link android.content.Context#getSystemService(String)
+ Context.getSystemService(Context.WIFI_AWARE_SERVICE)}</p>
+
+<p>Some APIs may require the following user permissions:</p>
+<ul>
+ <li>{@link android.Manifest.permission#ACCESS_WIFI_STATE}</li>
+ <li>{@link android.Manifest.permission#CHANGE_WIFI_STATE}</li>
+ <li>{@link android.Manifest.permission#ACCESS_COARSE_LOCATION}</li>
+</ul>
+
+<p class="note"><strong>Note:</strong> Not all Android-powered devices support Wi-Fi Aware
+ functionality.
+ If your application only works with Wi-Fi Aware (i.e. it should only be installed on devices which
+ support Wi-Fi Aware), declare so with a <a
+ href="{@docRoot}guide/topics/manifest/uses-feature-element.html">
+ {@code <uses-feature>}</a>
+ element in the manifest file:</p>
+<pre>
+<manifest ...>
+ <uses-feature android:name="android.hardware.wifi.aware" />
+ ...
+</manifest>
+</pre>
+<p>Alternatively, if you application does not require Wi-Fi Aware but can take advantage of it if
+ available, you can perform
+ the check at run-time in your code using {@link
+ android.content.pm.PackageManager#hasSystemFeature(String)} with {@link
+ android.content.pm.PackageManager#FEATURE_WIFI_AWARE}:</p>
+<pre>
+ getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_AWARE)
+</pre>
+</BODY>
+</HTML>
diff --git a/wifi/java/android/net/wifi/hotspot2/ConfigBuilder.java b/wifi/java/android/net/wifi/hotspot2/ConfigBuilder.java
new file mode 100644
index 0000000..96db5d0
--- /dev/null
+++ b/wifi/java/android/net/wifi/hotspot2/ConfigBuilder.java
@@ -0,0 +1,473 @@
+/**
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.hotspot2;
+
+import android.net.wifi.hotspot2.omadm.PPSMOParser;
+import android.text.TextUtils;
+import android.util.Base64;
+import android.util.Log;
+import android.util.Pair;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.LineNumberReader;
+import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Utility class for building PasspointConfiguration from an installation file.
+ *
+ * @hide
+ */
+public final class ConfigBuilder {
+ private static final String TAG = "ConfigBuilder";
+
+ // Header names.
+ private static final String CONTENT_TYPE = "Content-Type";
+ private static final String CONTENT_TRANSFER_ENCODING = "Content-Transfer-Encoding";
+
+ // MIME types.
+ private static final String TYPE_MULTIPART_MIXED = "multipart/mixed";
+ private static final String TYPE_WIFI_CONFIG = "application/x-wifi-config";
+ private static final String TYPE_PASSPOINT_PROFILE = "application/x-passpoint-profile";
+ private static final String TYPE_CA_CERT = "application/x-x509-ca-cert";
+ private static final String TYPE_PKCS12 = "application/x-pkcs12";
+
+ private static final String ENCODING_BASE64 = "base64";
+ private static final String BOUNDARY = "boundary=";
+
+ /**
+ * Class represent a MIME (Multipurpose Internet Mail Extension) part.
+ */
+ private static class MimePart {
+ /**
+ * Content type of the part.
+ */
+ public String type = null;
+
+ /**
+ * Decoded data.
+ */
+ public byte[] data = null;
+
+ /**
+ * Flag indicating if this is the last part (ending with --{boundary}--).
+ */
+ public boolean isLast = false;
+ }
+
+ /**
+ * Class represent the MIME (Multipurpose Internet Mail Extension) header.
+ */
+ private static class MimeHeader {
+ /**
+ * Content type.
+ */
+ public String contentType = null;
+
+ /**
+ * Boundary string (optional), only applies for the outter MIME header.
+ */
+ public String boundary = null;
+
+ /**
+ * Encoding type.
+ */
+ public String encodingType = null;
+ }
+
+
+ /**
+ * Parse the Hotspot 2.0 Release 1 configuration data into a {@link PasspointConfiguration}
+ * object. The configuration data is a base64 encoded MIME multipart data. Below is
+ * the format of the decoded message:
+ *
+ * Content-Type: multipart/mixed; boundary={boundary}
+ * Content-Transfer-Encoding: base64
+ *
+ * --{boundary}
+ * Content-Type: application/x-passpoint-profile
+ * Content-Transfer-Encoding: base64
+ *
+ * [base64 encoded Passpoint profile data]
+ * --{boundary}
+ * Content-Type: application/x-x509-ca-cert
+ * Content-Transfer-Encoding: base64
+ *
+ * [base64 encoded X509 CA certificate data]
+ * --{boundary}
+ * Content-Type: application/x-pkcs12
+ * Content-Transfer-Encoding: base64
+ *
+ * [base64 encoded PKCS#12 ASN.1 structure containing client certificate chain]
+ * --{boundary}
+ *
+ * @param mimeType MIME type of the encoded data.
+ * @param data A base64 encoded MIME multipart message containing the Passpoint profile
+ * (required), CA (Certificate Authority) certificate (optional), and client
+ * certificate chain (optional).
+ * @return {@link PasspointConfiguration}
+ */
+ public static PasspointConfiguration buildPasspointConfig(String mimeType, byte[] data) {
+ // Verify MIME type.
+ if (!TextUtils.equals(mimeType, TYPE_WIFI_CONFIG)) {
+ Log.e(TAG, "Unexpected MIME type: " + mimeType);
+ return null;
+ }
+
+ try {
+ // Decode the data.
+ byte[] decodedData = Base64.decode(new String(data, StandardCharsets.ISO_8859_1),
+ Base64.DEFAULT);
+ Map<String, byte[]> mimeParts = parseMimeMultipartMessage(new LineNumberReader(
+ new InputStreamReader(new ByteArrayInputStream(decodedData),
+ StandardCharsets.ISO_8859_1)));
+ return createPasspointConfig(mimeParts);
+ } catch (IOException | IllegalArgumentException e) {
+ Log.e(TAG, "Failed to parse installation file: " + e.getMessage());
+ return null;
+ }
+ }
+
+ /**
+ * Create a {@link PasspointConfiguration} object from list of MIME (Multipurpose Internet
+ * Mail Extension) parts.
+ *
+ * @param mimeParts Map of content type and content data.
+ * @return {@link PasspointConfiguration}
+ * @throws IOException
+ */
+ private static PasspointConfiguration createPasspointConfig(Map<String, byte[]> mimeParts)
+ throws IOException {
+ byte[] profileData = mimeParts.get(TYPE_PASSPOINT_PROFILE);
+ if (profileData == null) {
+ throw new IOException("Missing Passpoint Profile");
+ }
+
+ PasspointConfiguration config = PPSMOParser.parseMOText(new String(profileData));
+ if (config == null) {
+ throw new IOException("Failed to parse Passpoint profile");
+ }
+
+ // Credential is needed for storing the certificates and private client key.
+ if (config.credential == null) {
+ throw new IOException("Passpoint profile missing credential");
+ }
+
+ // Parse CA (Certificate Authority) certificate.
+ byte[] caCertData = mimeParts.get(TYPE_CA_CERT);
+ if (caCertData != null) {
+ try {
+ config.credential.caCertificate = parseCACert(caCertData);
+ } catch (CertificateException e) {
+ throw new IOException("Failed to parse CA Certificate");
+ }
+ }
+
+ // Parse PKCS12 data for client private key and certificate chain.
+ byte[] pkcs12Data = mimeParts.get(TYPE_PKCS12);
+ if (pkcs12Data != null) {
+ try {
+ Pair<PrivateKey, List<X509Certificate>> clientKey = parsePkcs12(pkcs12Data);
+ config.credential.clientPrivateKey = clientKey.first;
+ config.credential.clientCertificateChain =
+ clientKey.second.toArray(new X509Certificate[clientKey.second.size()]);
+ } catch(GeneralSecurityException | IOException e) {
+ throw new IOException("Failed to parse PCKS12 string");
+ }
+ }
+ return config;
+ }
+
+ /**
+ * Parse a MIME (Multipurpose Internet Mail Extension) multipart message from the given
+ * input stream.
+ *
+ * @param in The input stream for reading the message data
+ * @return A map of a content type and content data pair
+ * @throws IOException
+ */
+ private static Map<String, byte[]> parseMimeMultipartMessage(LineNumberReader in)
+ throws IOException {
+ // Parse the outer MIME header.
+ MimeHeader header = parseHeaders(in);
+ if (!TextUtils.equals(header.contentType, TYPE_MULTIPART_MIXED)) {
+ throw new IOException("Invalid content type: " + header.contentType);
+ }
+ if (TextUtils.isEmpty(header.boundary)) {
+ throw new IOException("Missing boundary string");
+ }
+ if (!TextUtils.equals(header.encodingType, ENCODING_BASE64)) {
+ throw new IOException("Unexpected encoding: " + header.encodingType);
+ }
+
+ // Read pass the first boundary string.
+ for (;;) {
+ String line = in.readLine();
+ if (line == null) {
+ throw new IOException("Unexpected EOF before first boundary @ " +
+ in.getLineNumber());
+ }
+ if (line.equals("--" + header.boundary)) {
+ break;
+ }
+ }
+
+ // Parse each MIME part.
+ Map<String, byte[]> mimeParts = new HashMap<>();
+ boolean isLast = false;
+ do {
+ MimePart mimePart = parseMimePart(in, header.boundary);
+ mimeParts.put(mimePart.type, mimePart.data);
+ isLast = mimePart.isLast;
+ } while(!isLast);
+ return mimeParts;
+ }
+
+ /**
+ * Parse a MIME (Multipurpose Internet Mail Extension) part. We expect the data to
+ * be encoded in base64.
+ *
+ * @param in Input stream to read the data from
+ * @param boundary Boundary string indicate the end of the part
+ * @return {@link MimePart}
+ * @throws IOException
+ */
+ private static MimePart parseMimePart(LineNumberReader in, String boundary)
+ throws IOException {
+ MimeHeader header = parseHeaders(in);
+ // Expect encoding type to be base64.
+ if (!TextUtils.equals(header.encodingType, ENCODING_BASE64)) {
+ throw new IOException("Unexpected encoding type: " + header.encodingType);
+ }
+
+ // Check for a valid content type.
+ if (!TextUtils.equals(header.contentType, TYPE_PASSPOINT_PROFILE) &&
+ !TextUtils.equals(header.contentType, TYPE_CA_CERT) &&
+ !TextUtils.equals(header.contentType, TYPE_PKCS12)) {
+ throw new IOException("Unexpected content type: " + header.contentType);
+ }
+
+ StringBuilder text = new StringBuilder();
+ boolean isLast = false;
+ String partBoundary = "--" + boundary;
+ String endBoundary = partBoundary + "--";
+ for (;;) {
+ String line = in.readLine();
+ if (line == null) {
+ throw new IOException("Unexpected EOF file in body @ " + in.getLineNumber());
+ }
+ // Check for boundary line.
+ if (line.startsWith(partBoundary)) {
+ if (line.equals(endBoundary)) {
+ isLast = true;
+ }
+ break;
+ }
+ text.append(line);
+ }
+
+ MimePart part = new MimePart();
+ part.type = header.contentType;
+ part.data = Base64.decode(text.toString(), Base64.DEFAULT);
+ part.isLast = isLast;
+ return part;
+ }
+
+ /**
+ * Parse a MIME (Multipurpose Internet Mail Extension) header from the input stream.
+ * @param in Input stream to read from.
+ * @return {@link MimeHeader}
+ * @throws IOException
+ */
+ private static MimeHeader parseHeaders(LineNumberReader in)
+ throws IOException {
+ MimeHeader header = new MimeHeader();
+
+ // Read the header from the input stream.
+ Map<String, String> headers = readHeaders(in);
+
+ // Parse each header.
+ for (Map.Entry<String, String> entry : headers.entrySet()) {
+ switch (entry.getKey()) {
+ case CONTENT_TYPE:
+ Pair<String, String> value = parseContentType(entry.getValue());
+ header.contentType = value.first;
+ header.boundary = value.second;
+ break;
+ case CONTENT_TRANSFER_ENCODING:
+ header.encodingType = entry.getValue();
+ break;
+ default:
+ throw new IOException("Unexpected header: " + entry.getKey());
+ }
+ }
+ return header;
+ }
+
+ /**
+ * Parse the Content-Type header value. The value will contain the content type string and
+ * an optional boundary string separated by a ";". Below are examples of valid Content-Type
+ * header value:
+ * multipart/mixed; boundary={boundary}
+ * application/x-passpoint-profile
+ *
+ * @param contentType The Content-Type value string
+ * @return A pair of content type and boundary string
+ * @throws IOException
+ */
+ private static Pair<String, String> parseContentType(String contentType) throws IOException {
+ String[] attributes = contentType.toString().split(";");
+ String type = null;
+ String boundary = null;
+
+ if (attributes.length < 1 || attributes.length > 2) {
+ throw new IOException("Invalid Content-Type: " + contentType);
+ }
+
+ type = attributes[0].trim();
+ if (attributes.length == 2) {
+ boundary = attributes[1].trim();
+ if (!boundary.startsWith(BOUNDARY)) {
+ throw new IOException("Invalid Content-Type: " + contentType);
+ }
+ boundary = boundary.substring(BOUNDARY.length());
+ // Remove the leading and trailing quote if present.
+ if (boundary.length() > 1 && boundary.startsWith("\"") && boundary.endsWith("\"")) {
+ boundary = boundary.substring(1, boundary.length()-1);
+ }
+ }
+
+ return new Pair<String, String>(type, boundary);
+ }
+
+ /**
+ * Read the headers from the given input stream. The header section is terminated by
+ * an empty line.
+ *
+ * @param in The input stream to read from
+ * @return Map of key-value pairs.
+ * @throws IOException
+ */
+ private static Map<String, String> readHeaders(LineNumberReader in)
+ throws IOException {
+ Map<String, String> headers = new HashMap<>();
+ String line;
+ String name = null;
+ StringBuilder value = null;
+ for (;;) {
+ line = in.readLine();
+ if (line == null) {
+ throw new IOException("Missing line @ " + in.getLineNumber());
+ }
+
+ // End of headers section.
+ if (line.length() == 0 || line.trim().length() == 0) {
+ // Save the previous header line.
+ if (name != null) {
+ headers.put(name, value.toString());
+ }
+ break;
+ }
+
+ int nameEnd = line.indexOf(':');
+ if (nameEnd < 0) {
+ if (value != null) {
+ // Continuation line for the header value.
+ value.append(' ').append(line.trim());
+ } else {
+ throw new IOException("Bad header line: '" + line + "' @ " +
+ in.getLineNumber());
+ }
+ } else {
+ // New header line detected, make sure it doesn't start with a whitespace.
+ if (Character.isWhitespace(line.charAt(0))) {
+ throw new IOException("Illegal blank prefix in header line '" + line +
+ "' @ " + in.getLineNumber());
+ }
+
+ if (name != null) {
+ // Save the previous header line.
+ headers.put(name, value.toString());
+ }
+
+ // Setup the current header line.
+ name = line.substring(0, nameEnd).trim();
+ value = new StringBuilder();
+ value.append(line.substring(nameEnd+1).trim());
+ }
+ }
+ return headers;
+ }
+
+ /**
+ * Parse a CA (Certificate Authority) certificate data and convert it to a
+ * X509Certificate object.
+ *
+ * @param octets Certificate data
+ * @return X509Certificate
+ * @throws CertificateException
+ */
+ private static X509Certificate parseCACert(byte[] octets) throws CertificateException {
+ CertificateFactory factory = CertificateFactory.getInstance("X.509");
+ return (X509Certificate) factory.generateCertificate(new ByteArrayInputStream(octets));
+ }
+
+ private static Pair<PrivateKey, List<X509Certificate>> parsePkcs12(byte[] octets)
+ throws GeneralSecurityException, IOException {
+ KeyStore ks = KeyStore.getInstance("PKCS12");
+ ByteArrayInputStream in = new ByteArrayInputStream(octets);
+ ks.load(in, new char[0]);
+ in.close();
+
+ // Only expects one set of key and certificate chain.
+ if (ks.size() != 1) {
+ throw new IOException("Unexpected key size: " + ks.size());
+ }
+
+ String alias = ks.aliases().nextElement();
+ if (alias == null) {
+ throw new IOException("No alias found");
+ }
+
+ PrivateKey clientKey = (PrivateKey) ks.getKey(alias, null);
+ List<X509Certificate> clientCertificateChain = null;
+ Certificate[] chain = ks.getCertificateChain(alias);
+ if (chain != null) {
+ clientCertificateChain = new ArrayList<>();
+ for (Certificate certificate : chain) {
+ if (!(certificate instanceof X509Certificate)) {
+ throw new IOException("Unexpceted certificate type: " +
+ certificate.getClass());
+ }
+ clientCertificateChain.add((X509Certificate) certificate);
+ }
+ }
+ return new Pair<PrivateKey, List<X509Certificate>>(clientKey, clientCertificateChain);
+ }
+}
\ No newline at end of file
diff --git a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
index 18aae53..643753a 100644
--- a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
+++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
@@ -36,6 +36,27 @@
public HomeSP homeSp = null;
public Credential credential = null;
+ /**
+ * Constructor for creating PasspointConfiguration with default values.
+ */
+ public PasspointConfiguration() {}
+
+ /**
+ * Copy constructor.
+ *
+ * @param source The source to copy from
+ */
+ public PasspointConfiguration(PasspointConfiguration source) {
+ if (source != null) {
+ if (source.homeSp != null) {
+ homeSp = new HomeSP(source.homeSp);
+ }
+ if (source.credential != null) {
+ credential = new Credential(source.credential);
+ }
+ }
+ }
+
@Override
public int describeContents() {
return 0;
@@ -61,6 +82,21 @@
credential.equals(that.credential));
}
+ /**
+ * Validate the configuration data.
+ *
+ * @return true on success or false on failure
+ */
+ public boolean validate() {
+ if (homeSp == null || !homeSp.validate()) {
+ return false;
+ }
+ if (credential == null || !credential.validate()) {
+ return false;
+ }
+ return true;
+ }
+
public static final Creator<PasspointConfiguration> CREATOR =
new Creator<PasspointConfiguration>() {
@Override
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
index 92dbd8a..790dfaf 100644
--- a/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
+++ b/wifi/java/android/net/wifi/hotspot2/pps/Credential.java
@@ -16,15 +16,21 @@
package android.net.wifi.hotspot2.pps;
+import android.net.wifi.EAPConstants;
import android.net.wifi.ParcelUtil;
import android.os.Parcelable;
import android.os.Parcel;
import android.text.TextUtils;
+import android.util.Log;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.cert.CertificateEncodingException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
/**
* Class representing Credential subtree in the PerProviderSubscription (PPS)
@@ -40,6 +46,14 @@
* @hide
*/
public final class Credential implements Parcelable {
+ private static final String TAG = "Credential";
+
+ /**
+ * Max string length for realm. Refer to Credential/Realm node in Hotspot 2.0 Release 2
+ * Technical Specification Section 9.1 for more info.
+ */
+ private static final int MAX_REALM_LENGTH = 253;
+
/**
* The realm associated with this credential. It will be used to determine
* if this credential can be used to authenticate with a given hotspot by
@@ -53,6 +67,26 @@
*/
public static final class UserCredential implements Parcelable {
/**
+ * Maximum string length for username. Refer to Credential/UsernamePassword/Username
+ * node in Hotspot 2.0 Release 2 Technical Specification Section 9.1 for more info.
+ */
+ private static final int MAX_USERNAME_LENGTH = 63;
+
+ /**
+ * Maximum string length for password. Refer to Credential/UsernamePassword/Password
+ * in Hotspot 2.0 Release 2 Technical Specification Section 9.1 for more info.
+ */
+ private static final int MAX_PASSWORD_LENGTH = 255;
+
+ /**
+ * Supported Non-EAP inner methods. Refer to
+ * Credential/UsernamePassword/EAPMethod/InnerEAPType in Hotspot 2.0 Release 2 Technical
+ * Specification Section 9.1 for more info.
+ */
+ private static final Set<String> SUPPORTED_AUTH =
+ new HashSet<String>(Arrays.asList("PAP", "CHAP", "MS-CHAP", "MS-CHAP-V2"));
+
+ /**
* Username of the credential.
*/
public String username = null;
@@ -75,6 +109,25 @@
*/
public String nonEapInnerMethod = null;
+ /**
+ * Constructor for creating UserCredential with default values.
+ */
+ public UserCredential() {}
+
+ /**
+ * Copy constructor.
+ *
+ * @param source The source to copy from
+ */
+ public UserCredential(UserCredential source) {
+ if (source != null) {
+ username = source.username;
+ password = source.password;
+ eapType = source.eapType;
+ nonEapInnerMethod = source.nonEapInnerMethod;
+ }
+ }
+
@Override
public int describeContents() {
return 0;
@@ -104,6 +157,44 @@
TextUtils.equals(nonEapInnerMethod, that.nonEapInnerMethod);
}
+ /**
+ * Validate the configuration data.
+ *
+ * @return true on success or false on failure
+ */
+ public boolean validate() {
+ if (TextUtils.isEmpty(username)) {
+ Log.d(TAG, "Missing username");
+ return false;
+ }
+ if (username.length() > MAX_USERNAME_LENGTH) {
+ Log.d(TAG, "username exceeding maximum length: " + username.length());
+ return false;
+ }
+
+ if (TextUtils.isEmpty(password)) {
+ Log.d(TAG, "Missing password");
+ return false;
+ }
+ if (password.length() > MAX_PASSWORD_LENGTH) {
+ Log.d(TAG, "password exceeding maximum length: " + password.length());
+ return false;
+ }
+
+ // Only supports EAP-TTLS for user credential.
+ if (eapType != EAPConstants.EAP_TTLS) {
+ Log.d(TAG, "Invalid EAP Type for user credential: " + eapType);
+ return false;
+ }
+
+ // Verify Non-EAP inner method for EAP-TTLS.
+ if (!SUPPORTED_AUTH.contains(nonEapInnerMethod)) {
+ Log.d(TAG, "Invalid non-EAP inner method for EAP-TTLS: " + nonEapInnerMethod);
+ return false;
+ }
+ return true;
+ }
+
public static final Creator<UserCredential> CREATOR =
new Creator<UserCredential>() {
@Override
@@ -125,12 +216,22 @@
public UserCredential userCredential = null;
/**
- * Certificate based credential.
+ * Certificate based credential. This is used for EAP-TLS.
* Contains fields under PerProviderSubscription/Credential/DigitalCertificate subtree.
*/
public static final class CertificateCredential implements Parcelable {
/**
- * Certificate type. Valid values are "802.1ar" and "x509v3".
+ * Supported certificate types.
+ */
+ private static final String CERT_TYPE_X509V3 = "x509v3";
+
+ /**
+ * Certificate SHA-256 fingerprint length.
+ */
+ private static final int CERT_SHA256_FINGER_PRINT_LENGTH = 32;
+
+ /**
+ * Certificate type.
*/
public String certType = null;
@@ -139,6 +240,26 @@
*/
public byte[] certSha256FingerPrint = null;
+ /**
+ * Constructor for creating CertificateCredential with default values.
+ */
+ public CertificateCredential() {}
+
+ /**
+ * Copy constructor.
+ *
+ * @param source The source to copy from
+ */
+ public CertificateCredential(CertificateCredential source) {
+ if (source != null) {
+ certType = source.certType;
+ if (source.certSha256FingerPrint != null) {
+ certSha256FingerPrint = Arrays.copyOf(source.certSha256FingerPrint,
+ source.certSha256FingerPrint.length);
+ }
+ }
+ }
+
@Override
public int describeContents() {
return 0;
@@ -164,6 +285,24 @@
Arrays.equals(certSha256FingerPrint, that.certSha256FingerPrint);
}
+ /**
+ * Validate the configuration data.
+ *
+ * @return true on success or false on failure
+ */
+ public boolean validate() {
+ if (!TextUtils.equals(CERT_TYPE_X509V3, certType)) {
+ Log.d(TAG, "Unsupported certificate type: " + certType);
+ return false;
+ }
+ if (certSha256FingerPrint == null ||
+ certSha256FingerPrint.length != CERT_SHA256_FINGER_PRINT_LENGTH) {
+ Log.d(TAG, "Invalid SHA-256 fingerprint");
+ return false;
+ }
+ return true;
+ }
+
public static final Creator<CertificateCredential> CREATOR =
new Creator<CertificateCredential>() {
@Override
@@ -188,7 +327,14 @@
*/
public static final class SimCredential implements Parcelable {
/**
- * International Mobile device Subscriber Identity.
+ * Maximum string length for IMSI.
+ */
+ public static final int MAX_IMSI_LENGTH = 15;
+
+ /**
+ * International Mobile Subscriber Identity, is used to identify the user
+ * of a cellular network and is a unique identification associated with all
+ * cellular networks
*/
public String imsi = null;
@@ -200,6 +346,23 @@
*/
public int eapType = Integer.MIN_VALUE;
+ /**
+ * Constructor for creating SimCredential with default values.
+ */
+ public SimCredential() {}
+
+ /**
+ * Copy constructor
+ *
+ * @param source The source to copy from
+ */
+ public SimCredential(SimCredential source) {
+ if (source != null) {
+ imsi = source.imsi;
+ eapType = source.eapType;
+ }
+ }
+
@Override
public int describeContents() {
return 0;
@@ -225,6 +388,26 @@
dest.writeInt(eapType);
}
+ /**
+ * Validate the configuration data.
+ *
+ * @return true on success or false on failure
+ */
+ public boolean validate() {
+ // Note: this only validate the format of IMSI string itself. Additional verification
+ // will be done by WifiService at the time of provisioning to verify against the IMSI
+ // of the SIM card installed in the device.
+ if (!verifyImsi()) {
+ return false;
+ }
+ if (eapType != EAPConstants.EAP_SIM && eapType != EAPConstants.EAP_AKA &&
+ eapType != EAPConstants.EAP_AKA_PRIME) {
+ Log.d(TAG, "Invalid EAP Type for SIM credential: " + eapType);
+ return false;
+ }
+ return true;
+ }
+
public static final Creator<SimCredential> CREATOR =
new Creator<SimCredential>() {
@Override
@@ -240,6 +423,43 @@
return new SimCredential[size];
}
};
+
+ /**
+ * Verify the IMSI (International Mobile Subscriber Identity) string. The string
+ * should contain zero or more numeric digits, and might ends with a "*" for prefix
+ * matching.
+ *
+ * @return true if IMSI is valid, false otherwise.
+ */
+ private boolean verifyImsi() {
+ if (TextUtils.isEmpty(imsi)) {
+ Log.d(TAG, "Missing IMSI");
+ return false;
+ }
+ if (imsi.length() > MAX_IMSI_LENGTH) {
+ Log.d(TAG, "IMSI exceeding maximum length: " + imsi.length());
+ return false;
+ }
+
+ // Locate the first non-digit character.
+ int nonDigit;
+ char stopChar = '\0';
+ for (nonDigit = 0; nonDigit < imsi.length(); nonDigit++) {
+ stopChar = imsi.charAt(nonDigit);
+ if (stopChar < '0' || stopChar > '9') {
+ break;
+ }
+ }
+
+ if (nonDigit == imsi.length()) {
+ return true;
+ }
+ else if (nonDigit == imsi.length()-1 && stopChar == '*') {
+ // Prefix matching.
+ return true;
+ }
+ return false;
+ }
}
public SimCredential simCredential = null;
@@ -258,6 +478,37 @@
*/
public PrivateKey clientPrivateKey = null;
+ /**
+ * Constructor for creating Credential with default values.
+ */
+ public Credential() {}
+
+ /**
+ * Copy constructor.
+ *
+ * @param source The source to copy from
+ */
+ public Credential(Credential source) {
+ if (source != null) {
+ realm = source.realm;
+ if (source.userCredential != null) {
+ userCredential = new UserCredential(source.userCredential);
+ }
+ if (source.certCredential != null) {
+ certCredential = new CertificateCredential(source.certCredential);
+ }
+ if (source.simCredential != null) {
+ simCredential = new SimCredential(source.simCredential);
+ }
+ if (source.clientCertificateChain != null) {
+ clientCertificateChain = Arrays.copyOf(source.clientCertificateChain,
+ source.clientCertificateChain.length);
+ }
+ caCertificate = source.caCertificate;
+ clientPrivateKey = source.clientPrivateKey;
+ }
+ }
+
@Override
public int describeContents() {
return 0;
@@ -296,6 +547,42 @@
isPrivateKeyEquals(clientPrivateKey, that.clientPrivateKey);
}
+ /**
+ * Validate the configuration data.
+ *
+ * @return true on success or false on failure
+ */
+ public boolean validate() {
+ if (TextUtils.isEmpty(realm)) {
+ Log.d(TAG, "Missing realm");
+ return false;
+ }
+ if (realm.length() > MAX_REALM_LENGTH) {
+ Log.d(TAG, "realm exceeding maximum length: " + realm.length());
+ return false;
+ }
+
+ // Verify the credential.
+ if (userCredential != null) {
+ if (!verifyUserCredential()) {
+ return false;
+ }
+ } else if (certCredential != null) {
+ if (!verifyCertCredential()) {
+ return false;
+ }
+ } else if (simCredential != null) {
+ if (!verifySimCredential()) {
+ return false;
+ }
+ } else {
+ Log.d(TAG, "Missing required credential");
+ return false;
+ }
+
+ return true;
+ }
+
public static final Creator<Credential> CREATOR =
new Creator<Credential>() {
@Override
@@ -317,6 +604,91 @@
}
};
+ /**
+ * Verify user credential.
+ *
+ * @return true if user credential is valid, false otherwise.
+ */
+ private boolean verifyUserCredential() {
+ if (userCredential == null) {
+ Log.d(TAG, "Missing user credential");
+ return false;
+ }
+ if (certCredential != null || simCredential != null) {
+ Log.d(TAG, "Contained more than one type of credential");
+ return false;
+ }
+ if (!userCredential.validate()) {
+ return false;
+ }
+ if (caCertificate == null) {
+ Log.d(TAG, "Missing CA Certificate for user credential");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Verify certificate credential, which is used for EAP-TLS. This will verify
+ * that the necessary client key and certificates are provided.
+ *
+ * @return true if certificate credential is valid, false otherwise.
+ */
+ private boolean verifyCertCredential() {
+ if (certCredential == null) {
+ Log.d(TAG, "Missing certificate credential");
+ return false;
+ }
+ if (userCredential != null || simCredential != null) {
+ Log.d(TAG, "Contained more than one type of credential");
+ return false;
+ }
+
+ if (!certCredential.validate()) {
+ return false;
+ }
+
+ // Verify required key and certificates for certificate credential.
+ if (caCertificate == null) {
+ Log.d(TAG, "Missing CA Certificate for certificate credential");
+ return false;
+ }
+ if (clientPrivateKey == null) {
+ Log.d(TAG, "Missing client private key for certificate credential");
+ return false;
+ }
+ try {
+ // Verify SHA-256 fingerprint for client certificate.
+ if (!verifySha256Fingerprint(clientCertificateChain,
+ certCredential.certSha256FingerPrint)) {
+ Log.d(TAG, "SHA-256 fingerprint mismatch");
+ return false;
+ }
+ } catch (NoSuchAlgorithmException | CertificateEncodingException e) {
+ Log.d(TAG, "Failed to verify SHA-256 fingerprint: " + e.getMessage());
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Verify SIM credential.
+ *
+ * @return true if SIM credential is valid, false otherwise.
+ */
+ private boolean verifySimCredential() {
+ if (simCredential == null) {
+ Log.d(TAG, "Missing SIM credential");
+ return false;
+ }
+ if (userCredential != null || certCredential != null) {
+ Log.d(TAG, "Contained more than one type of credential");
+ return false;
+ }
+ return simCredential.validate();
+ }
+
private static boolean isPrivateKeyEquals(PrivateKey key1, PrivateKey key2) {
if (key1 == null && key2 == null) {
return true;
@@ -373,4 +745,31 @@
return true;
}
+
+ /**
+ * Verify that the digest for a certificate in the certificate chain matches expected
+ * fingerprint. The certificate that matches the fingerprint is the client certificate.
+ *
+ * @param certChain Chain of certificates
+ * @param expectedFingerprint The expected SHA-256 digest of the client certificate
+ * @return true if the certificate chain contains a matching certificate, false otherwise
+ * @throws NoSuchAlgorithmException
+ * @throws CertificateEncodingException
+ */
+ private static boolean verifySha256Fingerprint(X509Certificate[] certChain,
+ byte[] expectedFingerprint)
+ throws NoSuchAlgorithmException, CertificateEncodingException {
+ if (certChain == null) {
+ return false;
+ }
+ MessageDigest digester = MessageDigest.getInstance("SHA-256");
+ for (X509Certificate certificate : certChain) {
+ digester.reset();
+ byte[] fingerprint = digester.digest(certificate.getEncoded());
+ if (Arrays.equals(expectedFingerprint, fingerprint)) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.java b/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.java
index 2acc8be..d4a5792 100644
--- a/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.java
+++ b/wifi/java/android/net/wifi/hotspot2/pps/HomeSP.java
@@ -19,6 +19,7 @@
import android.os.Parcelable;
import android.os.Parcel;
import android.text.TextUtils;
+import android.util.Log;
import java.util.Arrays;
@@ -34,6 +35,8 @@
* @hide
*/
public final class HomeSP implements Parcelable {
+ private static final String TAG = "HomeSP";
+
/**
* FQDN (Fully Qualified Domain Name) of this home service provider.
*/
@@ -50,6 +53,27 @@
*/
public long[] roamingConsortiumOIs = null;
+ /**
+ * Constructor for creating HomeSP with default values.
+ */
+ public HomeSP() {}
+
+ /**
+ * Copy constructor.
+ *
+ * @param source The source to copy from
+ */
+ public HomeSP(HomeSP source) {
+ if (source != null) {
+ fqdn = source.fqdn;
+ friendlyName = source.friendlyName;
+ if (source.roamingConsortiumOIs != null) {
+ roamingConsortiumOIs = Arrays.copyOf(source.roamingConsortiumOIs,
+ source.roamingConsortiumOIs.length);
+ }
+ }
+ }
+
@Override
public int describeContents() {
return 0;
@@ -77,6 +101,23 @@
Arrays.equals(roamingConsortiumOIs, that.roamingConsortiumOIs);
}
+ /**
+ * Validate HomeSP data.
+ *
+ * @return true on success or false on failure
+ */
+ public boolean validate() {
+ if (TextUtils.isEmpty(fqdn)) {
+ Log.d(TAG, "Missing FQDN");
+ return false;
+ }
+ if (TextUtils.isEmpty(friendlyName)) {
+ Log.d(TAG, "Missing friendly name");
+ return false;
+ }
+ return true;
+ }
+
public static final Creator<HomeSP> CREATOR =
new Creator<HomeSP>() {
@Override
diff --git a/wifi/java/android/net/wifi/nan/LvBufferUtils.java b/wifi/java/android/net/wifi/nan/LvBufferUtils.java
deleted file mode 100644
index eb56070..0000000
--- a/wifi/java/android/net/wifi/nan/LvBufferUtils.java
+++ /dev/null
@@ -1,340 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi.nan;
-
-import android.annotation.Nullable;
-
-import libcore.io.Memory;
-
-import java.nio.ByteOrder;
-import java.util.Iterator;
-
-/**
- * Utility class to construct and parse byte arrays using the LV format -
- * Length/Value format. The utilities accept a configuration of the size of
- * the Length field.
- *
- * @hide PROPOSED_NAN_API
- */
-public class LvBufferUtils {
- private LvBufferUtils() {
- // no reason to ever create this class
- }
-
- /**
- * Utility class to construct byte arrays using the LV format - Length/Value.
- * <p>
- * A constructor is created specifying the size of the Length (L) field.
- * <p>
- * The byte array is either provided (using
- * {@link LvBufferUtils.LvConstructor#wrap(byte[])}) or allocated (using
- * {@link LvBufferUtils.LvConstructor#allocate(int)}).
- * <p>
- * Values are added to the structure using the {@code LvConstructor.put*()}
- * methods.
- * <p>
- * The final byte array is obtained using {@link LvBufferUtils.LvConstructor#getArray()}.
- */
- public static class LvConstructor {
- private TlvBufferUtils.TlvConstructor mTlvImpl;
-
- /**
- * Define a LV constructor with the specified size of the Length (L) field.
- *
- * @param lengthSize Number of bytes used for the Length (L) field.
- * Values of 1 or 2 bytes are allowed.
- */
- public LvConstructor(int lengthSize) {
- mTlvImpl = new TlvBufferUtils.TlvConstructor(0, lengthSize);
- }
-
- /**
- * Set the byte array to be used to construct the LV.
- *
- * @param array Byte array to be formatted.
- * @return The constructor to facilitate chaining
- * {@code ctr.putXXX(..).putXXX(..)}.
- */
- public LvBufferUtils.LvConstructor wrap(@Nullable byte[] array) {
- mTlvImpl.wrap(array);
- return this;
- }
-
- /**
- * Allocates a new byte array to be used ot construct a LV.
- *
- * @param capacity The size of the byte array to be allocated.
- * @return The constructor to facilitate chaining
- * {@code ctr.putXXX(..).putXXX(..)}.
- */
- public LvBufferUtils.LvConstructor allocate(int capacity) {
- mTlvImpl.allocate(capacity);
- return this;
- }
-
- /**
- * Copies a byte into the LV array.
- *
- * @param b The byte to be inserted into the structure.
- * @return The constructor to facilitate chaining
- * {@code ctr.putXXX(..).putXXX(..)}.
- */
- public LvBufferUtils.LvConstructor putByte(byte b) {
- mTlvImpl.putByte(0, b);
- return this;
- }
-
- /**
- * Copies a byte array into the LV.
- *
- * @param array The array to be copied into the LV structure.
- * @param offset Start copying from the array at the specified offset.
- * @param length Copy the specified number (length) of bytes from the
- * array.
- * @return The constructor to facilitate chaining
- * {@code ctr.putXXX(..).putXXX(..)}.
- */
- public LvBufferUtils.LvConstructor putByteArray(@Nullable byte[] array, int offset,
- int length) {
- mTlvImpl.putByteArray(0, array, offset, length);
- return this;
- }
-
- /**
- * Copies a byte array into the LV.
- *
- * @param array The array to be copied (in full) into the LV structure.
- * @return The constructor to facilitate chaining
- * {@code ctr.putXXX(..).putXXX(..)}.
- */
- public LvBufferUtils.LvConstructor putByteArray(int type, @Nullable byte[] array) {
- return putByteArray(array, 0, (array == null) ? 0 : array.length);
- }
-
- /**
- * Places a zero length element (i.e. Length field = 0) into the LV.
- *
- * @return The constructor to facilitate chaining
- * {@code ctr.putXXX(..).putXXX(..)}.
- */
- public LvBufferUtils.LvConstructor putZeroLengthElement() {
- mTlvImpl.putZeroLengthElement(0);
- return this;
- }
-
- /**
- * Copies short into the LV.
- *
- * @param data The short to be inserted into the structure.
- * @return The constructor to facilitate chaining
- * {@code ctr.putXXX(..).putXXX(..)}.
- */
- public LvBufferUtils.LvConstructor putShort(short data) {
- mTlvImpl.putShort(0, data);
- return this;
- }
-
- /**
- * Copies integer into the LV.
- *
- * @param data The integer to be inserted into the structure.
- * @return The constructor to facilitate chaining
- * {@code ctr.putXXX(..).putXXX(..)}.
- */
- public LvBufferUtils.LvConstructor putInt(int data) {
- mTlvImpl.putInt(0, data);
- return this;
- }
-
- /**
- * Copies a String's byte representation into the LV.
- *
- * @param data The string whose bytes are to be inserted into the
- * structure.
- * @return The constructor to facilitate chaining
- * {@code ctr.putXXX(..).putXXX(..)}.
- */
- public LvBufferUtils.LvConstructor putString(@Nullable String data) {
- mTlvImpl.putString(0, data);
- return this;
- }
-
- /**
- * Returns the constructed LV formatted byte-array. This array is a copy of the wrapped
- * or allocated array - truncated to just the significant bytes - i.e. those written into
- * the LV.
- *
- * @return The byte array containing the LV formatted structure.
- */
- public byte[] getArray() {
- return mTlvImpl.getArray();
- }
- }
-
- /**
- * Utility class used when iterating over an LV formatted byte-array. Use
- * {@link LvBufferUtils.LvIterable} to iterate over array. A {@link LvBufferUtils.LvElement}
- * represents each entry in a LV formatted byte-array.
- */
- public static class LvElement {
- /**
- * The Length (L) field of the current LV element.
- */
- public int length;
-
- /**
- * The Value (V) field - a raw byte array representing the current LV
- * element where the entry starts at {@link LvBufferUtils.LvElement#offset}.
- */
- public byte[] refArray;
-
- /**
- * The offset to be used into {@link LvBufferUtils.LvElement#refArray} to access the
- * raw data representing the current LV element.
- */
- public int offset;
-
- private LvElement(int length, @Nullable byte[] refArray, int offset) {
- this.length = length;
- this.refArray = refArray;
- this.offset = offset;
- }
-
- /**
- * Utility function to return a byte representation of a LV element of
- * length 1. Note: an attempt to call this function on a LV item whose
- * {@link LvBufferUtils.LvElement#length} is != 1 will result in an exception.
- *
- * @return byte representation of current LV element.
- */
- public byte getByte() {
- if (length != 1) {
- throw new IllegalArgumentException(
- "Accesing a byte from a LV element of length " + length);
- }
- return refArray[offset];
- }
-
- /**
- * Utility function to return a short representation of a LV element of
- * length 2. Note: an attempt to call this function on a LV item whose
- * {@link LvBufferUtils.LvElement#length} is != 2 will result in an exception.
- *
- * @return short representation of current LV element.
- */
- public short getShort() {
- if (length != 2) {
- throw new IllegalArgumentException(
- "Accesing a short from a LV element of length " + length);
- }
- return Memory.peekShort(refArray, offset, ByteOrder.BIG_ENDIAN);
- }
-
- /**
- * Utility function to return an integer representation of a LV element
- * of length 4. Note: an attempt to call this function on a LV item
- * whose {@link LvBufferUtils.LvElement#length} is != 4 will result in an exception.
- *
- * @return integer representation of current LV element.
- */
- public int getInt() {
- if (length != 4) {
- throw new IllegalArgumentException(
- "Accesing an int from a LV element of length " + length);
- }
- return Memory.peekInt(refArray, offset, ByteOrder.BIG_ENDIAN);
- }
-
- /**
- * Utility function to return a String representation of a LV element.
- *
- * @return String representation of the current LV element.
- */
- public String getString() {
- return new String(refArray, offset, length);
- }
- }
-
- /**
- * Utility class to iterate over a LV formatted byte-array.
- */
- public static class LvIterable implements Iterable<LvBufferUtils.LvElement> {
- private final TlvBufferUtils.TlvIterable mTlvIterable;
-
- /**
- * Constructs an LvIterable object - specifying the format of the LV
- * (the size of the Length field), and the byte array whose data is to be parsed.
- *
- * @param lengthSize Number of bytes sued for the Length (L) field.
- * Values values are 1 or 2 bytes.
- * @param array The LV formatted byte-array to parse.
- */
- public LvIterable(int lengthSize, @Nullable byte[] array) {
- mTlvIterable = new TlvBufferUtils.TlvIterable(0, lengthSize, array);
- }
-
- /**
- * Prints out a parsed representation of the LV-formatted byte array.
- * Whenever possible bytes, shorts, and integer are printed out (for
- * fields whose length is 1, 2, or 4 respectively).
- */
- @Override
- public String toString() {
- return mTlvIterable.toString();
- }
-
- /**
- * Returns an iterator to step through a LV formatted byte-array. The
- * individual elements returned by the iterator are {@link LvBufferUtils.LvElement}.
- */
- @Override
- public Iterator<LvBufferUtils.LvElement> iterator() {
- return new Iterator<LvBufferUtils.LvElement>() {
- private Iterator<TlvBufferUtils.TlvElement> mTlvIterator = mTlvIterable.iterator();
-
- @Override
- public boolean hasNext() {
- return mTlvIterator.hasNext();
- }
-
- @Override
- public LvBufferUtils.LvElement next() {
- TlvBufferUtils.TlvElement tlvE = mTlvIterator.next();
-
- return new LvElement(tlvE.length, tlvE.refArray, tlvE.offset);
- }
-
- @Override
- public void remove() {
- throw new UnsupportedOperationException();
- }
- };
- }
- }
-
- /**
- * Validates that a LV array is constructed correctly. I.e. that its specified Length
- * fields correctly fill the specified length (and do not overshoot).
- *
- * @param array The LV array to verify.
- * @param lengthSize The size (in bytes) of the length field. Valid values are 1 or 2.
- * @return A boolean indicating whether the array is valid (true) or invalid (false).
- */
- public static boolean isValid(@Nullable byte[] array, int lengthSize) {
- return TlvBufferUtils.isValid(array, 0, lengthSize);
- }
-}
diff --git a/wifi/java/android/net/wifi/nan/WifiNanAttachCallback.java b/wifi/java/android/net/wifi/nan/WifiNanAttachCallback.java
deleted file mode 100644
index d8c310b..0000000
--- a/wifi/java/android/net/wifi/nan/WifiNanAttachCallback.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi.nan;
-
-/**
- * Base class for NAN attach callbacks. Should be extended by applications and set when calling
- * {@link WifiNanManager#attach(android.os.Handler, WifiNanAttachCallback)}. These are callbacks
- * applying to the NAN connection as a whole - not to specific publish or subscribe sessions -
- * for that see {@link WifiNanDiscoverySessionCallback}.
- *
- * @hide PROPOSED_NAN_API
- */
-public class WifiNanAttachCallback {
- /**
- * Called when NAN attach operation
- * {@link WifiNanManager#attach(android.os.Handler, WifiNanAttachCallback)}
- * is completed and that we can now start discovery sessions or connections.
- *
- * @param session The NAN object on which we can execute further NAN operations - e.g.
- * discovery, connections.
- */
- public void onAttached(WifiNanSession session) {
- /* empty */
- }
-
- /**
- * Called when NAN attach operation
- * {@link WifiNanManager#attach(android.os.Handler, WifiNanAttachCallback)} failed.
- */
- public void onAttachFailed() {
- /* empty */
- }
-}
diff --git a/wifi/java/android/net/wifi/nan/WifiNanCharacteristics.aidl b/wifi/java/android/net/wifi/nan/WifiNanCharacteristics.aidl
deleted file mode 100644
index e562a00..0000000
--- a/wifi/java/android/net/wifi/nan/WifiNanCharacteristics.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi.nan;
-
-parcelable WifiNanCharacteristics;
diff --git a/wifi/java/android/net/wifi/nan/WifiNanDiscoveryBaseSession.java b/wifi/java/android/net/wifi/nan/WifiNanDiscoveryBaseSession.java
deleted file mode 100644
index 17e974b..0000000
--- a/wifi/java/android/net/wifi/nan/WifiNanDiscoveryBaseSession.java
+++ /dev/null
@@ -1,289 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi.nan;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.net.wifi.RttManager;
-import android.util.Log;
-
-import dalvik.system.CloseGuard;
-
-import java.lang.ref.WeakReference;
-
-/**
- * A class representing a single publish or subscribe NAN session. This object
- * will not be created directly - only its child classes are available:
- * {@link WifiNanPublishDiscoverySession} and {@link WifiNanSubscribeDiscoverySession}. This
- * class provides functionality common to both publish and subscribe discovery sessions:
- * <ul>
- * <li>Sending messages: {@link #sendMessage(Object, int, byte[])} or
- * {@link #sendMessage(Object, int, byte[], int)} methods.
- * <li>Creating a network-specifier when requesting a NAN connection:
- * {@link #createNetworkSpecifier(int, Object, byte[])}.
- * </ul>
- * The {@link #destroy()} method must be called to destroy discovery sessions once they are
- * no longer needed.
- *
- * @hide PROPOSED_NAN_API
- */
-public class WifiNanDiscoveryBaseSession {
- private static final String TAG = "WifiNanDiscoveryBaseSsn";
- private static final boolean DBG = false;
- private static final boolean VDBG = false; // STOPSHIP if true
-
- private static final int MAX_SEND_RETRY_COUNT = 5;
-
- /** @hide */
- protected WeakReference<WifiNanManager> mMgr;
- /** @hide */
- protected final int mClientId;
- /** @hide */
- protected final int mSessionId;
- /** @hide */
- protected boolean mTerminated = false;
-
- private final CloseGuard mCloseGuard = CloseGuard.get();
-
- /**
- * Return the maximum permitted retry count when sending messages using
- * {@link #sendMessage(Object, int, byte[], int)}.
- *
- * @return Maximum retry count when sending messages.
- */
- public static int getMaxSendRetryCount() {
- return MAX_SEND_RETRY_COUNT;
- }
-
- /** @hide */
- public WifiNanDiscoveryBaseSession(WifiNanManager manager, int clientId, int sessionId) {
- if (VDBG) {
- Log.v(TAG, "New discovery session created: manager=" + manager + ", clientId="
- + clientId + ", sessionId=" + sessionId);
- }
-
- mMgr = new WeakReference<>(manager);
- mClientId = clientId;
- mSessionId = sessionId;
-
- mCloseGuard.open("destroy");
- }
-
- /**
- * Destroy the publish or subscribe session - free any resources, and stop
- * transmitting packets on-air (for an active session) or listening for
- * matches (for a passive session). The session may not be used for any
- * additional operations after its destruction.
- * <p>
- * This operation must be done on a session which is no longer needed. Otherwise system
- * resources will continue to be utilized until the application exits. The only
- * exception is a session for which we received a termination callback,
- * {@link WifiNanDiscoverySessionCallback#onSessionTerminated(int)}.
- */
- public void destroy() {
- WifiNanManager mgr = mMgr.get();
- if (mgr == null) {
- Log.w(TAG, "destroy: called post GC on WifiNanManager");
- return;
- }
- mgr.terminateSession(mClientId, mSessionId);
- mTerminated = true;
- mMgr.clear();
- mCloseGuard.close();
- }
-
- /**
- * Sets the status of the session to terminated - i.e. an indication that
- * already terminated rather than executing a termination.
- *
- * @hide
- */
- public void setTerminated() {
- if (mTerminated) {
- Log.w(TAG, "terminate: already terminated.");
- return;
- }
- mTerminated = true;
- mMgr.clear();
- mCloseGuard.close();
- }
-
- /** @hide */
- @Override
- protected void finalize() throws Throwable {
- try {
- if (!mTerminated) {
- mCloseGuard.warnIfOpen();
- destroy();
- }
- } finally {
- super.finalize();
- }
- }
-
- /**
- * Sends a message to the specified destination. NAN messages are transmitted in the context
- * of a discovery session - executed subsequent to a publish/subscribe
- * {@link WifiNanDiscoverySessionCallback#onServiceDiscovered(Object, byte[], byte[])} event.
- * <p>
- * NAN messages are not guaranteed delivery. Callbacks on
- * {@link WifiNanDiscoverySessionCallback} indicate message was transmitted successfully,
- * {@link WifiNanDiscoverySessionCallback#onMessageSent(int)}, or transmission failed
- * (possibly after several retries) -
- * {@link WifiNanDiscoverySessionCallback#onMessageSendFailed(int)}.
- * <p>
- * The peer will get a callback indicating a message was received using
- * {@link WifiNanDiscoverySessionCallback#onMessageReceived(Object, byte[])}.
- *
- * @param peerHandle The peer's handle for the message. Must be a result of an
- * {@link WifiNanDiscoverySessionCallback#onServiceDiscovered(Object, byte[], byte[])}
- * or
- * {@link WifiNanDiscoverySessionCallback#onMessageReceived(Object, byte[])} events.
- * @param messageId An arbitrary integer used by the caller to identify the message. The same
- * integer ID will be returned in the callbacks indicating message send success or
- * failure. The {@code messageId} is not used internally by the NAN service - it
- * can be arbitrary and non-unique.
- * @param message The message to be transmitted.
- * @param retryCount An integer specifying how many additional service-level (as opposed to PHY
- * or MAC level) retries should be attempted if there is no ACK from the receiver
- * (note: no retransmissions are attempted in other failure cases). A value of 0
- * indicates no retries. Max permitted value is {@link #getMaxSendRetryCount()}.
- */
- public void sendMessage(@NonNull Object peerHandle, int messageId, @Nullable byte[] message,
- int retryCount) {
- if (mTerminated) {
- Log.w(TAG, "sendMessage: called on terminated session");
- return;
- } else {
- WifiNanManager mgr = mMgr.get();
- if (mgr == null) {
- Log.w(TAG, "sendMessage: called post GC on WifiNanManager");
- return;
- }
-
- mgr.sendMessage(mClientId, mSessionId, peerHandle, message, messageId, retryCount);
- }
- }
-
- /**
- * Sends a message to the specified destination. NAN messages are transmitted in the context
- * of a discovery session - executed subsequent to a publish/subscribe
- * {@link WifiNanDiscoverySessionCallback#onServiceDiscovered(Object, byte[], byte[])} event.
- * <p>
- * NAN messages are not guaranteed delivery. Callbacks on
- * {@link WifiNanDiscoverySessionCallback} indicate message was transmitted successfully,
- * {@link WifiNanDiscoverySessionCallback#onMessageSent(int)}, or transmission failed
- * (possibly after several retries) -
- * {@link WifiNanDiscoverySessionCallback#onMessageSendFailed(int)}.
- * <p>
- * The peer will get a callback indicating a message was received using
- * {@link WifiNanDiscoverySessionCallback#onMessageReceived(Object, byte[])}.
- * Equivalent to {@link #sendMessage(Object, int, byte[], int)} with a {@code retryCount} of
- * 0.
- *
- * @param peerHandle The peer's handle for the message. Must be a result of an
- * {@link WifiNanDiscoverySessionCallback#onServiceDiscovered(Object, byte[], byte[])}
- * or
- * {@link WifiNanDiscoverySessionCallback#onMessageReceived(Object, byte[])} events.
- * @param messageId An arbitrary integer used by the caller to identify the message. The same
- * integer ID will be returned in the callbacks indicating message send success or
- * failure. The {@code messageId} is not used internally by the NAN service - it
- * can be arbitrary and non-unique.
- * @param message The message to be transmitted.
- */
- public void sendMessage(@NonNull Object peerHandle, int messageId, @Nullable byte[] message) {
- sendMessage(peerHandle, messageId, message, 0);
- }
-
- /**
- * Start a ranging operation with the specified peers. The peer IDs are obtained from an
- * {@link WifiNanDiscoverySessionCallback#onServiceDiscovered(Object, byte[], byte[])} or
- * {@link WifiNanDiscoverySessionCallback#onMessageReceived(Object, byte[])} operation - can
- * only range devices which are part of an ongoing discovery session.
- *
- * @param params RTT parameters - each corresponding to a specific peer ID (the array sizes
- * must be identical). The
- * {@link android.net.wifi.RttManager.RttParams#bssid} member must be set to
- * a peer ID - not to a MAC address.
- * @param listener The listener to receive the results of the ranging session.
- * @hide PROPOSED_NAN_SYSTEM_API [TODO: b/28847998 - track RTT API & visilibity]
- */
- public void startRanging(RttManager.RttParams[] params, RttManager.RttListener listener) {
- if (mTerminated) {
- Log.w(TAG, "startRanging: called on terminated session");
- return;
- } else {
- WifiNanManager mgr = mMgr.get();
- if (mgr == null) {
- Log.w(TAG, "startRanging: called post GC on WifiNanManager");
- return;
- }
-
- mgr.startRanging(mClientId, mSessionId, params, listener);
- }
- }
-
- /**
- * Create a {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} for a
- * WiFi NAN connection to the specified peer. The
- * {@link android.net.NetworkRequest.Builder#addTransportType(int)} should be set to
- * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_NAN}.
- * <p>
- * This method should be used when setting up a connection with a peer discovered through NAN
- * discovery or communication (in such scenarios the MAC address of the peer is shielded by
- * an opaque peer ID handle). If a NAN connection is needed to a peer discovered using other
- * OOB (out-of-band) mechanism then use the alternative
- * {@link WifiNanSession#createNetworkSpecifier(int, byte[], byte[])} method - which uses the
- * peer's MAC address.
- *
- * @param role The role of this device:
- * {@link WifiNanManager#WIFI_NAN_DATA_PATH_ROLE_INITIATOR} or
- * {@link WifiNanManager#WIFI_NAN_DATA_PATH_ROLE_RESPONDER}
- * @param peerHandle The peer's handle obtained through
- * {@link WifiNanDiscoverySessionCallback#onServiceDiscovered(Object, byte[], byte[])} or
- * {@link WifiNanDiscoverySessionCallback#onMessageReceived(Object, byte[])}. On a RESPONDER
- * this value is used to gate the acceptance of a connection request from only
- * that peer. A RESPONDER may specified a null - indicating that it will accept
- * connection requests from any device.
- * @param token An arbitrary token (message) to be used to match connection initiation request
- * to a responder setup. A RESPONDER is set up with a {@code token} which must
- * be matched by the token provided by the INITIATOR. A null token is permitted
- * on the RESPONDER and matches any peer token. An empty ({@code ""}) token is
- * not the same as a null token and requires the peer token to be empty as well.
- *
- * @return A string to be used to construct
- * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to
- * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest,android.net.ConnectivityManager.NetworkCallback)}
- * [or other varieties of that API].
- */
- public String createNetworkSpecifier(@WifiNanManager.DataPathRole int role,
- @Nullable Object peerHandle, @Nullable byte[] token) {
- if (mTerminated) {
- Log.w(TAG, "createNetworkSpecifier: called on terminated session");
- return null;
- } else {
- WifiNanManager mgr = mMgr.get();
- if (mgr == null) {
- Log.w(TAG, "createNetworkSpecifier: called post GC on WifiNanManager");
- return null;
- }
-
- return mgr.createNetworkSpecifier(mClientId, role, mSessionId, peerHandle, token);
- }
- }
-}
diff --git a/wifi/java/android/net/wifi/nan/WifiNanDiscoverySessionCallback.java b/wifi/java/android/net/wifi/nan/WifiNanDiscoverySessionCallback.java
deleted file mode 100644
index 271f420..0000000
--- a/wifi/java/android/net/wifi/nan/WifiNanDiscoverySessionCallback.java
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.net.wifi.nan;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * Base class for NAN session events callbacks. Should be extended by
- * applications wanting notifications. The callbacks are set when a
- * publish or subscribe session is created using
- * {@link WifiNanSession#publish(android.os.Handler, PublishConfig, WifiNanDiscoverySessionCallback)}
- * or
- * {@link WifiNanSession#subscribe(android.os.Handler, SubscribeConfig, WifiNanDiscoverySessionCallback)} .
- * <p>
- * A single callback is set at session creation - it cannot be replaced.
- *
- * @hide PROPOSED_NAN_API
- */
-public class WifiNanDiscoverySessionCallback {
- /** @hide */
- @IntDef({
- TERMINATE_REASON_DONE, TERMINATE_REASON_FAIL })
- @Retention(RetentionPolicy.SOURCE)
- public @interface SessionTerminateCodes {
- }
-
- /**
- * Indicates that publish or subscribe session is done - all the
- * requested operations (per {@link PublishConfig} or
- * {@link SubscribeConfig}) have been executed. Failure reason flag for
- * {@link WifiNanDiscoverySessionCallback#onSessionTerminated(int)} callback.
- */
- public static final int TERMINATE_REASON_DONE = 100;
-
- /**
- * Indicates that publish or subscribe session is terminated due to a
- * failure.
- * Failure reason flag for
- * {@link WifiNanDiscoverySessionCallback#onSessionTerminated(int)} callback.
- */
- public static final int TERMINATE_REASON_FAIL = 101;
-
- /**
- * Called when a publish operation is started successfully in response to a
- * {@link WifiNanSession#publish(android.os.Handler, PublishConfig, WifiNanDiscoverySessionCallback)}
- * operation.
- *
- * @param session The {@link WifiNanPublishDiscoverySession} used to control the
- * discovery session.
- */
- public void onPublishStarted(@NonNull WifiNanPublishDiscoverySession session) {
- /* empty */
- }
-
- /**
- * Called when a subscribe operation is started successfully in response to a
- * {@link WifiNanSession#subscribe(android.os.Handler, SubscribeConfig, WifiNanDiscoverySessionCallback)}
- * operation.
- *
- * @param session The {@link WifiNanSubscribeDiscoverySession} used to control the
- * discovery session.
- */
- public void onSubscribeStarted(@NonNull WifiNanSubscribeDiscoverySession session) {
- /* empty */
- }
-
- /**
- * Called when a publish or subscribe discovery session configuration update request
- * succeeds. Called in response to
- * {@link WifiNanPublishDiscoverySession#updatePublish(PublishConfig)} or
- * {@link WifiNanSubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}.
- */
- public void onSessionConfigUpdated() {
- /* empty */
- }
-
- /**
- * Called when a publish or subscribe discovery session cannot be created:
- * {@link WifiNanSession#publish(android.os.Handler, PublishConfig, WifiNanDiscoverySessionCallback)}
- * or
- * {@link WifiNanSession#subscribe(android.os.Handler, SubscribeConfig, WifiNanDiscoverySessionCallback)},
- * or when a configuration update fails:
- * {@link WifiNanPublishDiscoverySession#updatePublish(PublishConfig)} or
- * {@link WifiNanSubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}.
- * <p>
- * For discovery session updates failure leaves the session running with its previous
- * configuration - the discovery session is not terminated.
- */
- public void onSessionConfigFailed() {
- /* empty */
- }
-
- /**
- * Called when a discovery session (publish or subscribe) terminates. Termination may be due
- * to user-request (either directly through {@link WifiNanDiscoveryBaseSession#destroy()} or
- * application-specified expiration, e.g. {@link PublishConfig.Builder#setPublishCount(int)}
- * or {@link SubscribeConfig.Builder#setTtlSec(int)}) or due to a failure.
- *
- * @param reason The termination reason using
- * {@code WifiNanDiscoverySessionCallback.TERMINATE_*} codes.
- */
- public void onSessionTerminated(@SessionTerminateCodes int reason) {
- /* empty */
- }
-
- /**
- * Called when a discovery (publish or subscribe) operation results in a
- * service discovery.
- *
- * @param peerHandle An opaque handle to the peer matching our discovery operation.
- * @param serviceSpecificInfo The service specific information (arbitrary
- * byte array) provided by the peer as part of its discovery
- * configuration.
- * @param matchFilter The filter (Tx on advertiser and Rx on listener) which
- * resulted in this service discovery.
- */
- public void onServiceDiscovered(Object peerHandle, byte[] serviceSpecificInfo,
- byte[] matchFilter) {
- /* empty */
- }
-
- /**
- * Called in response to {@link WifiNanDiscoveryBaseSession#sendMessage(Object, int, byte[])}
- * when a message is transmitted successfully - i.e. when it was received successfully by the
- * peer (corresponds to an ACK being received).
- * <p>
- * Note that either this callback or
- * {@link WifiNanDiscoverySessionCallback#onMessageSendFailed(int)} will be
- * received - never both.
- *
- * @param messageId The arbitrary message ID specified when sending the message.
- */
- public void onMessageSent(@SuppressWarnings("unused") int messageId) {
- /* empty */
- }
-
- /**
- * Called when message transmission fails - when no ACK is received from the peer.
- * Retries when ACKs are not received are done by hardware, MAC, and in the NAN stack (using
- * the {@link WifiNanDiscoveryBaseSession#sendMessage(Object, int, byte[], int)} method) - this
- * event is received after all retries are exhausted.
- * <p>
- * Note that either this callback or
- * {@link WifiNanDiscoverySessionCallback#onMessageSent(int)} will be received
- * - never both.
- *
- * @param messageId The arbitrary message ID specified when sending the message.
- */
- public void onMessageSendFailed(@SuppressWarnings("unused") int messageId) {
- /* empty */
- }
-
- /**
- * Called when a message is received from a discovery session peer - in response to the
- * peer's {@link WifiNanDiscoveryBaseSession#sendMessage(Object, int, byte[])} or
- * {@link WifiNanDiscoveryBaseSession#sendMessage(Object, int, byte[], int)}.
- *
- * @param peerHandle An opaque handle to the peer matching our discovery operation.
- * @param message A byte array containing the message.
- */
- public void onMessageReceived(Object peerHandle, byte[] message) {
- /* empty */
- }
-}
diff --git a/wifi/java/android/net/wifi/nan/package.html b/wifi/java/android/net/wifi/nan/package.html
deleted file mode 100644
index ae3cf6c..0000000
--- a/wifi/java/android/net/wifi/nan/package.html
+++ /dev/null
@@ -1,43 +0,0 @@
-<HTML>
-<BODY>
-<p>Provides classes which allow applications to use Wi-Fi NAN to discover peers and create
- connections to them.</p>
-<p>Using the Wi-Fi NAN APIs, applications can advertise services, discover peers which are
- advertising services, and connect to them.
- Wi-Fi NAN is independent of Wi-Fi infrastructure (i.e. a device may or may
- not be associated with an AP concurrent to using Wi-Fi NAN). </p>
-<p>The primary entry point to Wi-Fi NAN capabilities is the
- {@link android.net.wifi.nan.WifiNanManager} class, which is acquired by calling
- {@link android.content.Context#getSystemService(String)
- Context.getSystemService(Context.WIFI_NAN_SERVICE)}</p>
-
-<p>Some APIs may require the following user permissions:</p>
-<ul>
- <li>{@link android.Manifest.permission#ACCESS_WIFI_STATE}</li>
- <li>{@link android.Manifest.permission#CHANGE_WIFI_STATE}</li>
- <li>{@link android.Manifest.permission#ACCESS_COARSE_LOCATION}</li>
-</ul>
-
-<p class="note"><strong>Note:</strong> Not all Android-powered devices support Wi-Fi NAN
- functionality.
- If your application only works with Wi-Fi NAN (i.e. it should only be installed on devices which
- support Wi-Fi NAN), declare so with a <a
- href="{@docRoot}guide/topics/manifest/uses-feature-element.html">
- {@code <uses-feature>}</a>
- element in the manifest file:</p>
-<pre>
-<manifest ...>
- <uses-feature android:name="android.hardware.wifi.nan" />
- ...
-</manifest>
-</pre>
-<p>Alternatively, if you application does not require Wi-Fi NAN but can take advantage of it if
- available, you can perform
- the check at run-time in your code using {@link
- android.content.pm.PackageManager#hasSystemFeature(String)} with {@link
- android.content.pm.PackageManager#FEATURE_WIFI_NAN}:</p>
-<pre>
- getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_NAN)
-</pre>
-</BODY>
-</HTML>
diff --git a/wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl b/wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl
index ee2e895..8b1cfae 100644
--- a/wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl
+++ b/wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl
@@ -28,5 +28,6 @@
Messenger getMessenger();
Messenger getP2pStateMachineMessenger();
void setMiracastMode(int mode);
+ void checkConfigureWifiDisplayPermission();
}
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index 8d5cf6319..c93ac7b 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -283,6 +283,13 @@
public static final String EXTRA_HANDOVER_MESSAGE =
"android.net.wifi.p2p.EXTRA_HANDOVER_MESSAGE";
+ /**
+ * The lookup key for a calling package returned by the WifiP2pService.
+ * @hide
+ */
+ public static final String CALLING_PACKAGE =
+ "android.net.wifi.p2p.CALLING_PACKAGE";
+
IWifiP2pManager mService;
private static final int BASE = Protocol.BASE_WIFI_P2P_MANAGER;
@@ -1271,7 +1278,10 @@
*/
public void requestPeers(Channel c, PeerListListener listener) {
checkChannel(c);
- c.mAsyncChannel.sendMessage(REQUEST_PEERS, 0, c.putListener(listener));
+ Bundle callingPackage = new Bundle();
+ callingPackage.putString(CALLING_PACKAGE, c.mContext.getOpPackageName());
+ c.mAsyncChannel.sendMessage(REQUEST_PEERS, 0, c.putListener(listener),
+ callingPackage);
}
/**
@@ -1314,6 +1324,11 @@
Channel c, WifiP2pWfdInfo wfdInfo,
ActionListener listener) {
checkChannel(c);
+ try {
+ mService.checkConfigureWifiDisplayPermission();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
c.mAsyncChannel.sendMessage(SET_WFD_INFO, 0, c.putListener(listener), wfdInfo);
}
diff --git a/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.base64 b/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.base64
new file mode 100644
index 0000000..8c1eb08
--- /dev/null
+++ b/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.base64
@@ -0,0 +1,85 @@
+Q29udGVudC1UeXBlOiBtdWx0aXBhcnQvbWl4ZWQ7IGJvdW5kYXJ5PXtib3VuZGFyeX0KQ29udGVu
+dC1UcmFuc2Zlci1FbmNvZGluZzogYmFzZTY0CgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBh
+cHBsaWNhdGlvbi94LXBhc3Nwb2ludC1wcm9maWxlCkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6
+IGJhc2U2NAoKUEUxbmJYUlVjbVZsSUhodGJHNXpQU0p6ZVc1amJXdzZaRzFrWkdZeExqSWlQZ29n
+SUR4V1pYSkVWRVErTVM0eVBDOVdaWEpFVkVRKwpDaUFnUEU1dlpHVStDaUFnSUNBOFRtOWtaVTVo
+YldVK1VHVnlVSEp2ZG1sa1pYSlRkV0p6WTNKcGNIUnBiMjQ4TDA1dlpHVk9ZVzFsClBnb2dJQ0Fn
+UEZKVVVISnZjR1Z5ZEdsbGN6NEtJQ0FnSUNBZ1BGUjVjR1UrQ2lBZ0lDQWdJQ0FnUEVSRVJrNWhi
+V1UrZFhKdU9uZG0KWVRwdGJ6cG9iM1J6Y0c5ME1tUnZkREF0Y0dWeWNISnZkbWxrWlhKemRXSnpZ
+M0pwY0hScGIyNDZNUzR3UEM5RVJFWk9ZVzFsUGdvZwpJQ0FnSUNBOEwxUjVjR1UrQ2lBZ0lDQThM
+MUpVVUhKdmNHVnlkR2xsY3o0S0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBOFRtOWtaVTVoCmJXVSth
+VEF3TVR3dlRtOWtaVTVoYldVK0NpQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQWdJRHhPYjJSbFRt
+RnRaVDVJYjIxbFUxQTgKTDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lD
+QWdJQ0FnUEU1dlpHVk9ZVzFsUGtaeWFXVnVaR3g1VG1GdApaVHd2VG05a1pVNWhiV1UrQ2lBZ0lD
+QWdJQ0FnSUNBOFZtRnNkV1UrUTJWdWRIVnllU0JJYjNWelpUd3ZWbUZzZFdVK0NpQWdJQ0FnCklD
+QWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcx
+bFBrWlJSRTQ4TDA1dlpHVk8KWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBtMXBOaTVqYnk1
+MWF6d3ZWbUZzZFdVK0NpQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZwpJQ0FnSUNBZ0lEeE9iMlJsUGdv
+Z0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBsSnZZVzFwYm1kRGIyNXpiM0owYVhWdFQwazhMMDV2
+ClpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ1BGWmhiSFZsUGpFeE1qSXpNeXcwTkRVMU5qWThMMVpo
+YkhWbFBnb2dJQ0FnSUNBZ0lEd3YKVG05a1pUNEtJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0E4
+VG05a1pUNEtJQ0FnSUNBZ0lDQThUbTlrWlU1aGJXVStRM0psWkdWdQpkR2xoYkR3dlRtOWtaVTVo
+YldVK0NpQWdJQ0FnSUNBZ1BFNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVTVoYldVK1VtVmhi
+RzA4CkwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBuTm9ZV3RsYmk1emRHbHlj
+bVZrTG1OdmJUd3ZWbUZzZFdVK0NpQWcKSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9i
+MlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBsVnpaWEp1WVcxbApVR0Z6YzNkdmNtUThM
+MDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2Iy
+UmxUbUZ0ClpUNVZjMlZ5Ym1GdFpUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4V1lX
+eDFaVDVxWVcxbGN6d3ZWbUZzZFdVK0NpQWcKSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lD
+QWdJQ0E4VG05a1pUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGxCaApjM04zYjNKa1BD
+OU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQbGx0T1hWYVJFRjNUbmM5UFR3
+dlZtRnNkV1UrCkNpQWdJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlQ0
+S0lDQWdJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWwKUGtWQlVFMWxkR2h2WkR3dlRtOWtaVTVoYldV
+K0NpQWdJQ0FnSUNBZ0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBZ0lDQWdJQ0FnSUR4TwpiMlJsVG1G
+dFpUNUZRVkJVZVhCbFBDOU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnSUNBOFZtRnNkV1Ur
+TWpFOEwxWmhiSFZsClBnb2dJQ0FnSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lDQWdJQ0Fn
+SUR4T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnSUNBZ0lEeE8KYjJSbFRtRnRaVDVKYm01bGNrMWxkR2h2
+WkR3dlRtOWtaVTVoYldVK0NpQWdJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQazFUTFVOSQpRVkF0
+VmpJOEwxWmhiSFZsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThM
+MDV2WkdVK0NpQWdJQ0FnCklDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJ
+Q0FnSUNBZ1BFNXZaR1ZPWVcxbFBrUnBaMmwwWVd4RFpYSjAKYVdacFkyRjBaVHd2VG05a1pVNWhi
+V1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbApQ
+a05sY25ScFptbGpZWFJsVkhsd1pUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4V1lX
+eDFaVDU0TlRBNWRqTThMMVpoCmJIVmxQZ29nSUNBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lD
+QWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmwKVG1GdFpUNURaWEowVTBoQk1q
+VTJSbWx1WjJWeVVISnBiblE4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzZFdV
+KwpNV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpG
+bU1XWXhaakZtTVdZeFpqRm1NV1l4ClpqRm1NV1l4Wmp3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0FnSUNB
+OEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWcKSUR4T2IyUmxQZ29nSUNB
+Z0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGxOSlRUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0E4
+VG05awpaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBrbE5VMGs4TDA1dlpHVk9ZVzFs
+UGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzCmRXVSthVzF6YVR3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0Fn
+SUNBOEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWcKSUNBZ0lDQWdQRTV2
+WkdWT1lXMWxQa1ZCVUZSNWNHVThMMDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnSUNBOFZtRnNk
+V1UrTWpROApMMVpoYkhWbFBnb2dJQ0FnSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEd3ZU
+bTlrWlQ0S0lDQWdJQ0FnUEM5T2IyUmxQZ29nCklDQWdQQzlPYjJSbFBnb2dJRHd2VG05a1pUNEtQ
+QzlOWjIxMFZISmxaVDRLCgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi94
+LXg1MDktY2EtY2VydApDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBiYXNlNjQKCkxTMHRMUzFD
+UlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVVJMUkVORFFXaERaMEYzU1VKQlowbEtR
+VWxNYkVaa2QzcE0KVm5WeVRVRXdSME5UY1VkVFNXSXpSRkZGUWtOM1ZVRk5Ra2w0UlVSQlQwSm5U
+bFlLUWtGTlZFSXdWa0pWUTBKRVVWUkZkMGhvWTA1TgpWRmwzVFZSRmVVMVVSVEZOUkVVeFYyaGpU
+azFxV1hkTlZFRTFUVlJGTVUxRVJURlhha0ZUVFZKQmR3cEVaMWxFVmxGUlJFVjNaRVpSClZrRm5V
+VEJGZUUxSlNVSkpha0ZPUW1kcmNXaHJhVWM1ZHpCQ1FWRkZSa0ZCVDBOQlVUaEJUVWxKUWtOblMw
+TkJVVVZCQ25wdVFWQlYKZWpJMlRYTmhaVFIzY3pRelkzcFNOREV2U2pKUmRISlRTVnBWUzIxV1ZY
+TldkVzFFWWxsSWNsQk9kbFJZUzFOTldFRmpaWGRQVWtSUgpXVmdLVW5GMlNIWndiamhEYzJOQ01T
+dHZSMWhhZGtoM2VHbzBlbFl3VjB0dlN6SjZaVmhyWVhVemRtTjViRE5JU1V0MWNFcG1jVEpVClJV
+RkRaV1pXYW1vd2RBcEtWeXRZTXpWUVIxZHdPUzlJTlhwSlZVNVdUbFpxVXpkVmJYTTRORWwyUzJo
+U1FqZzFNVEpRUWpsVmVVaGgKWjFoWlZsZzFSMWR3UVdOV2NIbG1jbXhTQ2taSk9WRmthR2dyVUdK
+ck1IVjVhM1JrWW1ZdlEyUm1aMGhQYjJWaWNsUjBkMUpzYWswdwpiMFIwV0NzeVEzWTJhakIzUWtz
+M2FFUTRjRkIyWmpFcmRYa0tSM3BqZW1sblFWVXZORXQzTjJWYWNYbGtaamxDS3pWU2RYQlNLMGxh
+CmFYQllOREY0UldsSmNrdFNkM0ZwTlRFM1YxZDZXR05xWVVjeVkwNWlaalExTVFwNGNFZzFVRzVX
+TTJreGRIRXdOR3BOUjFGVmVrWjMKU1VSQlVVRkNielJIUVUxSU5IZElVVmxFVmxJd1QwSkNXVVZH
+U1hkWU5IWnpPRUpwUW1OVFkyOWtDalZ1YjFwSVVrMDRSVFFyYVUxRgpTVWRCTVZWa1NYZFJOMDFF
+YlVGR1NYZFlOSFp6T0VKcFFtTlRZMjlrTlc1dldraFNUVGhGTkN0cGIxSmhhMFpFUVZNS1RWSkJk
+MFJuCldVUldVVkZFUlhka1JsRldRV2RSTUVWNFoyZHJRV2QxVlZZelJFMTBWelp6ZDBSQldVUldV
+akJVUWtGVmQwRjNSVUl2ZWtGTVFtZE8KVmdwSVVUaEZRa0ZOUTBGUldYZEVVVmxLUzI5YVNXaDJZ
+MDVCVVVWTVFsRkJSR2RuUlVKQlJtWlJjVTlVUVRkU2RqZExLMngxVVRkdwpibUZ6TkVKWmQwaEZD
+amxIUlZBdmRXOW9kalpMVDNrd1ZFZFJSbUp5VWxScVJtOU1WazVDT1VKYU1YbHRUVVJhTUM5VVNY
+ZEpWV00zCmQyazNZVGgwTlcxRmNWbElNVFV6ZDFjS1lWZHZiMmxUYW5sTVRHaDFTVFJ6VG5KT1Ew
+OTBhWE5rUW5FeWNqSk5SbGgwTm1nd2JVRlIKV1U5UWRqaFNPRXMzTDJablUzaEhSbkY2YUhsT2JX
+MVdUQW94Y1VKS2JHUjRNelJUY0hkelZFRk1VVlpRWWpSb1IzZEtlbHBtY2pGUQpZM0JGVVhnMmVF
+MXVWR3c0ZUVWWFdrVXpUWE01T1hWaFZYaGlVWEZKZDFKMUNreG5RVTlyVGtOdFdUSnRPRGxXYUhw
+aFNFb3hkVlk0Ck5VRmtUUzkwUkN0WmMyMXNibTVxZERsTVVrTmxhbUpDYVhCcVNVZHFUMWh5WnpG
+S1VDdHNlRllLYlhWTk5IWklLMUF2Yld4dGVITlEKVUhvd1pEWTFZaXRGUjIxS1duQnZUR3RQTDNS
+a1RrNTJRMWw2YWtwd1ZFVlhjRVZ6VHpaT1RXaExXVzg5Q2kwdExTMHRSVTVFSUVORgpVbFJKUmts
+RFFWUkZMUzB0TFMwSwotLXtib3VuZGFyeX0tLQo=
diff --git a/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.conf b/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.conf
new file mode 100644
index 0000000..6d86dd5
--- /dev/null
+++ b/wifi/tests/assets/hsr1/HSR1ProfileWithCACert.conf
@@ -0,0 +1,73 @@
+Content-Type: multipart/mixed; boundary={boundary}
+Content-Transfer-Encoding: base64
+
+--{boundary}
+Content-Type: application/x-passpoint-profile
+Content-Transfer-Encoding: base64
+
+PE1nbXRUcmVlIHhtbG5zPSJzeW5jbWw6ZG1kZGYxLjIiPgogIDxWZXJEVEQ+MS4yPC9WZXJEVEQ+
+CiAgPE5vZGU+CiAgICA8Tm9kZU5hbWU+UGVyUHJvdmlkZXJTdWJzY3JpcHRpb248L05vZGVOYW1l
+PgogICAgPFJUUHJvcGVydGllcz4KICAgICAgPFR5cGU+CiAgICAgICAgPERERk5hbWU+dXJuOndm
+YTptbzpob3RzcG90MmRvdDAtcGVycHJvdmlkZXJzdWJzY3JpcHRpb246MS4wPC9EREZOYW1lPgog
+ICAgICA8L1R5cGU+CiAgICA8L1JUUHJvcGVydGllcz4KICAgIDxOb2RlPgogICAgICA8Tm9kZU5h
+bWU+aTAwMTwvTm9kZU5hbWU+CiAgICAgIDxOb2RlPgogICAgICAgIDxOb2RlTmFtZT5Ib21lU1A8
+L05vZGVOYW1lPgogICAgICAgIDxOb2RlPgogICAgICAgICAgPE5vZGVOYW1lPkZyaWVuZGx5TmFt
+ZTwvTm9kZU5hbWU+CiAgICAgICAgICA8VmFsdWU+Q2VudHVyeSBIb3VzZTwvVmFsdWU+CiAgICAg
+ICAgPC9Ob2RlPgogICAgICAgIDxOb2RlPgogICAgICAgICAgPE5vZGVOYW1lPkZRRE48L05vZGVO
+YW1lPgogICAgICAgICAgPFZhbHVlPm1pNi5jby51azwvVmFsdWU+CiAgICAgICAgPC9Ob2RlPgog
+ICAgICAgIDxOb2RlPgogICAgICAgICAgPE5vZGVOYW1lPlJvYW1pbmdDb25zb3J0aXVtT0k8L05v
+ZGVOYW1lPgogICAgICAgICAgPFZhbHVlPjExMjIzMyw0NDU1NjY8L1ZhbHVlPgogICAgICAgIDwv
+Tm9kZT4KICAgICAgPC9Ob2RlPgogICAgICA8Tm9kZT4KICAgICAgICA8Tm9kZU5hbWU+Q3JlZGVu
+dGlhbDwvTm9kZU5hbWU+CiAgICAgICAgPE5vZGU+CiAgICAgICAgICA8Tm9kZU5hbWU+UmVhbG08
+L05vZGVOYW1lPgogICAgICAgICAgPFZhbHVlPnNoYWtlbi5zdGlycmVkLmNvbTwvVmFsdWU+CiAg
+ICAgICAgPC9Ob2RlPgogICAgICAgIDxOb2RlPgogICAgICAgICAgPE5vZGVOYW1lPlVzZXJuYW1l
+UGFzc3dvcmQ8L05vZGVOYW1lPgogICAgICAgICAgPE5vZGU+CiAgICAgICAgICAgIDxOb2RlTmFt
+ZT5Vc2VybmFtZTwvTm9kZU5hbWU+CiAgICAgICAgICAgIDxWYWx1ZT5qYW1lczwvVmFsdWU+CiAg
+ICAgICAgICA8L05vZGU+CiAgICAgICAgICA8Tm9kZT4KICAgICAgICAgICAgPE5vZGVOYW1lPlBh
+c3N3b3JkPC9Ob2RlTmFtZT4KICAgICAgICAgICAgPFZhbHVlPlltOXVaREF3Tnc9PTwvVmFsdWU+
+CiAgICAgICAgICA8L05vZGU+CiAgICAgICAgICA8Tm9kZT4KICAgICAgICAgICAgPE5vZGVOYW1l
+PkVBUE1ldGhvZDwvTm9kZU5hbWU+CiAgICAgICAgICAgIDxOb2RlPgogICAgICAgICAgICAgIDxO
+b2RlTmFtZT5FQVBUeXBlPC9Ob2RlTmFtZT4KICAgICAgICAgICAgICA8VmFsdWU+MjE8L1ZhbHVl
+PgogICAgICAgICAgICA8L05vZGU+CiAgICAgICAgICAgIDxOb2RlPgogICAgICAgICAgICAgIDxO
+b2RlTmFtZT5Jbm5lck1ldGhvZDwvTm9kZU5hbWU+CiAgICAgICAgICAgICAgPFZhbHVlPk1TLUNI
+QVAtVjI8L1ZhbHVlPgogICAgICAgICAgICA8L05vZGU+CiAgICAgICAgICA8L05vZGU+CiAgICAg
+ICAgPC9Ob2RlPgogICAgICAgIDxOb2RlPgogICAgICAgICAgPE5vZGVOYW1lPkRpZ2l0YWxDZXJ0
+aWZpY2F0ZTwvTm9kZU5hbWU+CiAgICAgICAgICA8Tm9kZT4KICAgICAgICAgICAgPE5vZGVOYW1l
+PkNlcnRpZmljYXRlVHlwZTwvTm9kZU5hbWU+CiAgICAgICAgICAgIDxWYWx1ZT54NTA5djM8L1Zh
+bHVlPgogICAgICAgICAgPC9Ob2RlPgogICAgICAgICAgPE5vZGU+CiAgICAgICAgICAgIDxOb2Rl
+TmFtZT5DZXJ0U0hBMjU2RmluZ2VyUHJpbnQ8L05vZGVOYW1lPgogICAgICAgICAgICA8VmFsdWU+
+MWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYxZjFmMWYx
+ZjFmMWYxZjwvVmFsdWU+CiAgICAgICAgICA8L05vZGU+CiAgICAgICAgPC9Ob2RlPgogICAgICAg
+IDxOb2RlPgogICAgICAgICAgPE5vZGVOYW1lPlNJTTwvTm9kZU5hbWU+CiAgICAgICAgICA8Tm9k
+ZT4KICAgICAgICAgICAgPE5vZGVOYW1lPklNU0k8L05vZGVOYW1lPgogICAgICAgICAgICA8VmFs
+dWU+aW1zaTwvVmFsdWU+CiAgICAgICAgICA8L05vZGU+CiAgICAgICAgICA8Tm9kZT4KICAgICAg
+ICAgICAgPE5vZGVOYW1lPkVBUFR5cGU8L05vZGVOYW1lPgogICAgICAgICAgICA8VmFsdWU+MjQ8
+L1ZhbHVlPgogICAgICAgICAgPC9Ob2RlPgogICAgICAgIDwvTm9kZT4KICAgICAgPC9Ob2RlPgog
+ICAgPC9Ob2RlPgogIDwvTm9kZT4KPC9NZ210VHJlZT4K
+
+--{boundary}
+Content-Type: application/x-x509-ca-cert
+Content-Transfer-Encoding: base64
+
+LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURLRENDQWhDZ0F3SUJBZ0lKQUlMbEZkd3pM
+VnVyTUEwR0NTcUdTSWIzRFFFQkN3VUFNQkl4RURBT0JnTlYKQkFNVEIwVkJVQ0JEUVRFd0hoY05N
+VFl3TVRFeU1URTFNREUxV2hjTk1qWXdNVEE1TVRFMU1ERTFXakFTTVJBdwpEZ1lEVlFRREV3ZEZR
+VkFnUTBFeE1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBCnpuQVBV
+ejI2TXNhZTR3czQzY3pSNDEvSjJRdHJTSVpVS21WVXNWdW1EYllIclBOdlRYS1NNWEFjZXdPUkRR
+WVgKUnF2SHZwbjhDc2NCMStvR1hadkh3eGo0elYwV0tvSzJ6ZVhrYXUzdmN5bDNISUt1cEpmcTJU
+RUFDZWZWamowdApKVytYMzVQR1dwOS9INXpJVU5WTlZqUzdVbXM4NEl2S2hSQjg1MTJQQjlVeUhh
+Z1hZVlg1R1dwQWNWcHlmcmxSCkZJOVFkaGgrUGJrMHV5a3RkYmYvQ2RmZ0hPb2ViclR0d1Jsak0w
+b0R0WCsyQ3Y2ajB3Qks3aEQ4cFB2ZjErdXkKR3pjemlnQVUvNEt3N2VacXlkZjlCKzVSdXBSK0la
+aXBYNDF4RWlJcktSd3FpNTE3V1d6WGNqYUcyY05iZjQ1MQp4cEg1UG5WM2kxdHEwNGpNR1FVekZ3
+SURBUUFCbzRHQU1INHdIUVlEVlIwT0JCWUVGSXdYNHZzOEJpQmNTY29kCjVub1pIUk04RTQraU1F
+SUdBMVVkSXdRN01EbUFGSXdYNHZzOEJpQmNTY29kNW5vWkhSTThFNCtpb1Jha0ZEQVMKTVJBd0Rn
+WURWUVFERXdkRlFWQWdRMEV4Z2drQWd1VVYzRE10VzZzd0RBWURWUjBUQkFVd0F3RUIvekFMQmdO
+VgpIUThFQkFNQ0FRWXdEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBRmZRcU9UQTdSdjdLK2x1UTdw
+bmFzNEJZd0hFCjlHRVAvdW9odjZLT3kwVEdRRmJyUlRqRm9MVk5COUJaMXltTURaMC9USXdJVWM3
+d2k3YTh0NW1FcVlIMTUzd1cKYVdvb2lTanlMTGh1STRzTnJOQ090aXNkQnEycjJNRlh0NmgwbUFR
+WU9QdjhSOEs3L2ZnU3hHRnF6aHlObW1WTAoxcUJKbGR4MzRTcHdzVEFMUVZQYjRoR3dKelpmcjFQ
+Y3BFUXg2eE1uVGw4eEVXWkUzTXM5OXVhVXhiUXFJd1J1CkxnQU9rTkNtWTJtODlWaHphSEoxdVY4
+NUFkTS90RCtZc21sbm5qdDlMUkNlamJCaXBqSUdqT1hyZzFKUCtseFYKbXVNNHZIK1AvbWxteHNQ
+UHowZDY1YitFR21KWnBvTGtPL3RkTk52Q1l6akpwVEVXcEVzTzZOTWhLWW89Ci0tLS0tRU5EIENF
+UlRJRklDQVRFLS0tLS0K
+--{boundary}--
diff --git a/wifi/tests/assets/hsr1/HSR1ProfileWithInvalidContentType.base64 b/wifi/tests/assets/hsr1/HSR1ProfileWithInvalidContentType.base64
new file mode 100644
index 0000000..906bfb3
--- /dev/null
+++ b/wifi/tests/assets/hsr1/HSR1ProfileWithInvalidContentType.base64
@@ -0,0 +1,85 @@
+Q29udGVudC1UeXBlOiBtdWx0aXBhcnQvbWl4ZWQ7IGJvdW5kYXJ5PXtib3VuZGFyeX0KQ29udGVu
+dC1UcmFuc2Zlci1FbmNvZGluZzogYmFzZTY0CgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBh
+cHBsaWNhdGlvbi9wYXNzcG9pbnQtcHJvZmlsZQpDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBi
+YXNlNjQKClBFMW5iWFJVY21WbElIaHRiRzV6UFNKemVXNWpiV3c2Wkcxa1pHWXhMaklpUGdvZ0lE
+eFdaWEpFVkVRK01TNHlQQzlXWlhKRVZFUSsKQ2lBZ1BFNXZaR1UrQ2lBZ0lDQThUbTlrWlU1aGJX
+VStVR1Z5VUhKdmRtbGtaWEpUZFdKelkzSnBjSFJwYjI0OEwwNXZaR1ZPWVcxbApQZ29nSUNBZ1BG
+SlVVSEp2Y0dWeWRHbGxjejRLSUNBZ0lDQWdQRlI1Y0dVK0NpQWdJQ0FnSUNBZ1BFUkVSazVoYldV
+K2RYSnVPbmRtCllUcHRienBvYjNSemNHOTBNbVJ2ZERBdGNHVnljSEp2ZG1sa1pYSnpkV0p6WTNK
+cGNIUnBiMjQ2TVM0d1BDOUVSRVpPWVcxbFBnb2cKSUNBZ0lDQThMMVI1Y0dVK0NpQWdJQ0E4TDFK
+VVVISnZjR1Z5ZEdsbGN6NEtJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQThUbTlrWlU1aApiV1UrYVRB
+d01Ud3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0FnSUR4T2IyUmxUbUZ0
+WlQ1SWIyMWxVMUE4CkwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0Fn
+SUNBZ1BFNXZaR1ZPWVcxbFBrWnlhV1Z1Wkd4NVRtRnQKWlR3dlRtOWtaVTVoYldVK0NpQWdJQ0Fn
+SUNBZ0lDQThWbUZzZFdVK1EyVnVkSFZ5ZVNCSWIzVnpaVHd2Vm1Gc2RXVStDaUFnSUNBZwpJQ0Fn
+UEM5T2IyUmxQZ29nSUNBZ0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWxQ
+a1pSUkU0OEwwNXZaR1ZPCllXMWxQZ29nSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQbTFwTmk1amJ5NTFh
+end2Vm1Gc2RXVStDaUFnSUNBZ0lDQWdQQzlPYjJSbFBnb2cKSUNBZ0lDQWdJRHhPYjJSbFBnb2dJ
+Q0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWxQbEp2WVcxcGJtZERiMjV6YjNKMGFYVnRUMGs4TDA1dgpa
+R1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBqRXhNakl6TXl3ME5EVTFOalk4TDFaaGJI
+VmxQZ29nSUNBZ0lDQWdJRHd2ClRtOWtaVDRLSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBOFRt
+OWtaVDRLSUNBZ0lDQWdJQ0E4VG05a1pVNWhiV1UrUTNKbFpHVnUKZEdsaGJEd3ZUbTlrWlU1aGJX
+VStDaUFnSUNBZ0lDQWdQRTV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlU1aGJXVStVbVZoYkcw
+OApMMDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQbk5vWVd0bGJpNXpkR2x5Y21W
+a0xtTnZiVHd2Vm1Gc2RXVStDaUFnCklDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWdJRHhPYjJS
+bFBnb2dJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWxQbFZ6WlhKdVlXMWwKVUdGemMzZHZjbVE4TDA1
+dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lEeE9iMlJs
+VG1GdApaVDVWYzJWeWJtRnRaVHd2VG05a1pVNWhiV1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lEeFdZV3gx
+WlQ1cVlXMWxjend2Vm1Gc2RXVStDaUFnCklDQWdJQ0FnSUNBOEwwNXZaR1UrQ2lBZ0lDQWdJQ0Fn
+SUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBsQmgKYzNOM2IzSmtQQzlP
+YjJSbFRtRnRaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BGWmhiSFZsUGxsdE9YVmFSRUYzVG5jOVBUd3ZW
+bUZzZFdVKwpDaUFnSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lDQWdJQ0E4VG05a1pUNEtJ
+Q0FnSUNBZ0lDQWdJQ0FnUEU1dlpHVk9ZVzFsClBrVkJVRTFsZEdodlpEd3ZUbTlrWlU1aGJXVStD
+aUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnSUNBZ0lEeE8KYjJSbFRtRnRa
+VDVGUVZCVWVYQmxQQzlPYjJSbFRtRnRaVDRLSUNBZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzZFdVK01q
+RThMMVpoYkhWbApQZ29nSUNBZ0lDQWdJQ0FnSUNBOEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lE
+eE9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ0lDQWdJRHhPCmIyUmxUbUZ0WlQ1SmJtNWxjazFsZEdodlpE
+d3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUNBZ1BGWmhiSFZsUGsxVExVTkkKUVZBdFZq
+SThMMVpoYkhWbFBnb2dJQ0FnSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lDQWdJQ0E4TDA1
+dlpHVStDaUFnSUNBZwpJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNB
+Z0lDQWdQRTV2WkdWT1lXMWxQa1JwWjJsMFlXeERaWEowCmFXWnBZMkYwWlR3dlRtOWtaVTVoYldV
+K0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlQ0S0lDQWdJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWwKUGtO
+bGNuUnBabWxqWVhSbFZIbHdaVHd2VG05a1pVNWhiV1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lEeFdZV3gx
+WlQ1NE5UQTVkak04TDFaaApiSFZsUGdvZ0lDQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0Fn
+SUNBZ1BFNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBZ0lEeE9iMlJsClRtRnRaVDVEWlhKMFUwaEJNalUy
+Um1sdVoyVnlVSEpwYm5ROEwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWdJQ0E4Vm1Gc2RXVSsK
+TVdZeFpqRm1NV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1N
+V1l4WmpGbU1XWXhaakZtTVdZeApaakZtTVdZeFpqd3ZWbUZzZFdVK0NpQWdJQ0FnSUNBZ0lDQThM
+MDV2WkdVK0NpQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0FnCklEeE9iMlJsUGdvZ0lDQWdJ
+Q0FnSUNBZ1BFNXZaR1ZPWVcxbFBsTkpUVHd2VG05a1pVNWhiV1UrQ2lBZ0lDQWdJQ0FnSUNBOFRt
+OWsKWlQ0S0lDQWdJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWxQa2xOVTBrOEwwNXZaR1ZPWVcxbFBn
+b2dJQ0FnSUNBZ0lDQWdJQ0E4Vm1GcwpkV1UrYVcxemFUd3ZWbUZzZFdVK0NpQWdJQ0FnSUNBZ0lD
+QThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlQ0S0lDQWdJQ0FnCklDQWdJQ0FnUEU1dlpH
+Vk9ZVzFsUGtWQlVGUjVjR1U4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzZFdV
+K01qUTgKTDFaaGJIVmxQZ29nSUNBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWdJRHd2VG05
+a1pUNEtJQ0FnSUNBZ1BDOU9iMlJsUGdvZwpJQ0FnUEM5T2IyUmxQZ29nSUR3dlRtOWtaVDRLUEM5
+TloyMTBWSEpsWlQ0SwoKLS17Ym91bmRhcnl9CkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24veC14
+NTA5LWNhLWNlcnQKQ29udGVudC1UcmFuc2Zlci1FbmNvZGluZzogYmFzZTY0CgpMUzB0TFMxQ1JV
+ZEpUaUJEUlZKVVNVWkpRMEZVUlMwdExTMHRDazFKU1VSTFJFTkRRV2hEWjBGM1NVSkJaMGxLUVVs
+TWJFWmtkM3BNClZuVnlUVUV3UjBOVGNVZFRTV0l6UkZGRlFrTjNWVUZOUWtsNFJVUkJUMEpuVGxZ
+S1FrRk5WRUl3VmtKVlEwSkVVVlJGZDBob1kwNU4KVkZsM1RWUkZlVTFVUlRGTlJFVXhWMmhqVGsx
+cVdYZE5WRUUxVFZSRk1VMUVSVEZYYWtGVFRWSkJkd3BFWjFsRVZsRlJSRVYzWkVaUgpWa0ZuVVRC
+RmVFMUpTVUpKYWtGT1FtZHJjV2hyYVVjNWR6QkNRVkZGUmtGQlQwTkJVVGhCVFVsSlFrTm5TME5C
+VVVWQkNucHVRVkJWCmVqSTJUWE5oWlRSM2N6UXpZM3BTTkRFdlNqSlJkSEpUU1ZwVlMyMVdWWE5X
+ZFcxRVlsbEljbEJPZGxSWVMxTk5XRUZqWlhkUFVrUlIKV1ZnS1VuRjJTSFp3YmpoRGMyTkNNU3R2
+UjFoYWRraDNlR28wZWxZd1YwdHZTeko2WlZocllYVXpkbU41YkROSVNVdDFjRXBtY1RKVQpSVUZE
+WldaV2Ftb3dkQXBLVnl0WU16VlFSMWR3T1M5SU5YcEpWVTVXVGxacVV6ZFZiWE00TkVsMlMyaFNR
+amcxTVRKUVFqbFZlVWhoCloxaFpWbGcxUjFkd1FXTldjSGxtY214U0NrWkpPVkZrYUdnclVHSnJN
+SFY1YTNSa1ltWXZRMlJtWjBoUGIyVmljbFIwZDFKc2FrMHcKYjBSMFdDc3lRM1kyYWpCM1FrczNh
+RVE0Y0ZCMlpqRXJkWGtLUjNwamVtbG5RVlV2TkV0M04yVmFjWGxrWmpsQ0t6VlNkWEJTSzBsYQph
+WEJZTkRGNFJXbEpja3RTZDNGcE5URTNWMWQ2V0dOcVlVY3lZMDVpWmpRMU1RcDRjRWcxVUc1V00y
+a3hkSEV3TkdwTlIxRlZla1ozClNVUkJVVUZDYnpSSFFVMUlOSGRJVVZsRVZsSXdUMEpDV1VWR1NY
+ZFlOSFp6T0VKcFFtTlRZMjlrQ2pWdWIxcElVazA0UlRRcmFVMUYKU1VkQk1WVmtTWGRSTjAxRWJV
+RkdTWGRZTkhaek9FSnBRbU5UWTI5a05XNXZXa2hTVFRoRk5DdHBiMUpoYTBaRVFWTUtUVkpCZDBS
+bgpXVVJXVVZGRVJYZGtSbEZXUVdkUk1FVjRaMmRyUVdkMVZWWXpSRTEwVnpaemQwUkJXVVJXVWpC
+VVFrRlZkMEYzUlVJdmVrRk1RbWRPClZncElVVGhGUWtGTlEwRlJXWGRFVVZsS1MyOWFTV2gyWTA1
+QlVVVk1RbEZCUkdkblJVSkJSbVpSY1U5VVFUZFNkamRMSzJ4MVVUZHcKYm1Gek5FSlpkMGhGQ2ps
+SFJWQXZkVzlvZGpaTFQza3dWRWRSUm1KeVVsUnFSbTlNVms1Q09VSmFNWGx0VFVSYU1DOVVTWGRK
+VldNMwpkMmszWVRoME5XMUZjVmxJTVRVemQxY0tZVmR2YjJsVGFubE1UR2gxU1RSelRuSk9RMDkw
+YVhOa1FuRXljakpOUmxoME5tZ3diVUZSCldVOVFkamhTT0VzM0wyWm5VM2hIUm5GNmFIbE9iVzFX
+VEFveGNVSktiR1I0TXpSVGNIZHpWRUZNVVZaUVlqUm9SM2RLZWxwbWNqRlEKWTNCRlVYZzJlRTF1
+Vkd3NGVFVlhXa1V6VFhNNU9YVmhWWGhpVVhGSmQxSjFDa3huUVU5clRrTnRXVEp0T0RsV2FIcGhT
+RW94ZFZZNApOVUZrVFM5MFJDdFpjMjFzYm01cWREbE1Va05sYW1KQ2FYQnFTVWRxVDFoeVp6RktV
+Q3RzZUZZS2JYVk5OSFpJSzFBdmJXeHRlSE5RClVIb3daRFkxWWl0RlIyMUtXbkJ2VEd0UEwzUmtU
+azUyUTFsNmFrcHdWRVZYY0VWelR6Wk9UV2hMV1c4OUNpMHRMUzB0UlU1RUlFTkYKVWxSSlJrbERR
+VlJGTFMwdExTMEsKLS17Ym91bmRhcnl9LS0K
diff --git a/wifi/tests/assets/hsr1/HSR1ProfileWithMissingBoundary.base64 b/wifi/tests/assets/hsr1/HSR1ProfileWithMissingBoundary.base64
new file mode 100644
index 0000000..3fa97d1
--- /dev/null
+++ b/wifi/tests/assets/hsr1/HSR1ProfileWithMissingBoundary.base64
@@ -0,0 +1,85 @@
+Q29udGVudC1UeXBlOiBtdWx0aXBhcnQvbWl4ZWQ7IGJvdW5kYXJ5PXtib3VuZGFyeX0KQ29udGVu
+dC1UcmFuc2Zlci1FbmNvZGluZzogYmFzZTY0CgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBh
+cHBsaWNhdGlvbi94LXBhc3Nwb2ludC1wcm9maWxlCkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6
+IGJhc2U2NAoKUEUxbmJYUlVjbVZsSUhodGJHNXpQU0p6ZVc1amJXdzZaRzFrWkdZeExqSWlQZ29n
+SUR4V1pYSkVWRVErTVM0eVBDOVdaWEpFVkVRKwpDaUFnUEU1dlpHVStDaUFnSUNBOFRtOWtaVTVo
+YldVK1VHVnlVSEp2ZG1sa1pYSlRkV0p6WTNKcGNIUnBiMjQ4TDA1dlpHVk9ZVzFsClBnb2dJQ0Fn
+UEZKVVVISnZjR1Z5ZEdsbGN6NEtJQ0FnSUNBZ1BGUjVjR1UrQ2lBZ0lDQWdJQ0FnUEVSRVJrNWhi
+V1UrZFhKdU9uZG0KWVRwdGJ6cG9iM1J6Y0c5ME1tUnZkREF0Y0dWeWNISnZkbWxrWlhKemRXSnpZ
+M0pwY0hScGIyNDZNUzR3UEM5RVJFWk9ZVzFsUGdvZwpJQ0FnSUNBOEwxUjVjR1UrQ2lBZ0lDQThM
+MUpVVUhKdmNHVnlkR2xsY3o0S0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBOFRtOWtaVTVoCmJXVSth
+VEF3TVR3dlRtOWtaVTVoYldVK0NpQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQWdJRHhPYjJSbFRt
+RnRaVDVJYjIxbFUxQTgKTDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lD
+QWdJQ0FnUEU1dlpHVk9ZVzFsUGtaeWFXVnVaR3g1VG1GdApaVHd2VG05a1pVNWhiV1UrQ2lBZ0lD
+QWdJQ0FnSUNBOFZtRnNkV1UrUTJWdWRIVnllU0JJYjNWelpUd3ZWbUZzZFdVK0NpQWdJQ0FnCklD
+QWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcx
+bFBrWlJSRTQ4TDA1dlpHVk8KWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBtMXBOaTVqYnk1
+MWF6d3ZWbUZzZFdVK0NpQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZwpJQ0FnSUNBZ0lEeE9iMlJsUGdv
+Z0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBsSnZZVzFwYm1kRGIyNXpiM0owYVhWdFQwazhMMDV2
+ClpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ1BGWmhiSFZsUGpFeE1qSXpNeXcwTkRVMU5qWThMMVpo
+YkhWbFBnb2dJQ0FnSUNBZ0lEd3YKVG05a1pUNEtJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0E4
+VG05a1pUNEtJQ0FnSUNBZ0lDQThUbTlrWlU1aGJXVStRM0psWkdWdQpkR2xoYkR3dlRtOWtaVTVo
+YldVK0NpQWdJQ0FnSUNBZ1BFNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVTVoYldVK1VtVmhi
+RzA4CkwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBuTm9ZV3RsYmk1emRHbHlj
+bVZrTG1OdmJUd3ZWbUZzZFdVK0NpQWcKSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9i
+MlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBsVnpaWEp1WVcxbApVR0Z6YzNkdmNtUThM
+MDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2Iy
+UmxUbUZ0ClpUNVZjMlZ5Ym1GdFpUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4V1lX
+eDFaVDVxWVcxbGN6d3ZWbUZzZFdVK0NpQWcKSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lD
+QWdJQ0E4VG05a1pUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGxCaApjM04zYjNKa1BD
+OU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQbGx0T1hWYVJFRjNUbmM5UFR3
+dlZtRnNkV1UrCkNpQWdJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlQ0
+S0lDQWdJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWwKUGtWQlVFMWxkR2h2WkR3dlRtOWtaVTVoYldV
+K0NpQWdJQ0FnSUNBZ0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBZ0lDQWdJQ0FnSUR4TwpiMlJsVG1G
+dFpUNUZRVkJVZVhCbFBDOU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnSUNBOFZtRnNkV1Ur
+TWpFOEwxWmhiSFZsClBnb2dJQ0FnSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lDQWdJQ0Fn
+SUR4T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnSUNBZ0lEeE8KYjJSbFRtRnRaVDVKYm01bGNrMWxkR2h2
+WkR3dlRtOWtaVTVoYldVK0NpQWdJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQazFUTFVOSQpRVkF0
+VmpJOEwxWmhiSFZsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThM
+MDV2WkdVK0NpQWdJQ0FnCklDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJ
+Q0FnSUNBZ1BFNXZaR1ZPWVcxbFBrUnBaMmwwWVd4RFpYSjAKYVdacFkyRjBaVHd2VG05a1pVNWhi
+V1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbApQ
+a05sY25ScFptbGpZWFJsVkhsd1pUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4V1lX
+eDFaVDU0TlRBNWRqTThMMVpoCmJIVmxQZ29nSUNBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lD
+QWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmwKVG1GdFpUNURaWEowVTBoQk1q
+VTJSbWx1WjJWeVVISnBiblE4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzZFdV
+KwpNV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpG
+bU1XWXhaakZtTVdZeFpqRm1NV1l4ClpqRm1NV1l4Wmp3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0FnSUNB
+OEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWcKSUR4T2IyUmxQZ29nSUNB
+Z0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGxOSlRUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0E4
+VG05awpaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBrbE5VMGs4TDA1dlpHVk9ZVzFs
+UGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzCmRXVSthVzF6YVR3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0Fn
+SUNBOEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWcKSUNBZ0lDQWdQRTV2
+WkdWT1lXMWxQa1ZCVUZSNWNHVThMMDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnSUNBOFZtRnNk
+V1UrTWpROApMMVpoYkhWbFBnb2dJQ0FnSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEd3ZU
+bTlrWlQ0S0lDQWdJQ0FnUEM5T2IyUmxQZ29nCklDQWdQQzlPYjJSbFBnb2dJRHd2VG05a1pUNEtQ
+QzlOWjIxMFZISmxaVDRLCgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi94
+LXg1MDktY2EtY2VydApDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBiYXNlNjQKCkxTMHRMUzFD
+UlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVVJMUkVORFFXaERaMEYzU1VKQlowbEtR
+VWxNYkVaa2QzcE0KVm5WeVRVRXdSME5UY1VkVFNXSXpSRkZGUWtOM1ZVRk5Ra2w0UlVSQlQwSm5U
+bFlLUWtGTlZFSXdWa0pWUTBKRVVWUkZkMGhvWTA1TgpWRmwzVFZSRmVVMVVSVEZOUkVVeFYyaGpU
+azFxV1hkTlZFRTFUVlJGTVUxRVJURlhha0ZUVFZKQmR3cEVaMWxFVmxGUlJFVjNaRVpSClZrRm5V
+VEJGZUUxSlNVSkpha0ZPUW1kcmNXaHJhVWM1ZHpCQ1FWRkZSa0ZCVDBOQlVUaEJUVWxKUWtOblMw
+TkJVVVZCQ25wdVFWQlYKZWpJMlRYTmhaVFIzY3pRelkzcFNOREV2U2pKUmRISlRTVnBWUzIxV1ZY
+TldkVzFFWWxsSWNsQk9kbFJZUzFOTldFRmpaWGRQVWtSUgpXVmdLVW5GMlNIWndiamhEYzJOQ01T
+dHZSMWhhZGtoM2VHbzBlbFl3VjB0dlN6SjZaVmhyWVhVemRtTjViRE5JU1V0MWNFcG1jVEpVClJV
+RkRaV1pXYW1vd2RBcEtWeXRZTXpWUVIxZHdPUzlJTlhwSlZVNVdUbFpxVXpkVmJYTTRORWwyUzJo
+U1FqZzFNVEpRUWpsVmVVaGgKWjFoWlZsZzFSMWR3UVdOV2NIbG1jbXhTQ2taSk9WRmthR2dyVUdK
+ck1IVjVhM1JrWW1ZdlEyUm1aMGhQYjJWaWNsUjBkMUpzYWswdwpiMFIwV0NzeVEzWTJhakIzUWtz
+M2FFUTRjRkIyWmpFcmRYa0tSM3BqZW1sblFWVXZORXQzTjJWYWNYbGtaamxDS3pWU2RYQlNLMGxh
+CmFYQllOREY0UldsSmNrdFNkM0ZwTlRFM1YxZDZXR05xWVVjeVkwNWlaalExTVFwNGNFZzFVRzVX
+TTJreGRIRXdOR3BOUjFGVmVrWjMKU1VSQlVVRkNielJIUVUxSU5IZElVVmxFVmxJd1QwSkNXVVZH
+U1hkWU5IWnpPRUpwUW1OVFkyOWtDalZ1YjFwSVVrMDRSVFFyYVUxRgpTVWRCTVZWa1NYZFJOMDFF
+YlVGR1NYZFlOSFp6T0VKcFFtTlRZMjlrTlc1dldraFNUVGhGTkN0cGIxSmhhMFpFUVZNS1RWSkJk
+MFJuCldVUldVVkZFUlhka1JsRldRV2RSTUVWNFoyZHJRV2QxVlZZelJFMTBWelp6ZDBSQldVUldV
+akJVUWtGVmQwRjNSVUl2ZWtGTVFtZE8KVmdwSVVUaEZRa0ZOUTBGUldYZEVVVmxLUzI5YVNXaDJZ
+MDVCVVVWTVFsRkJSR2RuUlVKQlJtWlJjVTlVUVRkU2RqZExLMngxVVRkdwpibUZ6TkVKWmQwaEZD
+amxIUlZBdmRXOW9kalpMVDNrd1ZFZFJSbUp5VWxScVJtOU1WazVDT1VKYU1YbHRUVVJhTUM5VVNY
+ZEpWV00zCmQyazNZVGgwTlcxRmNWbElNVFV6ZDFjS1lWZHZiMmxUYW5sTVRHaDFTVFJ6VG5KT1Ew
+OTBhWE5rUW5FeWNqSk5SbGgwTm1nd2JVRlIKV1U5UWRqaFNPRXMzTDJablUzaEhSbkY2YUhsT2JX
+MVdUQW94Y1VKS2JHUjRNelJUY0hkelZFRk1VVlpRWWpSb1IzZEtlbHBtY2pGUQpZM0JGVVhnMmVF
+MXVWR3c0ZUVWWFdrVXpUWE01T1hWaFZYaGlVWEZKZDFKMUNreG5RVTlyVGtOdFdUSnRPRGxXYUhw
+aFNFb3hkVlk0Ck5VRmtUUzkwUkN0WmMyMXNibTVxZERsTVVrTmxhbUpDYVhCcVNVZHFUMWh5WnpG
+S1VDdHNlRllLYlhWTk5IWklLMUF2Yld4dGVITlEKVUhvd1pEWTFZaXRGUjIxS1duQnZUR3RQTDNS
+a1RrNTJRMWw2YWtwd1ZFVlhjRVZ6VHpaT1RXaExXVzg5Q2kwdExTMHRSVTVFSUVORgpVbFJKUmts
+RFFWUkZMUzB0TFMwSwo=
diff --git a/wifi/tests/assets/hsr1/HSR1ProfileWithNonBase64Part.base64 b/wifi/tests/assets/hsr1/HSR1ProfileWithNonBase64Part.base64
new file mode 100644
index 0000000..975f8e5
--- /dev/null
+++ b/wifi/tests/assets/hsr1/HSR1ProfileWithNonBase64Part.base64
@@ -0,0 +1,85 @@
+Q29udGVudC1UeXBlOiBtdWx0aXBhcnQvbWl4ZWQ7IGJvdW5kYXJ5PXtib3VuZGFyeX0KQ29udGVu
+dC1UcmFuc2Zlci1FbmNvZGluZzogYmFzZTMyCgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBh
+cHBsaWNhdGlvbi94LXBhc3Nwb2ludC1wcm9maWxlCkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6
+IGJhc2U2NAoKUEUxbmJYUlVjbVZsSUhodGJHNXpQU0p6ZVc1amJXdzZaRzFrWkdZeExqSWlQZ29n
+SUR4V1pYSkVWRVErTVM0eVBDOVdaWEpFVkVRKwpDaUFnUEU1dlpHVStDaUFnSUNBOFRtOWtaVTVo
+YldVK1VHVnlVSEp2ZG1sa1pYSlRkV0p6WTNKcGNIUnBiMjQ4TDA1dlpHVk9ZVzFsClBnb2dJQ0Fn
+UEZKVVVISnZjR1Z5ZEdsbGN6NEtJQ0FnSUNBZ1BGUjVjR1UrQ2lBZ0lDQWdJQ0FnUEVSRVJrNWhi
+V1UrZFhKdU9uZG0KWVRwdGJ6cG9iM1J6Y0c5ME1tUnZkREF0Y0dWeWNISnZkbWxrWlhKemRXSnpZ
+M0pwY0hScGIyNDZNUzR3UEM5RVJFWk9ZVzFsUGdvZwpJQ0FnSUNBOEwxUjVjR1UrQ2lBZ0lDQThM
+MUpVVUhKdmNHVnlkR2xsY3o0S0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBOFRtOWtaVTVoCmJXVSth
+VEF3TVR3dlRtOWtaVTVoYldVK0NpQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lDQWdJRHhPYjJSbFRt
+RnRaVDVJYjIxbFUxQTgKTDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUR4T2IyUmxQZ29nSUNBZ0lD
+QWdJQ0FnUEU1dlpHVk9ZVzFsUGtaeWFXVnVaR3g1VG1GdApaVHd2VG05a1pVNWhiV1UrQ2lBZ0lD
+QWdJQ0FnSUNBOFZtRnNkV1UrUTJWdWRIVnllU0JJYjNWelpUd3ZWbUZzZFdVK0NpQWdJQ0FnCklD
+QWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcx
+bFBrWlJSRTQ4TDA1dlpHVk8KWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBtMXBOaTVqYnk1
+MWF6d3ZWbUZzZFdVK0NpQWdJQ0FnSUNBZ1BDOU9iMlJsUGdvZwpJQ0FnSUNBZ0lEeE9iMlJsUGdv
+Z0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBsSnZZVzFwYm1kRGIyNXpiM0owYVhWdFQwazhMMDV2
+ClpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ1BGWmhiSFZsUGpFeE1qSXpNeXcwTkRVMU5qWThMMVpo
+YkhWbFBnb2dJQ0FnSUNBZ0lEd3YKVG05a1pUNEtJQ0FnSUNBZ1BDOU9iMlJsUGdvZ0lDQWdJQ0E4
+VG05a1pUNEtJQ0FnSUNBZ0lDQThUbTlrWlU1aGJXVStRM0psWkdWdQpkR2xoYkR3dlRtOWtaVTVo
+YldVK0NpQWdJQ0FnSUNBZ1BFNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVTVoYldVK1VtVmhi
+RzA4CkwwNXZaR1ZPWVcxbFBnb2dJQ0FnSUNBZ0lDQWdQRlpoYkhWbFBuTm9ZV3RsYmk1emRHbHlj
+bVZrTG1OdmJUd3ZWbUZzZFdVK0NpQWcKSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9i
+MlJsUGdvZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBsVnpaWEp1WVcxbApVR0Z6YzNkdmNtUThM
+MDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2Iy
+UmxUbUZ0ClpUNVZjMlZ5Ym1GdFpUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4V1lX
+eDFaVDVxWVcxbGN6d3ZWbUZzZFdVK0NpQWcKSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lD
+QWdJQ0E4VG05a1pUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGxCaApjM04zYjNKa1BD
+OU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQbGx0T1hWYVJFRjNUbmM5UFR3
+dlZtRnNkV1UrCkNpQWdJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThUbTlrWlQ0
+S0lDQWdJQ0FnSUNBZ0lDQWdQRTV2WkdWT1lXMWwKUGtWQlVFMWxkR2h2WkR3dlRtOWtaVTVoYldV
+K0NpQWdJQ0FnSUNBZ0lDQWdJRHhPYjJSbFBnb2dJQ0FnSUNBZ0lDQWdJQ0FnSUR4TwpiMlJsVG1G
+dFpUNUZRVkJVZVhCbFBDOU9iMlJsVG1GdFpUNEtJQ0FnSUNBZ0lDQWdJQ0FnSUNBOFZtRnNkV1Ur
+TWpFOEwxWmhiSFZsClBnb2dJQ0FnSUNBZ0lDQWdJQ0E4TDA1dlpHVStDaUFnSUNBZ0lDQWdJQ0Fn
+SUR4T2IyUmxQZ29nSUNBZ0lDQWdJQ0FnSUNBZ0lEeE8KYjJSbFRtRnRaVDVKYm01bGNrMWxkR2h2
+WkR3dlRtOWtaVTVoYldVK0NpQWdJQ0FnSUNBZ0lDQWdJQ0FnUEZaaGJIVmxQazFUTFVOSQpRVkF0
+VmpJOEwxWmhiSFZsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThMMDV2WkdVK0NpQWdJQ0FnSUNBZ0lDQThM
+MDV2WkdVK0NpQWdJQ0FnCklDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEeE9iMlJsUGdvZ0lDQWdJ
+Q0FnSUNBZ1BFNXZaR1ZPWVcxbFBrUnBaMmwwWVd4RFpYSjAKYVdacFkyRjBaVHd2VG05a1pVNWhi
+V1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbApQ
+a05sY25ScFptbGpZWFJsVkhsd1pUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0FnSUR4V1lX
+eDFaVDU0TlRBNWRqTThMMVpoCmJIVmxQZ29nSUNBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lD
+QWdJQ0FnUEU1dlpHVStDaUFnSUNBZ0lDQWdJQ0FnSUR4T2IyUmwKVG1GdFpUNURaWEowVTBoQk1q
+VTJSbWx1WjJWeVVISnBiblE4TDA1dlpHVk9ZVzFsUGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzZFdV
+KwpNV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpGbU1XWXhaakZtTVdZeFpqRm1NV1l4WmpG
+bU1XWXhaakZtTVdZeFpqRm1NV1l4ClpqRm1NV1l4Wmp3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0FnSUNB
+OEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnUEM5T2IyUmxQZ29nSUNBZ0lDQWcKSUR4T2IyUmxQZ29nSUNB
+Z0lDQWdJQ0FnUEU1dlpHVk9ZVzFsUGxOSlRUd3ZUbTlrWlU1aGJXVStDaUFnSUNBZ0lDQWdJQ0E4
+VG05awpaVDRLSUNBZ0lDQWdJQ0FnSUNBZ1BFNXZaR1ZPWVcxbFBrbE5VMGs4TDA1dlpHVk9ZVzFs
+UGdvZ0lDQWdJQ0FnSUNBZ0lDQThWbUZzCmRXVSthVzF6YVR3dlZtRnNkV1UrQ2lBZ0lDQWdJQ0Fn
+SUNBOEwwNXZaR1UrQ2lBZ0lDQWdJQ0FnSUNBOFRtOWtaVDRLSUNBZ0lDQWcKSUNBZ0lDQWdQRTV2
+WkdWT1lXMWxQa1ZCVUZSNWNHVThMMDV2WkdWT1lXMWxQZ29nSUNBZ0lDQWdJQ0FnSUNBOFZtRnNk
+V1UrTWpROApMMVpoYkhWbFBnb2dJQ0FnSUNBZ0lDQWdQQzlPYjJSbFBnb2dJQ0FnSUNBZ0lEd3ZU
+bTlrWlQ0S0lDQWdJQ0FnUEM5T2IyUmxQZ29nCklDQWdQQzlPYjJSbFBnb2dJRHd2VG05a1pUNEtQ
+QzlOWjIxMFZISmxaVDRLCgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi94
+LXg1MDktY2EtY2VydApDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBiYXNlNjQKCkxTMHRMUzFD
+UlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVVJMUkVORFFXaERaMEYzU1VKQlowbEtR
+VWxNYkVaa2QzcE0KVm5WeVRVRXdSME5UY1VkVFNXSXpSRkZGUWtOM1ZVRk5Ra2w0UlVSQlQwSm5U
+bFlLUWtGTlZFSXdWa0pWUTBKRVVWUkZkMGhvWTA1TgpWRmwzVFZSRmVVMVVSVEZOUkVVeFYyaGpU
+azFxV1hkTlZFRTFUVlJGTVUxRVJURlhha0ZUVFZKQmR3cEVaMWxFVmxGUlJFVjNaRVpSClZrRm5V
+VEJGZUUxSlNVSkpha0ZPUW1kcmNXaHJhVWM1ZHpCQ1FWRkZSa0ZCVDBOQlVUaEJUVWxKUWtOblMw
+TkJVVVZCQ25wdVFWQlYKZWpJMlRYTmhaVFIzY3pRelkzcFNOREV2U2pKUmRISlRTVnBWUzIxV1ZY
+TldkVzFFWWxsSWNsQk9kbFJZUzFOTldFRmpaWGRQVWtSUgpXVmdLVW5GMlNIWndiamhEYzJOQ01T
+dHZSMWhhZGtoM2VHbzBlbFl3VjB0dlN6SjZaVmhyWVhVemRtTjViRE5JU1V0MWNFcG1jVEpVClJV
+RkRaV1pXYW1vd2RBcEtWeXRZTXpWUVIxZHdPUzlJTlhwSlZVNVdUbFpxVXpkVmJYTTRORWwyUzJo
+U1FqZzFNVEpRUWpsVmVVaGgKWjFoWlZsZzFSMWR3UVdOV2NIbG1jbXhTQ2taSk9WRmthR2dyVUdK
+ck1IVjVhM1JrWW1ZdlEyUm1aMGhQYjJWaWNsUjBkMUpzYWswdwpiMFIwV0NzeVEzWTJhakIzUWtz
+M2FFUTRjRkIyWmpFcmRYa0tSM3BqZW1sblFWVXZORXQzTjJWYWNYbGtaamxDS3pWU2RYQlNLMGxh
+CmFYQllOREY0UldsSmNrdFNkM0ZwTlRFM1YxZDZXR05xWVVjeVkwNWlaalExTVFwNGNFZzFVRzVX
+TTJreGRIRXdOR3BOUjFGVmVrWjMKU1VSQlVVRkNielJIUVUxSU5IZElVVmxFVmxJd1QwSkNXVVZH
+U1hkWU5IWnpPRUpwUW1OVFkyOWtDalZ1YjFwSVVrMDRSVFFyYVUxRgpTVWRCTVZWa1NYZFJOMDFF
+YlVGR1NYZFlOSFp6T0VKcFFtTlRZMjlrTlc1dldraFNUVGhGTkN0cGIxSmhhMFpFUVZNS1RWSkJk
+MFJuCldVUldVVkZFUlhka1JsRldRV2RSTUVWNFoyZHJRV2QxVlZZelJFMTBWelp6ZDBSQldVUldV
+akJVUWtGVmQwRjNSVUl2ZWtGTVFtZE8KVmdwSVVUaEZRa0ZOUTBGUldYZEVVVmxLUzI5YVNXaDJZ
+MDVCVVVWTVFsRkJSR2RuUlVKQlJtWlJjVTlVUVRkU2RqZExLMngxVVRkdwpibUZ6TkVKWmQwaEZD
+amxIUlZBdmRXOW9kalpMVDNrd1ZFZFJSbUp5VWxScVJtOU1WazVDT1VKYU1YbHRUVVJhTUM5VVNY
+ZEpWV00zCmQyazNZVGgwTlcxRmNWbElNVFV6ZDFjS1lWZHZiMmxUYW5sTVRHaDFTVFJ6VG5KT1Ew
+OTBhWE5rUW5FeWNqSk5SbGgwTm1nd2JVRlIKV1U5UWRqaFNPRXMzTDJablUzaEhSbkY2YUhsT2JX
+MVdUQW94Y1VKS2JHUjRNelJUY0hkelZFRk1VVlpRWWpSb1IzZEtlbHBtY2pGUQpZM0JGVVhnMmVF
+MXVWR3c0ZUVWWFdrVXpUWE01T1hWaFZYaGlVWEZKZDFKMUNreG5RVTlyVGtOdFdUSnRPRGxXYUhw
+aFNFb3hkVlk0Ck5VRmtUUzkwUkN0WmMyMXNibTVxZERsTVVrTmxhbUpDYVhCcVNVZHFUMWh5WnpG
+S1VDdHNlRllLYlhWTk5IWklLMUF2Yld4dGVITlEKVUhvd1pEWTFZaXRGUjIxS1duQnZUR3RQTDNS
+a1RrNTJRMWw2YWtwd1ZFVlhjRVZ6VHpaT1RXaExXVzg5Q2kwdExTMHRSVTVFSUVORgpVbFJKUmts
+RFFWUkZMUzB0TFMwSwotLXtib3VuZGFyeX0tLQo=
diff --git a/wifi/tests/assets/hsr1/HSR1ProfileWithoutProfile.base64 b/wifi/tests/assets/hsr1/HSR1ProfileWithoutProfile.base64
new file mode 100644
index 0000000..833c527
--- /dev/null
+++ b/wifi/tests/assets/hsr1/HSR1ProfileWithoutProfile.base64
@@ -0,0 +1,31 @@
+Q29udGVudC1UeXBlOiBtdWx0aXBhcnQvbWl4ZWQ7IGJvdW5kYXJ5PXtib3VuZGFyeX0KQ29udGVu
+dC1UcmFuc2Zlci1FbmNvZGluZzogYmFzZTY0CgotLXtib3VuZGFyeX0KQ29udGVudC1UeXBlOiBh
+cHBsaWNhdGlvbi94LXg1MDktY2EtY2VydApDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBiYXNl
+NjQKCkxTMHRMUzFDUlVkSlRpQkRSVkpVU1VaSlEwRlVSUzB0TFMwdENrMUpTVVJMUkVORFFXaERa
+MEYzU1VKQlowbEtRVWxNYkVaa2QzcE0KVm5WeVRVRXdSME5UY1VkVFNXSXpSRkZGUWtOM1ZVRk5R
+a2w0UlVSQlQwSm5UbFlLUWtGTlZFSXdWa0pWUTBKRVVWUkZkMGhvWTA1TgpWRmwzVFZSRmVVMVVS
+VEZOUkVVeFYyaGpUazFxV1hkTlZFRTFUVlJGTVUxRVJURlhha0ZUVFZKQmR3cEVaMWxFVmxGUlJF
+VjNaRVpSClZrRm5VVEJGZUUxSlNVSkpha0ZPUW1kcmNXaHJhVWM1ZHpCQ1FWRkZSa0ZCVDBOQlVU
+aEJUVWxKUWtOblMwTkJVVVZCQ25wdVFWQlYKZWpJMlRYTmhaVFIzY3pRelkzcFNOREV2U2pKUmRI
+SlRTVnBWUzIxV1ZYTldkVzFFWWxsSWNsQk9kbFJZUzFOTldFRmpaWGRQVWtSUgpXVmdLVW5GMlNI
+WndiamhEYzJOQ01TdHZSMWhhZGtoM2VHbzBlbFl3VjB0dlN6SjZaVmhyWVhVemRtTjViRE5JU1V0
+MWNFcG1jVEpVClJVRkRaV1pXYW1vd2RBcEtWeXRZTXpWUVIxZHdPUzlJTlhwSlZVNVdUbFpxVXpk
+VmJYTTRORWwyUzJoU1FqZzFNVEpRUWpsVmVVaGgKWjFoWlZsZzFSMWR3UVdOV2NIbG1jbXhTQ2ta
+Sk9WRmthR2dyVUdKck1IVjVhM1JrWW1ZdlEyUm1aMGhQYjJWaWNsUjBkMUpzYWswdwpiMFIwV0Nz
+eVEzWTJhakIzUWtzM2FFUTRjRkIyWmpFcmRYa0tSM3BqZW1sblFWVXZORXQzTjJWYWNYbGtaamxD
+S3pWU2RYQlNLMGxhCmFYQllOREY0UldsSmNrdFNkM0ZwTlRFM1YxZDZXR05xWVVjeVkwNWlaalEx
+TVFwNGNFZzFVRzVXTTJreGRIRXdOR3BOUjFGVmVrWjMKU1VSQlVVRkNielJIUVUxSU5IZElVVmxF
+VmxJd1QwSkNXVVZHU1hkWU5IWnpPRUpwUW1OVFkyOWtDalZ1YjFwSVVrMDRSVFFyYVUxRgpTVWRC
+TVZWa1NYZFJOMDFFYlVGR1NYZFlOSFp6T0VKcFFtTlRZMjlrTlc1dldraFNUVGhGTkN0cGIxSmhh
+MFpFUVZNS1RWSkJkMFJuCldVUldVVkZFUlhka1JsRldRV2RSTUVWNFoyZHJRV2QxVlZZelJFMTBW
+elp6ZDBSQldVUldVakJVUWtGVmQwRjNSVUl2ZWtGTVFtZE8KVmdwSVVUaEZRa0ZOUTBGUldYZEVV
+VmxLUzI5YVNXaDJZMDVCVVVWTVFsRkJSR2RuUlVKQlJtWlJjVTlVUVRkU2RqZExLMngxVVRkdwpi
+bUZ6TkVKWmQwaEZDamxIUlZBdmRXOW9kalpMVDNrd1ZFZFJSbUp5VWxScVJtOU1WazVDT1VKYU1Y
+bHRUVVJhTUM5VVNYZEpWV00zCmQyazNZVGgwTlcxRmNWbElNVFV6ZDFjS1lWZHZiMmxUYW5sTVRH
+aDFTVFJ6VG5KT1EwOTBhWE5rUW5FeWNqSk5SbGgwTm1nd2JVRlIKV1U5UWRqaFNPRXMzTDJablUz
+aEhSbkY2YUhsT2JXMVdUQW94Y1VKS2JHUjRNelJUY0hkelZFRk1VVlpRWWpSb1IzZEtlbHBtY2pG
+UQpZM0JGVVhnMmVFMXVWR3c0ZUVWWFdrVXpUWE01T1hWaFZYaGlVWEZKZDFKMUNreG5RVTlyVGtO
+dFdUSnRPRGxXYUhwaFNFb3hkVlk0Ck5VRmtUUzkwUkN0WmMyMXNibTVxZERsTVVrTmxhbUpDYVhC
+cVNVZHFUMWh5WnpGS1VDdHNlRllLYlhWTk5IWklLMUF2Yld4dGVITlEKVUhvd1pEWTFZaXRGUjIx
+S1duQnZUR3RQTDNSa1RrNTJRMWw2YWtwd1ZFVlhjRVZ6VHpaT1RXaExXVzg5Q2kwdExTMHRSVTVF
+SUVORgpVbFJKUmtsRFFWUkZMUzB0TFMwSwotLXtib3VuZGFyeX0tLQo=
diff --git a/wifi/tests/assets/hsr1/README.txt b/wifi/tests/assets/hsr1/README.txt
new file mode 100644
index 0000000..d1f8384
--- /dev/null
+++ b/wifi/tests/assets/hsr1/README.txt
@@ -0,0 +1,5 @@
+HSR1ProfileWithCACert.conf - unencoded installation file that contains a Passpoint profile and a CA Certificate
+HSR1ProfileWithCACert.base64 - base64 encoded of the data contained in HSR1ProfileWithCAWith.conf
+HSR1ProfileWithNonBase64Part.base64 - base64 encoded installation file that contains a part of non-base64 encoding type
+HSR1ProfileWithMissingBoundary.base64 - base64 encoded installation file with missing end-boundary in the MIME data
+HSR1ProfileWithInvalidContentType.base64 - base64 encoded installation file with that contains a MIME part with an invalid content type.
diff --git a/wifi/tests/src/android/net/wifi/aware/TlvBufferUtilsTest.java b/wifi/tests/src/android/net/wifi/aware/TlvBufferUtilsTest.java
new file mode 100644
index 0000000..15641ab
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/aware/TlvBufferUtilsTest.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.aware;
+
+import static org.hamcrest.core.IsEqual.equalTo;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ErrorCollector;
+
+import java.nio.BufferOverflowException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Unit test harness for TlvBufferUtils class.
+ */
+@SmallTest
+public class TlvBufferUtilsTest {
+ @Rule
+ public ErrorCollector collector = new ErrorCollector();
+
+ /*
+ * TlvBufferUtils Tests
+ */
+
+ @Test
+ public void testTlvBuild() {
+ TlvBufferUtils.TlvConstructor tlv11 = new TlvBufferUtils.TlvConstructor(1, 1);
+ tlv11.allocate(15);
+ tlv11.putByte(0, (byte) 2);
+ tlv11.putByteArray(2, new byte[] {
+ 0, 1, 2 });
+
+ collector.checkThat("tlv11-correct-construction",
+ tlv11.getArray(), equalTo(new byte[]{0, 1, 2, 2, 3, 0, 1, 2}));
+
+ TlvBufferUtils.TlvConstructor tlv01 = new TlvBufferUtils.TlvConstructor(0, 1);
+ tlv01.allocate(15);
+ tlv01.putByte(0, (byte) 2);
+ tlv01.putByteArray(2, new byte[] {
+ 0, 1, 2 });
+
+ collector.checkThat("tlv01-correct-construction",
+ tlv01.getArray(), equalTo(new byte[] {1, 2, 3, 0, 1, 2 }));
+
+ collector.checkThat("tlv11-valid",
+ TlvBufferUtils.isValid(tlv11.getArray(), 1, 1),
+ equalTo(true));
+ collector.checkThat("tlv01-valid",
+ TlvBufferUtils.isValid(tlv01.getArray(), 0, 1),
+ equalTo(true));
+ }
+
+ /**
+ * Verify that can build a valid TLV from a List of byte[].
+ */
+ @Test
+ public void testTlvListOperations() {
+ byte[] entry1 = { 1, 2, 3 };
+ byte[] entry2 = { 4, 5 };
+ byte[] entry3 = new byte[0];
+ List<byte[]> data = new ArrayList<>();
+ data.add(entry1);
+ data.add(entry2);
+ data.add(entry3);
+ data.add(null); // zero-length should work
+
+ TlvBufferUtils.TlvConstructor tlv01 = new TlvBufferUtils.TlvConstructor(0, 1);
+ tlv01.allocateAndPut(data);
+ byte[] tlvData = tlv01.getArray();
+ List<byte[]> parsedList = new TlvBufferUtils.TlvIterable(0, 1, tlvData).toList();
+
+ collector.checkThat("tlvData-correct-length", tlvData.length,
+ equalTo(entry1.length + 1 + entry2.length + 1 + entry3.length + 1 + 1));
+ collector.checkThat("parsedList-correct-length", parsedList.size(), equalTo(4));
+ collector.checkThat("parsedList-entry1", parsedList.get(0), equalTo(entry1));
+ collector.checkThat("parsedList-entry2", parsedList.get(1), equalTo(entry2));
+ collector.checkThat("parsedList-entry3", parsedList.get(2), equalTo(entry3));
+ collector.checkThat("parsedList-entry4", parsedList.get(3), equalTo(new byte[0]));
+ }
+
+ /**
+ * Verify that can parse a (correctly formatted) byte array to a list.
+ */
+ @Test
+ public void testTlvParseToList() {
+ byte[] validTlv01 = { 0, 1, 55, 2, 33, 66, 0 };
+
+ List<byte[]> parsedList = new TlvBufferUtils.TlvIterable(0, 1, validTlv01).toList();
+
+ collector.checkThat("parsedList-entry1", parsedList.get(0), equalTo(new byte[0]));
+ collector.checkThat("parsedList-entry2", parsedList.get(1), equalTo(new byte[] { 55 }));
+ collector.checkThat("parsedList-entry3", parsedList.get(2), equalTo(new byte[] { 33, 66 }));
+ collector.checkThat("parsedList-entry4", parsedList.get(3), equalTo(new byte[0]));
+ }
+
+ /**
+ * Verify that an exception is thrown when trying to parse an invalid array.
+ */
+ @Test(expected = BufferOverflowException.class)
+ public void testTlvParseToListError() {
+ byte[] invalidTlv01 = { 0, 1, 55, 2, 55, 66, 3 }; // bad data
+
+ List<byte[]> data = new TlvBufferUtils.TlvIterable(0, 1, invalidTlv01).toList();
+ }
+
+ @Test
+ public void testTlvIterate() {
+ final String ascii = "ABC";
+ final String nonAscii = "何かもっと複雑な";
+
+ TlvBufferUtils.TlvConstructor tlv22 = new TlvBufferUtils.TlvConstructor(2, 2);
+ tlv22.allocate(18);
+ tlv22.putInt(0, 2);
+ tlv22.putShort(2, (short) 3);
+ tlv22.putZeroLengthElement(55);
+
+ TlvBufferUtils.TlvIterable tlv22It = new TlvBufferUtils.TlvIterable(2, 2, tlv22.getArray());
+ int count = 0;
+ for (TlvBufferUtils.TlvElement tlv : tlv22It) {
+ if (count == 0) {
+ collector.checkThat("tlv22-correct-iteration-mType", tlv.type, equalTo(0));
+ collector.checkThat("tlv22-correct-iteration-mLength", tlv.length, equalTo(4));
+ collector.checkThat("tlv22-correct-iteration-DATA", tlv.getInt(), equalTo(2));
+ } else if (count == 1) {
+ collector.checkThat("tlv22-correct-iteration-mType", tlv.type, equalTo(2));
+ collector.checkThat("tlv22-correct-iteration-mLength", tlv.length, equalTo(2));
+ collector.checkThat("tlv22-correct-iteration-DATA", (int) tlv.getShort(),
+ equalTo(3));
+ } else if (count == 2) {
+ collector.checkThat("tlv22-correct-iteration-mType", tlv.type, equalTo(55));
+ collector.checkThat("tlv22-correct-iteration-mLength", tlv.length, equalTo(0));
+ } else {
+ collector.checkThat("Invalid number of iterations in loop - tlv22", true,
+ equalTo(false));
+ }
+ ++count;
+ }
+ if (count != 3) {
+ collector.checkThat("Invalid number of iterations outside loop - tlv22", true,
+ equalTo(false));
+ }
+
+ TlvBufferUtils.TlvConstructor tlv02 = new TlvBufferUtils.TlvConstructor(0, 2);
+ tlv02.allocate(100);
+ tlv02.putByte(0, (byte) 2);
+ tlv02.putString(0, ascii);
+ tlv02.putString(0, nonAscii);
+
+ TlvBufferUtils.TlvIterable tlv02It = new TlvBufferUtils.TlvIterable(0, 2, tlv02.getArray());
+ count = 0;
+ for (TlvBufferUtils.TlvElement tlv : tlv02It) {
+ if (count == 0) {
+ collector.checkThat("tlv02-correct-iteration-mLength", tlv.length, equalTo(1));
+ collector.checkThat("tlv02-correct-iteration-DATA", (int) tlv.getByte(),
+ equalTo(2));
+ } else if (count == 1) {
+ collector.checkThat("tlv02-correct-iteration-mLength", tlv.length,
+ equalTo(ascii.length()));
+ collector.checkThat("tlv02-correct-iteration-DATA", tlv.getString().equals(ascii),
+ equalTo(true));
+ } else if (count == 2) {
+ collector.checkThat("tlv02-correct-iteration-mLength", tlv.length,
+ equalTo(nonAscii.getBytes().length));
+ collector.checkThat("tlv02-correct-iteration-DATA",
+ tlv.getString().equals(nonAscii), equalTo(true));
+ } else {
+ collector.checkThat("Invalid number of iterations in loop - tlv02", true,
+ equalTo(false));
+ }
+ ++count;
+ }
+ collector.checkThat("Invalid number of iterations outside loop - tlv02", count,
+ equalTo(3));
+
+ collector.checkThat("tlv22-valid",
+ TlvBufferUtils.isValid(tlv22.getArray(), 2, 2),
+ equalTo(true));
+ collector.checkThat("tlv02-valid",
+ TlvBufferUtils.isValid(tlv02.getArray(), 0, 2),
+ equalTo(true));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testTlvInvalidSizeT1L0() {
+ TlvBufferUtils.TlvConstructor tlv10 = new TlvBufferUtils.TlvConstructor(1, 0);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testTlvInvalidSizeTm3L2() {
+ TlvBufferUtils.TlvConstructor tlv10 = new TlvBufferUtils.TlvConstructor(-3, 2);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testTlvInvalidSizeT1Lm2() {
+ TlvBufferUtils.TlvConstructor tlv10 = new TlvBufferUtils.TlvConstructor(1, -2);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testTlvInvalidSizeT1L3() {
+ TlvBufferUtils.TlvConstructor tlv10 = new TlvBufferUtils.TlvConstructor(1, 3);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testTlvInvalidSizeT3L1() {
+ TlvBufferUtils.TlvConstructor tlv10 = new TlvBufferUtils.TlvConstructor(3, 1);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testTlvItInvalidSizeT1L0() {
+ final byte[] dummy = {
+ 0, 1, 2 };
+ final int dummyLength = 3;
+ TlvBufferUtils.TlvIterable tlvIt10 = new TlvBufferUtils.TlvIterable(1, 0, dummy);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testTlvItInvalidSizeTm3L2() {
+ final byte[] dummy = {
+ 0, 1, 2 };
+ final int dummyLength = 3;
+ TlvBufferUtils.TlvIterable tlvIt10 = new TlvBufferUtils.TlvIterable(-3, 2, dummy);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testTlvItInvalidSizeT1Lm2() {
+ final byte[] dummy = {
+ 0, 1, 2 };
+ final int dummyLength = 3;
+ TlvBufferUtils.TlvIterable tlvIt10 = new TlvBufferUtils.TlvIterable(1, -2, dummy);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testTlvItInvalidSizeT1L3() {
+ final byte[] dummy = {
+ 0, 1, 2 };
+ final int dummyLength = 3;
+ TlvBufferUtils.TlvIterable tlvIt10 = new TlvBufferUtils.TlvIterable(1, 3, dummy);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testTlvItInvalidSizeT3L1() {
+ final byte[] dummy = {
+ 0, 1, 2 };
+ final int dummyLength = 3;
+ TlvBufferUtils.TlvIterable tlvIt10 = new TlvBufferUtils.TlvIterable(3, 1, dummy);
+ }
+
+ /**
+ * Validate that a malformed byte array fails the TLV validity test.
+ */
+ @Test
+ public void testTlvInvalidByteArray() {
+ TlvBufferUtils.TlvConstructor tlv01 = new TlvBufferUtils.TlvConstructor(0, 1);
+ tlv01.allocate(15);
+ tlv01.putByte(0, (byte) 2);
+ tlv01.putByteArray(2, new byte[]{0, 1, 2});
+
+ byte[] array = tlv01.getArray();
+ array[0] = 10;
+
+ collector.checkThat("tlv01-invalid",
+ TlvBufferUtils.isValid(array, 0, 1), equalTo(false));
+ }
+}
diff --git a/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
new file mode 100644
index 0000000..24c0127
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/aware/WifiAwareManagerTest.java
@@ -0,0 +1,1063 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi.aware;
+
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Matchers.isNull;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.net.wifi.RttManager;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.test.TestLooper;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Base64;
+
+import libcore.util.HexEncoding;
+
+import org.json.JSONObject;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ErrorCollector;
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+/**
+ * Unit test harness for WifiAwareManager class.
+ */
+@SmallTest
+public class WifiAwareManagerTest {
+ private WifiAwareManager mDut;
+ private TestLooper mMockLooper;
+ private Handler mMockLooperHandler;
+
+ @Rule
+ public ErrorCollector collector = new ErrorCollector();
+
+ @Mock
+ public Context mockContext;
+
+ @Mock
+ public WifiAwareAttachCallback mockCallback;
+
+ @Mock
+ public WifiAwareDiscoverySessionCallback mockSessionCallback;
+
+ @Mock
+ public IWifiAwareManager mockAwareService;
+
+ @Mock
+ public WifiAwarePublishDiscoverySession mockPublishSession;
+
+ @Mock
+ public WifiAwareSubscribeDiscoverySession mockSubscribeSession;
+
+ @Mock
+ public RttManager.RttListener mockRttListener;
+
+ private static final int AWARE_STATUS_ERROR = -1;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+
+ mDut = new WifiAwareManager(mockContext, mockAwareService);
+ mMockLooper = new TestLooper();
+ mMockLooperHandler = new Handler(mMockLooper.getLooper());
+ }
+
+ /*
+ * Straight pass-through tests
+ */
+
+ /**
+ * Validate pass-through of enableUsage() API.
+ */
+ @Test
+ public void testEnableUsage() throws Exception {
+ mDut.enableUsage();
+
+ verify(mockAwareService).enableUsage();
+ }
+
+ /**
+ * Validate pass-through of disableUsage() API.
+ */
+ @Test
+ public void testDisableUsage() throws Exception {
+ mDut.disableUsage();
+
+ verify(mockAwareService).disableUsage();
+ }
+
+ /**
+ * Validate pass-through of isUsageEnabled() API.
+ */
+ @Test
+ public void testIsUsageEnable() throws Exception {
+ mDut.isAvailable();
+
+ verify(mockAwareService).isUsageEnabled();
+ }
+
+ /**
+ * Validate pass-through of getCharacteristics() API.
+ */
+ @Test
+ public void testGetCharacteristics() throws Exception {
+ mDut.getCharacteristics();
+
+ verify(mockAwareService).getCharacteristics();
+ }
+
+ /*
+ * WifiAwareEventCallbackProxy Tests
+ */
+
+ /**
+ * Validate the successful connect flow: (1) connect + success (2) publish, (3) disconnect
+ * (4) try publishing on old session (5) connect again
+ */
+ @Test
+ public void testConnectFlow() throws Exception {
+ final int clientId = 4565;
+
+ InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService);
+ ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor
+ .forClass(IWifiAwareEventCallback.class);
+ ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
+ WifiAwareSession.class);
+ ArgumentCaptor<IBinder> binder = ArgumentCaptor.forClass(IBinder.class);
+
+ // (1) connect + success
+ mDut.attach(mockCallback, mMockLooperHandler);
+ inOrder.verify(mockAwareService).connect(binder.capture(), anyString(),
+ clientProxyCallback.capture(), (ConfigRequest) isNull(), eq(false));
+ clientProxyCallback.getValue().onConnectSuccess(clientId);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onAttached(sessionCaptor.capture());
+ WifiAwareSession session = sessionCaptor.getValue();
+
+ // (2) publish - should succeed
+ PublishConfig publishConfig = new PublishConfig.Builder().build();
+ session.publish(publishConfig, mockSessionCallback, mMockLooperHandler);
+ inOrder.verify(mockAwareService).publish(eq(clientId), eq(publishConfig),
+ any(IWifiAwareDiscoverySessionCallback.class));
+
+ // (3) disconnect
+ session.destroy();
+ inOrder.verify(mockAwareService).disconnect(eq(clientId), eq(binder.getValue()));
+
+ // (4) try publishing again - fails silently
+ session.publish(new PublishConfig.Builder().build(), mockSessionCallback,
+ mMockLooperHandler);
+
+ // (5) connect
+ mDut.attach(mockCallback, mMockLooperHandler);
+ inOrder.verify(mockAwareService).connect(binder.capture(), anyString(),
+ any(IWifiAwareEventCallback.class), (ConfigRequest) isNull(), eq(false));
+
+ verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService);
+ }
+
+ /**
+ * Validate the failed connect flow: (1) connect + failure, (2) connect + success (3) subscribe
+ */
+ @Test
+ public void testConnectFailure() throws Exception {
+ final int clientId = 4565;
+ final int reason = AWARE_STATUS_ERROR;
+
+ InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService);
+ ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
+ WifiAwareSession.class);
+ ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor
+ .forClass(IWifiAwareEventCallback.class);
+
+ // (1) connect + failure
+ mDut.attach(mockCallback, mMockLooperHandler);
+ inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
+ clientProxyCallback.capture(), (ConfigRequest) isNull(), eq(false));
+ clientProxyCallback.getValue().onConnectFail(reason);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onAttachFailed();
+
+ // (2) connect + success
+ mDut.attach(mockCallback, mMockLooperHandler);
+ inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
+ clientProxyCallback.capture(), (ConfigRequest) isNull(), eq(false));
+ clientProxyCallback.getValue().onConnectSuccess(clientId);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onAttached(sessionCaptor.capture());
+ WifiAwareSession session = sessionCaptor.getValue();
+
+ // (4) subscribe: should succeed
+ SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
+ session.subscribe(subscribeConfig, mockSessionCallback, mMockLooperHandler);
+ inOrder.verify(mockAwareService).subscribe(eq(clientId), eq(subscribeConfig),
+ any(IWifiAwareDiscoverySessionCallback.class));
+
+ verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService);
+ }
+
+ /**
+ * Validate that can call connect to create multiple sessions: (1) connect
+ * + success, (2) try connect again
+ */
+ @Test
+ public void testInvalidConnectSequence() throws Exception {
+ final int clientId = 4565;
+
+ InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService);
+ ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor
+ .forClass(IWifiAwareEventCallback.class);
+
+ // (1) connect + success
+ mDut.attach(mockCallback, mMockLooperHandler);
+ inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
+ clientProxyCallback.capture(), (ConfigRequest) isNull(), eq(false));
+ clientProxyCallback.getValue().onConnectSuccess(clientId);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onAttached(any(WifiAwareSession.class));
+
+ // (2) connect + success
+ mDut.attach(mockCallback, mMockLooperHandler);
+ inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
+ clientProxyCallback.capture(), (ConfigRequest) isNull(), eq(false));
+ clientProxyCallback.getValue().onConnectSuccess(clientId + 1);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onAttached(any(WifiAwareSession.class));
+
+ verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService);
+ }
+
+ /*
+ * WifiAwareDiscoverySessionCallbackProxy Tests
+ */
+
+ /**
+ * Validate the publish flow: (0) connect + success, (1) publish, (2)
+ * success creates session, (3) pass through everything, (4) update publish
+ * through session, (5) terminate locally, (6) try another command -
+ * ignored.
+ */
+ @Test
+ public void testPublishFlow() throws Exception {
+ final int clientId = 4565;
+ final int sessionId = 123;
+ final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ final PublishConfig publishConfig = new PublishConfig.Builder().build();
+ final WifiAwareManager.PeerHandle peerHandle = new WifiAwareManager.PeerHandle(873);
+ final String string1 = "hey from here...";
+ final byte[] matchFilter = { 1, 12, 2, 31, 32 };
+ final int messageId = 2123;
+ final int reason = AWARE_STATUS_ERROR;
+
+ InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService,
+ mockPublishSession);
+ ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
+ WifiAwareSession.class);
+ ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor
+ .forClass(IWifiAwareEventCallback.class);
+ ArgumentCaptor<IWifiAwareDiscoverySessionCallback> sessionProxyCallback = ArgumentCaptor
+ .forClass(IWifiAwareDiscoverySessionCallback.class);
+ ArgumentCaptor<WifiAwarePublishDiscoverySession> publishSession = ArgumentCaptor
+ .forClass(WifiAwarePublishDiscoverySession.class);
+ ArgumentCaptor<WifiAwareManager.PeerHandle> peerIdCaptor = ArgumentCaptor.forClass(
+ WifiAwareManager.PeerHandle.class);
+ ArgumentCaptor<List<byte[]>> matchFilterCaptor = ArgumentCaptor.forClass(
+ (Class) List.class);
+
+ // (0) connect + success
+ mDut.attach(mMockLooperHandler, configRequest, mockCallback, null);
+ inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
+ clientProxyCallback.capture(), eq(configRequest), eq(false));
+ clientProxyCallback.getValue().onConnectSuccess(clientId);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onAttached(sessionCaptor.capture());
+ WifiAwareSession session = sessionCaptor.getValue();
+
+ // (1) publish
+ session.publish(publishConfig, mockSessionCallback, mMockLooperHandler);
+ inOrder.verify(mockAwareService).publish(eq(clientId), eq(publishConfig),
+ sessionProxyCallback.capture());
+
+ // (2) publish session created
+ sessionProxyCallback.getValue().onSessionStarted(sessionId);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onPublishStarted(publishSession.capture());
+
+ // (3) ...
+ publishSession.getValue().sendMessage(peerHandle, messageId, string1.getBytes());
+ sessionProxyCallback.getValue().onMatch(peerHandle.peerId, string1.getBytes(), matchFilter);
+ sessionProxyCallback.getValue().onMessageReceived(peerHandle.peerId, string1.getBytes());
+ sessionProxyCallback.getValue().onMessageSendFail(messageId, reason);
+ sessionProxyCallback.getValue().onMessageSendSuccess(messageId);
+ mMockLooper.dispatchAll();
+
+ inOrder.verify(mockAwareService).sendMessage(eq(clientId), eq(sessionId),
+ eq(peerHandle.peerId), eq(string1.getBytes()), eq(messageId), eq(0));
+ inOrder.verify(mockSessionCallback).onServiceDiscovered(peerIdCaptor.capture(),
+ eq(string1.getBytes()),
+ matchFilterCaptor.capture());
+
+ // note: need to capture/compare elements since the Mockito eq() is a shallow comparator
+ List<byte[]> parsedMatchFilter = new TlvBufferUtils.TlvIterable(0, 1, matchFilter).toList();
+ collector.checkThat("match-filter-size", parsedMatchFilter.size(),
+ equalTo(matchFilterCaptor.getValue().size()));
+ collector.checkThat("match-filter-entry0", parsedMatchFilter.get(0),
+ equalTo(matchFilterCaptor.getValue().get(0)));
+ collector.checkThat("match-filter-entry1", parsedMatchFilter.get(1),
+ equalTo(matchFilterCaptor.getValue().get(1)));
+
+ assertEquals(peerIdCaptor.getValue().peerId, peerHandle.peerId);
+ inOrder.verify(mockSessionCallback).onMessageReceived(peerIdCaptor.capture(),
+ eq(string1.getBytes()));
+ assertEquals(peerIdCaptor.getValue().peerId, peerHandle.peerId);
+ inOrder.verify(mockSessionCallback).onMessageSendFailed(eq(messageId));
+ inOrder.verify(mockSessionCallback).onMessageSendSucceeded(eq(messageId));
+
+ // (4) update publish
+ publishSession.getValue().updatePublish(publishConfig);
+ sessionProxyCallback.getValue().onSessionConfigFail(reason);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockAwareService).updatePublish(eq(clientId), eq(sessionId),
+ eq(publishConfig));
+ inOrder.verify(mockSessionCallback).onSessionConfigFailed();
+
+ // (5) terminate
+ publishSession.getValue().destroy();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockAwareService).terminateSession(clientId, sessionId);
+
+ // (6) try an update (nothing)
+ publishSession.getValue().updatePublish(publishConfig);
+ mMockLooper.dispatchAll();
+
+ verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService,
+ mockPublishSession);
+ }
+
+ /**
+ * Validate race condition of session terminate and session action: (1)
+ * connect, (2) publish success + terminate, (3) update.
+ */
+ @Test
+ public void testPublishRemoteTerminate() throws Exception {
+ final int clientId = 4565;
+ final int sessionId = 123;
+ final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ final PublishConfig publishConfig = new PublishConfig.Builder().build();
+ final int reason = WifiAwareDiscoverySessionCallback.TERMINATE_REASON_DONE;
+
+ InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService,
+ mockPublishSession);
+ ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
+ WifiAwareSession.class);
+ ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor
+ .forClass(IWifiAwareEventCallback.class);
+ ArgumentCaptor<IWifiAwareDiscoverySessionCallback> sessionProxyCallback = ArgumentCaptor
+ .forClass(IWifiAwareDiscoverySessionCallback.class);
+ ArgumentCaptor<WifiAwarePublishDiscoverySession> publishSession = ArgumentCaptor
+ .forClass(WifiAwarePublishDiscoverySession.class);
+
+ // (1) connect successfully
+ mDut.attach(mMockLooperHandler, configRequest, mockCallback, null);
+ inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
+ clientProxyCallback.capture(), eq(configRequest), eq(false));
+ clientProxyCallback.getValue().onConnectSuccess(clientId);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onAttached(sessionCaptor.capture());
+ WifiAwareSession session = sessionCaptor.getValue();
+
+ // (2) publish: successfully - then terminated
+ session.publish(publishConfig, mockSessionCallback, mMockLooperHandler);
+ inOrder.verify(mockAwareService).publish(eq(clientId), eq(publishConfig),
+ sessionProxyCallback.capture());
+ sessionProxyCallback.getValue().onSessionStarted(sessionId);
+ sessionProxyCallback.getValue().onSessionTerminated(reason);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onPublishStarted(publishSession.capture());
+ inOrder.verify(mockSessionCallback).onSessionTerminated(reason);
+
+ // (3) failure when trying to update: NOP
+ publishSession.getValue().updatePublish(publishConfig);
+
+ verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService,
+ mockPublishSession);
+ }
+
+ /**
+ * Validate the subscribe flow: (0) connect + success, (1) subscribe, (2)
+ * success creates session, (3) pass through everything, (4) update
+ * subscribe through session, (5) terminate locally, (6) try another command
+ * - ignored.
+ */
+ @Test
+ public void testSubscribeFlow() throws Exception {
+ final int clientId = 4565;
+ final int sessionId = 123;
+ final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ final SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
+ final WifiAwareManager.PeerHandle peerHandle = new WifiAwareManager.PeerHandle(873);
+ final String string1 = "hey from here...";
+ final byte[] matchFilter = { 1, 12, 3, 31, 32 }; // bad data!
+ final int messageId = 2123;
+ final int reason = AWARE_STATUS_ERROR;
+
+ InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService,
+ mockSubscribeSession);
+ ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
+ WifiAwareSession.class);
+ ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor
+ .forClass(IWifiAwareEventCallback.class);
+ ArgumentCaptor<IWifiAwareDiscoverySessionCallback> sessionProxyCallback = ArgumentCaptor
+ .forClass(IWifiAwareDiscoverySessionCallback.class);
+ ArgumentCaptor<WifiAwareSubscribeDiscoverySession> subscribeSession = ArgumentCaptor
+ .forClass(WifiAwareSubscribeDiscoverySession.class);
+ ArgumentCaptor<WifiAwareManager.PeerHandle> peerIdCaptor = ArgumentCaptor.forClass(
+ WifiAwareManager.PeerHandle.class);
+
+ // (0) connect + success
+ mDut.attach(mMockLooperHandler, configRequest, mockCallback, null);
+ inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
+ clientProxyCallback.capture(), eq(configRequest), eq(false));
+ clientProxyCallback.getValue().onConnectSuccess(clientId);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onAttached(sessionCaptor.capture());
+ WifiAwareSession session = sessionCaptor.getValue();
+
+ // (1) subscribe
+ session.subscribe(subscribeConfig, mockSessionCallback, mMockLooperHandler);
+ inOrder.verify(mockAwareService).subscribe(eq(clientId), eq(subscribeConfig),
+ sessionProxyCallback.capture());
+
+ // (2) subscribe session created
+ sessionProxyCallback.getValue().onSessionStarted(sessionId);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onSubscribeStarted(subscribeSession.capture());
+
+ // (3) ...
+ subscribeSession.getValue().sendMessage(peerHandle, messageId, string1.getBytes());
+ sessionProxyCallback.getValue().onMatch(peerHandle.peerId, string1.getBytes(), matchFilter);
+ sessionProxyCallback.getValue().onMessageReceived(peerHandle.peerId, string1.getBytes());
+ sessionProxyCallback.getValue().onMessageSendFail(messageId, reason);
+ sessionProxyCallback.getValue().onMessageSendSuccess(messageId);
+ mMockLooper.dispatchAll();
+
+ inOrder.verify(mockAwareService).sendMessage(eq(clientId), eq(sessionId),
+ eq(peerHandle.peerId), eq(string1.getBytes()), eq(messageId), eq(0));
+ inOrder.verify(mockSessionCallback).onServiceDiscovered(peerIdCaptor.capture(),
+ eq(string1.getBytes()), (List<byte[]>) isNull());
+ assertEquals((peerIdCaptor.getValue()).peerId, peerHandle.peerId);
+ inOrder.verify(mockSessionCallback).onMessageReceived(peerIdCaptor.capture(),
+ eq(string1.getBytes()));
+ assertEquals((peerIdCaptor.getValue()).peerId, peerHandle.peerId);
+ inOrder.verify(mockSessionCallback).onMessageSendFailed(eq(messageId));
+ inOrder.verify(mockSessionCallback).onMessageSendSucceeded(eq(messageId));
+
+ // (4) update subscribe
+ subscribeSession.getValue().updateSubscribe(subscribeConfig);
+ sessionProxyCallback.getValue().onSessionConfigFail(reason);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockAwareService).updateSubscribe(eq(clientId), eq(sessionId),
+ eq(subscribeConfig));
+ inOrder.verify(mockSessionCallback).onSessionConfigFailed();
+
+ // (5) terminate
+ subscribeSession.getValue().destroy();
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockAwareService).terminateSession(clientId, sessionId);
+
+ // (6) try an update (nothing)
+ subscribeSession.getValue().updateSubscribe(subscribeConfig);
+ mMockLooper.dispatchAll();
+
+ verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService,
+ mockSubscribeSession);
+ }
+
+ /**
+ * Validate race condition of session terminate and session action: (1)
+ * connect, (2) subscribe success + terminate, (3) update.
+ */
+ @Test
+ public void testSubscribeRemoteTerminate() throws Exception {
+ final int clientId = 4565;
+ final int sessionId = 123;
+ final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ final SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
+ final int reason = WifiAwareDiscoverySessionCallback.TERMINATE_REASON_DONE;
+
+ InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService,
+ mockSubscribeSession);
+ ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
+ WifiAwareSession.class);
+ ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor
+ .forClass(IWifiAwareEventCallback.class);
+ ArgumentCaptor<IWifiAwareDiscoverySessionCallback> sessionProxyCallback = ArgumentCaptor
+ .forClass(IWifiAwareDiscoverySessionCallback.class);
+ ArgumentCaptor<WifiAwareSubscribeDiscoverySession> subscribeSession = ArgumentCaptor
+ .forClass(WifiAwareSubscribeDiscoverySession.class);
+
+ // (1) connect successfully
+ mDut.attach(mMockLooperHandler, configRequest, mockCallback, null);
+ inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
+ clientProxyCallback.capture(), eq(configRequest), eq(false));
+ clientProxyCallback.getValue().onConnectSuccess(clientId);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onAttached(sessionCaptor.capture());
+ WifiAwareSession session = sessionCaptor.getValue();
+
+ // (2) subscribe: successfully - then terminated
+ session.subscribe(subscribeConfig, mockSessionCallback, mMockLooperHandler);
+ inOrder.verify(mockAwareService).subscribe(eq(clientId), eq(subscribeConfig),
+ sessionProxyCallback.capture());
+ sessionProxyCallback.getValue().onSessionStarted(sessionId);
+ sessionProxyCallback.getValue().onSessionTerminated(reason);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onSubscribeStarted(subscribeSession.capture());
+ inOrder.verify(mockSessionCallback).onSessionTerminated(reason);
+
+ // (3) failure when trying to update: NOP
+ subscribeSession.getValue().updateSubscribe(subscribeConfig);
+
+ verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService,
+ mockSubscribeSession);
+ }
+
+ /*
+ * ConfigRequest Tests
+ */
+
+ @Test
+ public void testConfigRequestBuilderDefaults() {
+ ConfigRequest configRequest = new ConfigRequest.Builder().build();
+
+ collector.checkThat("mClusterHigh", ConfigRequest.CLUSTER_ID_MAX,
+ equalTo(configRequest.mClusterHigh));
+ collector.checkThat("mClusterLow", ConfigRequest.CLUSTER_ID_MIN,
+ equalTo(configRequest.mClusterLow));
+ collector.checkThat("mMasterPreference", 0,
+ equalTo(configRequest.mMasterPreference));
+ collector.checkThat("mSupport5gBand", false, equalTo(configRequest.mSupport5gBand));
+ }
+
+ @Test
+ public void testConfigRequestBuilder() {
+ final int clusterHigh = 100;
+ final int clusterLow = 5;
+ final int masterPreference = 55;
+ final boolean supportBand5g = true;
+
+ ConfigRequest configRequest = new ConfigRequest.Builder().setClusterHigh(clusterHigh)
+ .setClusterLow(clusterLow).setMasterPreference(masterPreference)
+ .setSupport5gBand(supportBand5g)
+ .build();
+
+ collector.checkThat("mClusterHigh", clusterHigh, equalTo(configRequest.mClusterHigh));
+ collector.checkThat("mClusterLow", clusterLow, equalTo(configRequest.mClusterLow));
+ collector.checkThat("mMasterPreference", masterPreference,
+ equalTo(configRequest.mMasterPreference));
+ collector.checkThat("mSupport5gBand", supportBand5g, equalTo(configRequest.mSupport5gBand));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testConfigRequestBuilderMasterPrefNegative() {
+ ConfigRequest.Builder builder = new ConfigRequest.Builder();
+ builder.setMasterPreference(-1);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testConfigRequestBuilderMasterPrefReserved1() {
+ new ConfigRequest.Builder().setMasterPreference(1);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testConfigRequestBuilderMasterPrefReserved255() {
+ new ConfigRequest.Builder().setMasterPreference(255);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testConfigRequestBuilderMasterPrefTooLarge() {
+ new ConfigRequest.Builder().setMasterPreference(256);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testConfigRequestBuilderClusterLowNegative() {
+ new ConfigRequest.Builder().setClusterLow(-1);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testConfigRequestBuilderClusterHighNegative() {
+ new ConfigRequest.Builder().setClusterHigh(-1);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testConfigRequestBuilderClusterLowAboveMax() {
+ new ConfigRequest.Builder().setClusterLow(ConfigRequest.CLUSTER_ID_MAX + 1);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testConfigRequestBuilderClusterHighAboveMax() {
+ new ConfigRequest.Builder().setClusterHigh(ConfigRequest.CLUSTER_ID_MAX + 1);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testConfigRequestBuilderClusterLowLargerThanHigh() {
+ new ConfigRequest.Builder().setClusterLow(100).setClusterHigh(5).build();
+ }
+
+ @Test
+ public void testConfigRequestParcel() {
+ final int clusterHigh = 189;
+ final int clusterLow = 25;
+ final int masterPreference = 177;
+ final boolean supportBand5g = true;
+
+ ConfigRequest configRequest = new ConfigRequest.Builder().setClusterHigh(clusterHigh)
+ .setClusterLow(clusterLow).setMasterPreference(masterPreference)
+ .setSupport5gBand(supportBand5g)
+ .build();
+
+ Parcel parcelW = Parcel.obtain();
+ configRequest.writeToParcel(parcelW, 0);
+ byte[] bytes = parcelW.marshall();
+ parcelW.recycle();
+
+ Parcel parcelR = Parcel.obtain();
+ parcelR.unmarshall(bytes, 0, bytes.length);
+ parcelR.setDataPosition(0);
+ ConfigRequest rereadConfigRequest = ConfigRequest.CREATOR.createFromParcel(parcelR);
+
+ assertEquals(configRequest, rereadConfigRequest);
+ }
+
+ /*
+ * SubscribeConfig Tests
+ */
+
+ @Test
+ public void testSubscribeConfigBuilderDefaults() {
+ SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().build();
+
+ collector.checkThat("mServiceName", subscribeConfig.mServiceName, equalTo(null));
+ collector.checkThat("mSubscribeType", subscribeConfig.mSubscribeType,
+ equalTo(SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE));
+ collector.checkThat("mSubscribeCount", subscribeConfig.mSubscribeCount, equalTo(0));
+ collector.checkThat("mTtlSec", subscribeConfig.mTtlSec, equalTo(0));
+ collector.checkThat("mMatchStyle", subscribeConfig.mMatchStyle,
+ equalTo(SubscribeConfig.MATCH_STYLE_ALL));
+ collector.checkThat("mEnableTerminateNotification",
+ subscribeConfig.mEnableTerminateNotification, equalTo(true));
+ }
+
+ @Test
+ public void testSubscribeConfigBuilder() {
+ final String serviceName = "some_service_or_other";
+ final String serviceSpecificInfo = "long arbitrary string with some info";
+ final byte[] matchFilter = { 1, 16, 1, 22 };
+ final int subscribeType = SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE;
+ final int subscribeCount = 10;
+ final int subscribeTtl = 15;
+ final int matchStyle = SubscribeConfig.MATCH_STYLE_FIRST_ONLY;
+ final boolean enableTerminateNotification = false;
+
+ SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(serviceName)
+ .setServiceSpecificInfo(serviceSpecificInfo.getBytes()).setMatchFilter(
+ new TlvBufferUtils.TlvIterable(0, 1, matchFilter).toList())
+ .setSubscribeType(subscribeType)
+ .setSubscribeCount(subscribeCount).setTtlSec(subscribeTtl).setMatchStyle(matchStyle)
+ .setTerminateNotificationEnabled(enableTerminateNotification).build();
+
+ collector.checkThat("mServiceName", serviceName.getBytes(),
+ equalTo(subscribeConfig.mServiceName));
+ collector.checkThat("mServiceSpecificInfo",
+ serviceSpecificInfo.getBytes(), equalTo(subscribeConfig.mServiceSpecificInfo));
+ collector.checkThat("mMatchFilter", matchFilter, equalTo(subscribeConfig.mMatchFilter));
+ collector.checkThat("mSubscribeType", subscribeType,
+ equalTo(subscribeConfig.mSubscribeType));
+ collector.checkThat("mSubscribeCount", subscribeCount,
+ equalTo(subscribeConfig.mSubscribeCount));
+ collector.checkThat("mTtlSec", subscribeTtl, equalTo(subscribeConfig.mTtlSec));
+ collector.checkThat("mMatchStyle", matchStyle, equalTo(subscribeConfig.mMatchStyle));
+ collector.checkThat("mEnableTerminateNotification", enableTerminateNotification,
+ equalTo(subscribeConfig.mEnableTerminateNotification));
+ }
+
+ @Test
+ public void testSubscribeConfigParcel() {
+ final String serviceName = "some_service_or_other";
+ final String serviceSpecificInfo = "long arbitrary string with some info";
+ final byte[] matchFilter = { 1, 16, 1, 22 };
+ final int subscribeType = SubscribeConfig.SUBSCRIBE_TYPE_PASSIVE;
+ final int subscribeCount = 10;
+ final int subscribeTtl = 15;
+ final int matchStyle = SubscribeConfig.MATCH_STYLE_FIRST_ONLY;
+ final boolean enableTerminateNotification = true;
+
+ SubscribeConfig subscribeConfig = new SubscribeConfig.Builder().setServiceName(serviceName)
+ .setServiceSpecificInfo(serviceSpecificInfo.getBytes()).setMatchFilter(
+ new TlvBufferUtils.TlvIterable(0, 1, matchFilter).toList())
+ .setSubscribeType(subscribeType)
+ .setSubscribeCount(subscribeCount).setTtlSec(subscribeTtl).setMatchStyle(matchStyle)
+ .setTerminateNotificationEnabled(enableTerminateNotification).build();
+
+ Parcel parcelW = Parcel.obtain();
+ subscribeConfig.writeToParcel(parcelW, 0);
+ byte[] bytes = parcelW.marshall();
+ parcelW.recycle();
+
+ Parcel parcelR = Parcel.obtain();
+ parcelR.unmarshall(bytes, 0, bytes.length);
+ parcelR.setDataPosition(0);
+ SubscribeConfig rereadSubscribeConfig = SubscribeConfig.CREATOR.createFromParcel(parcelR);
+
+ assertEquals(subscribeConfig, rereadSubscribeConfig);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSubscribeConfigBuilderBadSubscribeType() {
+ new SubscribeConfig.Builder().setSubscribeType(10);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSubscribeConfigBuilderNegativeCount() {
+ new SubscribeConfig.Builder().setSubscribeCount(-1);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testSubscribeConfigBuilderNegativeTtl() {
+ new SubscribeConfig.Builder().setTtlSec(-100);
+ }
+
+ /**
+ * Validate that a bad match style configuration throws an exception.
+ */
+ @Test(expected = IllegalArgumentException.class)
+ public void testSubscribeConfigBuilderBadMatchStyle() {
+ new SubscribeConfig.Builder().setMatchStyle(10);
+ }
+
+ /*
+ * PublishConfig Tests
+ */
+
+ @Test
+ public void testPublishConfigBuilderDefaults() {
+ PublishConfig publishConfig = new PublishConfig.Builder().build();
+
+ collector.checkThat("mServiceName", publishConfig.mServiceName, equalTo(null));
+ collector.checkThat("mPublishType", publishConfig.mPublishType,
+ equalTo(PublishConfig.PUBLISH_TYPE_UNSOLICITED));
+ collector.checkThat("mPublishCount", publishConfig.mPublishCount, equalTo(0));
+ collector.checkThat("mTtlSec", publishConfig.mTtlSec, equalTo(0));
+ collector.checkThat("mEnableTerminateNotification",
+ publishConfig.mEnableTerminateNotification, equalTo(true));
+ }
+
+ @Test
+ public void testPublishConfigBuilder() {
+ final String serviceName = "some_service_or_other";
+ final String serviceSpecificInfo = "long arbitrary string with some info";
+ final byte[] matchFilter = { 1, 16, 1, 22 };
+ final int publishType = PublishConfig.PUBLISH_TYPE_SOLICITED;
+ final int publishCount = 10;
+ final int publishTtl = 15;
+ final boolean enableTerminateNotification = false;
+
+ PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(serviceName)
+ .setServiceSpecificInfo(serviceSpecificInfo.getBytes()).setMatchFilter(
+ new TlvBufferUtils.TlvIterable(0, 1, matchFilter).toList())
+ .setPublishType(publishType)
+ .setPublishCount(publishCount).setTtlSec(publishTtl)
+ .setTerminateNotificationEnabled(enableTerminateNotification).build();
+
+ collector.checkThat("mServiceName", serviceName.getBytes(),
+ equalTo(publishConfig.mServiceName));
+ collector.checkThat("mServiceSpecificInfo",
+ serviceSpecificInfo.getBytes(), equalTo(publishConfig.mServiceSpecificInfo));
+ collector.checkThat("mMatchFilter", matchFilter, equalTo(publishConfig.mMatchFilter));
+ collector.checkThat("mPublishType", publishType, equalTo(publishConfig.mPublishType));
+ collector.checkThat("mPublishCount", publishCount, equalTo(publishConfig.mPublishCount));
+ collector.checkThat("mTtlSec", publishTtl, equalTo(publishConfig.mTtlSec));
+ collector.checkThat("mEnableTerminateNotification", enableTerminateNotification,
+ equalTo(publishConfig.mEnableTerminateNotification));
+ }
+
+ @Test
+ public void testPublishConfigParcel() {
+ final String serviceName = "some_service_or_other";
+ final String serviceSpecificInfo = "long arbitrary string with some info";
+ final byte[] matchFilter = { 1, 16, 1, 22 };
+ final int publishType = PublishConfig.PUBLISH_TYPE_SOLICITED;
+ final int publishCount = 10;
+ final int publishTtl = 15;
+ final boolean enableTerminateNotification = false;
+
+ PublishConfig publishConfig = new PublishConfig.Builder().setServiceName(serviceName)
+ .setServiceSpecificInfo(serviceSpecificInfo.getBytes()).setMatchFilter(
+ new TlvBufferUtils.TlvIterable(0, 1, matchFilter).toList())
+ .setPublishType(publishType)
+ .setPublishCount(publishCount).setTtlSec(publishTtl)
+ .setTerminateNotificationEnabled(enableTerminateNotification).build();
+
+ Parcel parcelW = Parcel.obtain();
+ publishConfig.writeToParcel(parcelW, 0);
+ byte[] bytes = parcelW.marshall();
+ parcelW.recycle();
+
+ Parcel parcelR = Parcel.obtain();
+ parcelR.unmarshall(bytes, 0, bytes.length);
+ parcelR.setDataPosition(0);
+ PublishConfig rereadPublishConfig = PublishConfig.CREATOR.createFromParcel(parcelR);
+
+ assertEquals(publishConfig, rereadPublishConfig);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testPublishConfigBuilderBadPublishType() {
+ new PublishConfig.Builder().setPublishType(5);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testPublishConfigBuilderNegativeCount() {
+ new PublishConfig.Builder().setPublishCount(-4);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void testPublishConfigBuilderNegativeTtl() {
+ new PublishConfig.Builder().setTtlSec(-10);
+ }
+
+ /*
+ * Ranging tests
+ */
+
+ /**
+ * Validate ranging + success flow: (1) connect, (2) create a (publish) session, (3) start
+ * ranging, (4) ranging success callback, (5) ranging aborted callback ignored (since
+ * listener removed).
+ */
+ @Test
+ public void testRangingCallbacks() throws Exception {
+ final int clientId = 4565;
+ final int sessionId = 123;
+ final int rangingId = 3482;
+ final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ final PublishConfig publishConfig = new PublishConfig.Builder().build();
+ final RttManager.RttParams rttParams = new RttManager.RttParams();
+ rttParams.deviceType = RttManager.RTT_PEER_NAN;
+ rttParams.bssid = Integer.toString(1234);
+ final RttManager.RttResult rttResults = new RttManager.RttResult();
+ rttResults.distance = 10;
+
+ when(mockAwareService.startRanging(anyInt(), anyInt(),
+ any(RttManager.ParcelableRttParams.class))).thenReturn(rangingId);
+
+ InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService,
+ mockPublishSession, mockRttListener);
+ ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
+ WifiAwareSession.class);
+ ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor
+ .forClass(IWifiAwareEventCallback.class);
+ ArgumentCaptor<IWifiAwareDiscoverySessionCallback> sessionProxyCallback = ArgumentCaptor
+ .forClass(IWifiAwareDiscoverySessionCallback.class);
+ ArgumentCaptor<WifiAwarePublishDiscoverySession> publishSession = ArgumentCaptor
+ .forClass(WifiAwarePublishDiscoverySession.class);
+ ArgumentCaptor<RttManager.ParcelableRttParams> rttParamCaptor = ArgumentCaptor
+ .forClass(RttManager.ParcelableRttParams.class);
+ ArgumentCaptor<RttManager.RttResult[]> rttResultsCaptor = ArgumentCaptor
+ .forClass(RttManager.RttResult[].class);
+
+ // (1) connect successfully
+ mDut.attach(mMockLooperHandler, configRequest, mockCallback, null);
+ inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
+ clientProxyCallback.capture(), eq(configRequest), eq(false));
+ clientProxyCallback.getValue().onConnectSuccess(clientId);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onAttached(sessionCaptor.capture());
+ WifiAwareSession session = sessionCaptor.getValue();
+
+ // (2) publish successfully
+ session.publish(publishConfig, mockSessionCallback, mMockLooperHandler);
+ inOrder.verify(mockAwareService).publish(eq(clientId), eq(publishConfig),
+ sessionProxyCallback.capture());
+ sessionProxyCallback.getValue().onSessionStarted(sessionId);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onPublishStarted(publishSession.capture());
+
+ // (3) start ranging
+ publishSession.getValue().startRanging(new RttManager.RttParams[]{rttParams},
+ mockRttListener);
+ inOrder.verify(mockAwareService).startRanging(eq(clientId), eq(sessionId),
+ rttParamCaptor.capture());
+ collector.checkThat("RttParams.deviceType", rttParams.deviceType,
+ equalTo(rttParamCaptor.getValue().mParams[0].deviceType));
+ collector.checkThat("RttParams.bssid", rttParams.bssid,
+ equalTo(rttParamCaptor.getValue().mParams[0].bssid));
+
+ // (4) ranging success callback
+ clientProxyCallback.getValue().onRangingSuccess(rangingId,
+ new RttManager.ParcelableRttResults(new RttManager.RttResult[] { rttResults }));
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockRttListener).onSuccess(rttResultsCaptor.capture());
+ collector.checkThat("RttResult.distance", rttResults.distance,
+ equalTo(rttResultsCaptor.getValue()[0].distance));
+
+ // (5) ranging aborted callback (should be ignored since listener cleared on first callback)
+ clientProxyCallback.getValue().onRangingAborted(rangingId);
+ mMockLooper.dispatchAll();
+
+ verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService,
+ mockPublishSession, mockRttListener);
+ }
+
+ /*
+ * Data-path tests
+ */
+
+ /**
+ * Validate that correct network specifier is generated for client-based data-path.
+ */
+ @Test
+ public void testNetworkSpecifierWithClient() throws Exception {
+ final int clientId = 4565;
+ final int sessionId = 123;
+ final WifiAwareManager.PeerHandle peerHandle = new WifiAwareManager.PeerHandle(123412);
+ final int role = WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_RESPONDER;
+ final String token = "Some arbitrary token string - can really be anything";
+ final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ final PublishConfig publishConfig = new PublishConfig.Builder().build();
+
+ String tokenB64 = Base64.encodeToString(token.getBytes(), Base64.DEFAULT);
+
+ ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
+ WifiAwareSession.class);
+ ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor
+ .forClass(IWifiAwareEventCallback.class);
+ ArgumentCaptor<IWifiAwareDiscoverySessionCallback> sessionProxyCallback = ArgumentCaptor
+ .forClass(IWifiAwareDiscoverySessionCallback.class);
+ ArgumentCaptor<WifiAwarePublishDiscoverySession> publishSession = ArgumentCaptor
+ .forClass(WifiAwarePublishDiscoverySession.class);
+
+ InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService,
+ mockPublishSession, mockRttListener);
+
+ // (1) connect successfully
+ mDut.attach(mMockLooperHandler, configRequest, mockCallback, null);
+ inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
+ clientProxyCallback.capture(), eq(configRequest), eq(false));
+ clientProxyCallback.getValue().onConnectSuccess(clientId);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onAttached(sessionCaptor.capture());
+ WifiAwareSession session = sessionCaptor.getValue();
+
+ // (2) publish successfully
+ session.publish(publishConfig, mockSessionCallback, mMockLooperHandler);
+ inOrder.verify(mockAwareService).publish(eq(clientId), eq(publishConfig),
+ sessionProxyCallback.capture());
+ sessionProxyCallback.getValue().onSessionStarted(sessionId);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockSessionCallback).onPublishStarted(publishSession.capture());
+
+ // (3) request a network specifier from the session
+ String networkSpecifier = publishSession.getValue().createNetworkSpecifier(peerHandle,
+ token.getBytes());
+
+ // validate format
+ JSONObject jsonObject = new JSONObject(networkSpecifier);
+ collector.checkThat("role", role,
+ equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE)));
+ collector.checkThat("client_id", clientId,
+ equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_CLIENT_ID)));
+ collector.checkThat("session_id", sessionId,
+ equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_SESSION_ID)));
+ collector.checkThat("peer_id", peerHandle.peerId,
+ equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_ID)));
+ collector.checkThat("token", tokenB64,
+ equalTo(jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_TOKEN)));
+
+ verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService,
+ mockPublishSession, mockRttListener);
+ }
+
+ /**
+ * Validate that correct network specifier is generated for a direct data-path (i.e.
+ * specifying MAC address as opposed to a client-based oqaque specification).
+ */
+ @Test
+ public void testNetworkSpecifierDirect() throws Exception {
+ final int clientId = 134;
+ final ConfigRequest configRequest = new ConfigRequest.Builder().build();
+ final byte[] someMac = HexEncoding.decode("000102030405".toCharArray(), false);
+ final int role = WifiAwareManager.WIFI_AWARE_DATA_PATH_ROLE_INITIATOR;
+ final String token = "Some arbitrary token string - can really be anything";
+
+ String tokenB64 = Base64.encodeToString(token.getBytes(), Base64.DEFAULT);
+
+ ArgumentCaptor<WifiAwareSession> sessionCaptor = ArgumentCaptor.forClass(
+ WifiAwareSession.class);
+ ArgumentCaptor<IWifiAwareEventCallback> clientProxyCallback = ArgumentCaptor
+ .forClass(IWifiAwareEventCallback.class);
+
+ InOrder inOrder = inOrder(mockCallback, mockSessionCallback, mockAwareService,
+ mockPublishSession, mockRttListener);
+
+ // (1) connect successfully
+ mDut.attach(mMockLooperHandler, configRequest, mockCallback, null);
+ inOrder.verify(mockAwareService).connect(any(IBinder.class), anyString(),
+ clientProxyCallback.capture(), eq(configRequest), eq(false));
+ clientProxyCallback.getValue().onConnectSuccess(clientId);
+ mMockLooper.dispatchAll();
+ inOrder.verify(mockCallback).onAttached(sessionCaptor.capture());
+ WifiAwareSession session = sessionCaptor.getValue();
+
+ /* (2) request a direct network specifier*/
+ String networkSpecifier = session.createNetworkSpecifier(role, someMac, token.getBytes());
+
+ /* validate format*/
+ JSONObject jsonObject = new JSONObject(networkSpecifier);
+ collector.checkThat("role", role,
+ equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_ROLE)));
+ collector.checkThat("client_id", clientId,
+ equalTo(jsonObject.getInt(WifiAwareManager.NETWORK_SPECIFIER_KEY_CLIENT_ID)));
+ collector.checkThat("peer_mac", someMac, equalTo(HexEncoding.decode(
+ jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_PEER_MAC).toCharArray(),
+ false)));
+ collector.checkThat("token", tokenB64,
+ equalTo(jsonObject.getString(WifiAwareManager.NETWORK_SPECIFIER_KEY_TOKEN)));
+
+ verifyNoMoreInteractions(mockCallback, mockSessionCallback, mockAwareService,
+ mockPublishSession, mockRttListener);
+ }
+}
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/ConfigBuilderTest.java b/wifi/tests/src/android/net/wifi/hotspot2/ConfigBuilderTest.java
new file mode 100644
index 0000000..6095929
--- /dev/null
+++ b/wifi/tests/src/android/net/wifi/hotspot2/ConfigBuilderTest.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.net.wifi.hotspot2;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.net.wifi.FakeKeys;
+import android.net.wifi.hotspot2.pps.Credential;
+import android.net.wifi.hotspot2.pps.HomeSP;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.Arrays;
+
+import org.junit.Test;
+
+/**
+ * Unit tests for {@link android.net.wifi.hotspot2.ConfigBuilder}.
+ */
+@SmallTest
+public class ConfigBuilderTest {
+ /**
+ * Hotspot 2.0 Release 1 installation file that contains a Passpoint profile and a
+ * CA (Certificate Authority) X.509 certificate {@link FakeKeys#CA_CERT0}.
+ */
+ private static final String PASSPOINT_INSTALLATION_FILE_WITH_CA_CERT =
+ "assets/hsr1/HSR1ProfileWithCACert.base64";
+ private static final String PASSPOINT_INSTALLATION_FILE_WITH_UNENCODED_DATA =
+ "assets/hsr1/HSR1ProfileWithCACert.conf";
+ private static final String PASSPOINT_INSTALLATION_FILE_WITH_INVALID_PART =
+ "assets/hsr1/HSR1ProfileWithNonBase64Part.base64";
+ private static final String PASSPOINT_INSTALLATION_FILE_WITH_MISSING_BOUNDARY =
+ "assets/hsr1/HSR1ProfileWithMissingBoundary.base64";
+ private static final String PASSPOINT_INSTALLATION_FILE_WITH_INVALID_CONTENT_TYPE =
+ "assets/hsr1/HSR1ProfileWithInvalidContentType.base64";
+ private static final String PASSPOINT_INSTALLATION_FILE_WITHOUT_PROFILE =
+ "assets/hsr1/HSR1ProfileWithoutProfile.base64";
+
+ /**
+ * Read the content of the given resource file into a String.
+ *
+ * @param filename String name of the file
+ * @return String
+ * @throws IOException
+ */
+ private String loadResourceFile(String filename) throws IOException {
+ InputStream in = getClass().getClassLoader().getResourceAsStream(filename);
+ BufferedReader reader = new BufferedReader(new InputStreamReader(in));
+ StringBuilder builder = new StringBuilder();
+ String line;
+ while ((line = reader.readLine()) != null) {
+ builder.append(line).append("\n");
+ }
+
+ return builder.toString();
+ }
+
+ /**
+ * Generate a {@link PasspointConfiguration} that matches the configuration specified in the
+ * XML file {@link #PASSPOINT_INSTALLATION_FILE_WITH_CA_CERT}.
+ *
+ * @return {@link PasspointConfiguration}
+ */
+ private PasspointConfiguration generateConfigurationFromProfile() {
+ PasspointConfiguration config = new PasspointConfiguration();
+
+ // HomeSP configuration.
+ config.homeSp = new HomeSP();
+ config.homeSp.friendlyName = "Century House";
+ config.homeSp.fqdn = "mi6.co.uk";
+ config.homeSp.roamingConsortiumOIs = new long[] {0x112233L, 0x445566L};
+
+ // Credential configuration.
+ config.credential = new Credential();
+ config.credential.realm = "shaken.stirred.com";
+ config.credential.userCredential = new Credential.UserCredential();
+ config.credential.userCredential.username = "james";
+ config.credential.userCredential.password = "Ym9uZDAwNw==";
+ config.credential.userCredential.eapType = 21;
+ config.credential.userCredential.nonEapInnerMethod = "MS-CHAP-V2";
+ config.credential.certCredential = new Credential.CertificateCredential();
+ config.credential.certCredential.certType = "x509v3";
+ config.credential.certCredential.certSha256FingerPrint = new byte[32];
+ Arrays.fill(config.credential.certCredential.certSha256FingerPrint, (byte)0x1f);
+ config.credential.simCredential = new Credential.SimCredential();
+ config.credential.simCredential.imsi = "imsi";
+ config.credential.simCredential.eapType = 24;
+ config.credential.caCertificate = FakeKeys.CA_CERT0;
+ return config;
+ }
+
+ /**
+ * Verify a valid installation file is parsed successfully with the matching contents.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseConfigFile() throws Exception {
+ String configStr = loadResourceFile(PASSPOINT_INSTALLATION_FILE_WITH_CA_CERT);
+ PasspointConfiguration expectedConfig = generateConfigurationFromProfile();
+ PasspointConfiguration actualConfig =
+ ConfigBuilder.buildPasspointConfig(
+ "application/x-wifi-config", configStr.getBytes());
+ assertTrue(actualConfig.equals(expectedConfig));
+ }
+
+ /**
+ * Verify that parsing an installation file with invalid MIME type will fail.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseConfigFileWithInvalidMimeType() throws Exception {
+ String configStr = loadResourceFile(PASSPOINT_INSTALLATION_FILE_WITH_CA_CERT);
+ assertNull(ConfigBuilder.buildPasspointConfig(
+ "application/wifi-config", configStr.getBytes()));
+ }
+
+ /**
+ * Verify that parsing an un-encoded installation file will fail.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseConfigFileWithUnencodedData() throws Exception {
+ String configStr = loadResourceFile(PASSPOINT_INSTALLATION_FILE_WITH_UNENCODED_DATA);
+ assertNull(ConfigBuilder.buildPasspointConfig(
+ "application/x-wifi-config", configStr.getBytes()));
+ }
+
+ /**
+ * Verify that parsing an installation file that contains a non-base64 part will fail.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseConfigFileWithInvalidPart() throws Exception {
+ String configStr = loadResourceFile(PASSPOINT_INSTALLATION_FILE_WITH_INVALID_PART);
+ assertNull(ConfigBuilder.buildPasspointConfig(
+ "application/x-wifi-config", configStr.getBytes()));
+ }
+
+ /**
+ * Verify that parsing an installation file that contains a missing boundary string will fail.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseConfigFileWithMissingBoundary() throws Exception {
+ String configStr = loadResourceFile(PASSPOINT_INSTALLATION_FILE_WITH_MISSING_BOUNDARY);
+ assertNull(ConfigBuilder.buildPasspointConfig(
+ "application/x-wifi-config", configStr.getBytes()));
+ }
+
+ /**
+ * Verify that parsing an installation file that contains a MIME part with an invalid content
+ * type will fail.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseConfigFileWithInvalidContentType() throws Exception {
+ String configStr = loadResourceFile(PASSPOINT_INSTALLATION_FILE_WITH_INVALID_CONTENT_TYPE);
+ assertNull(ConfigBuilder.buildPasspointConfig(
+ "application/x-wifi-config", configStr.getBytes()));
+ }
+
+ /**
+ * Verify that parsing an installation file that doesn't contain a Passpoint profile will fail.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void parseConfigFileWithoutPasspointProfile() throws Exception {
+ String configStr = loadResourceFile(PASSPOINT_INSTALLATION_FILE_WITHOUT_PROFILE);
+ assertNull(ConfigBuilder.buildPasspointConfig(
+ "application/x-wifi-config", configStr.getBytes()));
+ }
+}
\ No newline at end of file
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
index be11f0e..2350d32 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
@@ -16,8 +16,10 @@
package android.net.wifi.hotspot2;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import android.net.wifi.EAPConstants;
import android.net.wifi.hotspot2.pps.Credential;
import android.net.wifi.hotspot2.pps.HomeSP;
import android.os.Parcel;
@@ -31,6 +33,11 @@
@SmallTest
public class PasspointConfigurationTest {
+ /**
+ * Utility function for creating a {@link android.net.wifi.hotspot2.pps.HomeSP}.
+ *
+ * @return {@link android.net.wifi.hotspot2.pps.HomeSP}
+ */
private static HomeSP createHomeSp() {
HomeSP homeSp = new HomeSP();
homeSp.fqdn = "fqdn";
@@ -39,18 +46,31 @@
return homeSp;
}
+ /**
+ * Utility function for creating a {@link android.net.wifi.hotspot2.pps.Credential}.
+ *
+ * @return {@link android.net.wifi.hotspot2.pps.Credential}
+ */
private static Credential createCredential() {
Credential cred = new Credential();
cred.realm = "realm";
cred.userCredential = null;
cred.certCredential = null;
- cred.simCredential = null;
+ cred.simCredential = new Credential.SimCredential();
+ cred.simCredential.imsi = "1234*";
+ cred.simCredential.eapType = EAPConstants.EAP_SIM;
cred.caCertificate = null;
cred.clientCertificateChain = null;
cred.clientPrivateKey = null;
return cred;
}
+ /**
+ * Verify parcel write and read consistency for the given configuration.
+ *
+ * @param writeConfig The configuration to verify
+ * @throws Exception
+ */
private static void verifyParcel(PasspointConfiguration writeConfig) throws Exception {
Parcel parcel = Parcel.obtain();
writeConfig.writeToParcel(parcel, 0);
@@ -61,11 +81,21 @@
assertTrue(readConfig.equals(writeConfig));
}
+ /**
+ * Verify parcel read/write for a default configuration.
+ *
+ * @throws Exception
+ */
@Test
public void verifyParcelWithDefault() throws Exception {
verifyParcel(new PasspointConfiguration());
}
+ /**
+ * Verify parcel read/write for a configuration that contained both HomeSP and Credential.
+ *
+ * @throws Exception
+ */
@Test
public void verifyParcelWithHomeSPAndCredential() throws Exception {
PasspointConfiguration config = new PasspointConfiguration();
@@ -74,6 +104,11 @@
verifyParcel(config);
}
+ /**
+ * Verify parcel read/write for a configuration that contained only HomeSP.
+ *
+ * @throws Exception
+ */
@Test
public void verifyParcelWithHomeSPOnly() throws Exception {
PasspointConfiguration config = new PasspointConfiguration();
@@ -81,10 +116,89 @@
verifyParcel(config);
}
+ /**
+ * Verify parcel read/write for a configuration that contained only Credential.
+ *
+ * @throws Exception
+ */
@Test
public void verifyParcelWithCredentialOnly() throws Exception {
PasspointConfiguration config = new PasspointConfiguration();
config.credential = createCredential();
verifyParcel(config);
}
-}
\ No newline at end of file
+
+ /**
+ * Verify that a default/empty configuration is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateDefaultConfig() throws Exception {
+ PasspointConfiguration config = new PasspointConfiguration();
+ assertFalse(config.validate());
+ }
+
+ /**
+ * Verify that a configuration without Credential is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateConfigWithoutCredential() throws Exception {
+ PasspointConfiguration config = new PasspointConfiguration();
+ config.homeSp = createHomeSp();
+ assertFalse(config.validate());
+ }
+
+ /**
+ * Verify that a a configuration without HomeSP is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateConfigWithoutHomeSp() throws Exception {
+ PasspointConfiguration config = new PasspointConfiguration();
+ config.credential = createCredential();
+ assertFalse(config.validate());
+ }
+
+ /**
+ * Verify a valid configuration.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateValidConfig() throws Exception {
+ PasspointConfiguration config = new PasspointConfiguration();
+ config.homeSp = createHomeSp();
+ config.credential = createCredential();
+ assertTrue(config.validate());
+ }
+
+ /**
+ * Verify that copy constructor works when pass in a null source.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateCopyConstructorWithNullSource() throws Exception {
+ PasspointConfiguration copyConfig = new PasspointConfiguration(null);
+ PasspointConfiguration defaultConfig = new PasspointConfiguration();
+ assertTrue(copyConfig.equals(defaultConfig));
+ }
+
+ /**
+ * Verify that copy constructor works when pass in a valid source.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateCopyConstructorWithValidSource() throws Exception {
+ PasspointConfiguration sourceConfig = new PasspointConfiguration();
+ sourceConfig.homeSp = createHomeSp();
+ sourceConfig.credential = createCredential();
+ PasspointConfiguration copyConfig = new PasspointConfiguration(sourceConfig);
+ assertTrue(copyConfig.equals(sourceConfig));
+ }
+}
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
index 68ac4ef..9c8b749 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/CredentialTest.java
@@ -16,14 +16,19 @@
package android.net.wifi.hotspot2.pps;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import android.net.wifi.EAPConstants;
import android.net.wifi.FakeKeys;
import android.os.Parcel;
import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
+import java.util.Arrays;
import org.junit.Test;
@@ -52,15 +57,15 @@
private static Credential createCredentialWithCertificateCredential() {
Credential.CertificateCredential certCred = new Credential.CertificateCredential();
certCred.certType = "x509v3";
- certCred.certSha256FingerPrint = new byte[256];
+ certCred.certSha256FingerPrint = new byte[32];
return createCredential(null, certCred, null, FakeKeys.CA_CERT0,
new X509Certificate[] {FakeKeys.CLIENT_CERT}, FakeKeys.RSA_KEY1);
}
private static Credential createCredentialWithSimCredential() {
Credential.SimCredential simCred = new Credential.SimCredential();
- simCred.imsi = "imsi";
- simCred.eapType = 1;
+ simCred.imsi = "1234*";
+ simCred.eapType = EAPConstants.EAP_SIM;
return createCredential(null, null, simCred, null, null, null);
}
@@ -68,7 +73,7 @@
Credential.UserCredential userCred = new Credential.UserCredential();
userCred.username = "username";
userCred.password = "password";
- userCred.eapType = 1;
+ userCred.eapType = EAPConstants.EAP_TTLS;
userCred.nonEapInnerMethod = "MS-CHAP";
return createCredential(userCred, null, null, FakeKeys.CA_CERT0,
new X509Certificate[] {FakeKeys.CLIENT_CERT}, FakeKeys.RSA_KEY1);
@@ -83,23 +88,434 @@
assertTrue(readCred.equals(writeCred));
}
+ /**
+ * Verify parcel read/write for a default/empty credential.
+ *
+ * @throws Exception
+ */
@Test
public void verifyParcelWithDefault() throws Exception {
verifyParcel(new Credential());
}
+ /**
+ * Verify parcel read/write for a certificate credential.
+ *
+ * @throws Exception
+ */
@Test
public void verifyParcelWithCertificateCredential() throws Exception {
verifyParcel(createCredentialWithCertificateCredential());
}
+ /**
+ * Verify parcel read/write for a SIM credential.
+ *
+ * @throws Exception
+ */
@Test
public void verifyParcelWithSimCredential() throws Exception {
verifyParcel(createCredentialWithSimCredential());
}
+ /**
+ * Verify parcel read/write for an user credential.
+ *
+ * @throws Exception
+ */
@Test
public void verifyParcelWithUserCredential() throws Exception {
verifyParcel(createCredentialWithUserCredential());
}
-}
+
+ /**
+ * Verify a valid user credential.
+ * @throws Exception
+ */
+ @Test
+ public void validateUserCredential() throws Exception {
+ Credential cred = new Credential();
+ cred.realm = "realm";
+ cred.userCredential = new Credential.UserCredential();
+ cred.userCredential.username = "username";
+ cred.userCredential.password = "password";
+ cred.userCredential.eapType = EAPConstants.EAP_TTLS;
+ cred.userCredential.nonEapInnerMethod = "MS-CHAP";
+ cred.caCertificate = FakeKeys.CA_CERT0;
+ assertTrue(cred.validate());
+ }
+
+ /**
+ * Verify that an user credential without CA Certificate is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateUserCredentialWithoutCaCert() throws Exception {
+ Credential cred = new Credential();
+ cred.realm = "realm";
+ cred.userCredential = new Credential.UserCredential();
+ cred.userCredential.username = "username";
+ cred.userCredential.password = "password";
+ cred.userCredential.eapType = EAPConstants.EAP_TTLS;
+ cred.userCredential.nonEapInnerMethod = "MS-CHAP";
+ assertFalse(cred.validate());
+ }
+
+ /**
+ * Verify that an user credential with EAP type other than EAP-TTLS is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateUserCredentialWithEapTls() throws Exception {
+ Credential cred = new Credential();
+ cred.realm = "realm";
+ cred.userCredential = new Credential.UserCredential();
+ cred.userCredential.username = "username";
+ cred.userCredential.password = "password";
+ cred.userCredential.eapType = EAPConstants.EAP_TLS;
+ cred.userCredential.nonEapInnerMethod = "MS-CHAP";
+ cred.caCertificate = FakeKeys.CA_CERT0;
+ assertFalse(cred.validate());
+ }
+
+
+ /**
+ * Verify that an user credential without realm is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateUserCredentialWithoutRealm() throws Exception {
+ Credential cred = new Credential();
+ cred.userCredential = new Credential.UserCredential();
+ cred.userCredential.username = "username";
+ cred.userCredential.password = "password";
+ cred.userCredential.eapType = EAPConstants.EAP_TTLS;
+ cred.userCredential.nonEapInnerMethod = "MS-CHAP";
+ cred.caCertificate = FakeKeys.CA_CERT0;
+ assertFalse(cred.validate());
+ }
+
+ /**
+ * Verify that an user credential without username is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateUserCredentialWithoutUsername() throws Exception {
+ Credential cred = new Credential();
+ cred.realm = "realm";
+ cred.userCredential = new Credential.UserCredential();
+ cred.userCredential.password = "password";
+ cred.userCredential.eapType = EAPConstants.EAP_TTLS;
+ cred.userCredential.nonEapInnerMethod = "MS-CHAP";
+ cred.caCertificate = FakeKeys.CA_CERT0;
+ assertFalse(cred.validate());
+ }
+
+ /**
+ * Verify that an user credential without password is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateUserCredentialWithoutPassword() throws Exception {
+ Credential cred = new Credential();
+ cred.realm = "realm";
+ cred.userCredential = new Credential.UserCredential();
+ cred.userCredential.username = "username";
+ cred.userCredential.eapType = EAPConstants.EAP_TTLS;
+ cred.userCredential.nonEapInnerMethod = "MS-CHAP";
+ cred.caCertificate = FakeKeys.CA_CERT0;
+ assertFalse(cred.validate());
+ }
+
+ /**
+ * Verify that an user credential without auth methoh (non-EAP inner method) is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateUserCredentialWithoutAuthMethod() throws Exception {
+ Credential cred = new Credential();
+ cred.realm = "realm";
+ cred.userCredential = new Credential.UserCredential();
+ cred.userCredential.username = "username";
+ cred.userCredential.password = "password";
+ cred.userCredential.eapType = EAPConstants.EAP_TTLS;
+ cred.caCertificate = FakeKeys.CA_CERT0;
+ assertFalse(cred.validate());
+ }
+
+ /**
+ * Verify a certificate credential. CA Certificate, client certificate chain,
+ * and client private key are all required. Also the digest for client
+ * certificate must match the fingerprint specified in the certificate credential.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateCertCredential() throws Exception {
+ Credential cred = new Credential();
+ cred.realm = "realm";
+ // Setup certificate credential.
+ cred.certCredential = new Credential.CertificateCredential();
+ cred.certCredential.certType = "x509v3";
+ cred.certCredential.certSha256FingerPrint =
+ MessageDigest.getInstance("SHA-256").digest(FakeKeys.CLIENT_CERT.getEncoded());
+ // Setup certificates and private key.
+ cred.caCertificate = FakeKeys.CA_CERT0;
+ cred.clientCertificateChain = new X509Certificate[] {FakeKeys.CLIENT_CERT};
+ cred.clientPrivateKey = FakeKeys.RSA_KEY1;
+ assertTrue(cred.validate());
+ }
+
+ /**
+ * Verify that an certificate credential without CA Certificate is invalid.
+ *
+ * @throws Exception
+ */
+ public void validateCertCredentialWithoutCaCert() throws Exception {
+ Credential cred = new Credential();
+ cred.realm = "realm";
+ // Setup certificate credential.
+ cred.certCredential = new Credential.CertificateCredential();
+ cred.certCredential.certType = "x509v3";
+ cred.certCredential.certSha256FingerPrint =
+ MessageDigest.getInstance("SHA-256").digest(FakeKeys.CLIENT_CERT.getEncoded());
+ // Setup certificates and private key.
+ cred.clientCertificateChain = new X509Certificate[] {FakeKeys.CLIENT_CERT};
+ cred.clientPrivateKey = FakeKeys.RSA_KEY1;
+ assertFalse(cred.validate());
+ }
+
+ /**
+ * Verify that a certificate credential without client certificate chain is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateCertCredentialWithoutClientCertChain() throws Exception {
+ Credential cred = new Credential();
+ cred.realm = "realm";
+ // Setup certificate credential.
+ cred.certCredential = new Credential.CertificateCredential();
+ cred.certCredential.certType = "x509v3";
+ cred.certCredential.certSha256FingerPrint =
+ MessageDigest.getInstance("SHA-256").digest(FakeKeys.CLIENT_CERT.getEncoded());
+ // Setup certificates and private key.
+ cred.caCertificate = FakeKeys.CA_CERT0;
+ cred.clientPrivateKey = FakeKeys.RSA_KEY1;
+ assertFalse(cred.validate());
+ }
+
+ /**
+ * Verify that a certificate credential without client private key is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateCertCredentialWithoutClientPrivateKey() throws Exception {
+ Credential cred = new Credential();
+ cred.realm = "realm";
+ // Setup certificate credential.
+ cred.certCredential = new Credential.CertificateCredential();
+ cred.certCredential.certType = "x509v3";
+ cred.certCredential.certSha256FingerPrint =
+ MessageDigest.getInstance("SHA-256").digest(FakeKeys.CLIENT_CERT.getEncoded());
+ // Setup certificates and private key.
+ cred.caCertificate = FakeKeys.CA_CERT0;
+ cred.clientCertificateChain = new X509Certificate[] {FakeKeys.CLIENT_CERT};
+ assertFalse(cred.validate());
+ }
+
+ /**
+ * Verify that a certificate credential with mismatch client certificate fingerprint
+ * is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateCertCredentialWithMismatchFingerprint() throws Exception {
+ Credential cred = new Credential();
+ cred.realm = "realm";
+ // Setup certificate credential.
+ cred.certCredential = new Credential.CertificateCredential();
+ cred.certCredential.certType = "x509v3";
+ cred.certCredential.certSha256FingerPrint = new byte[32];
+ Arrays.fill(cred.certCredential.certSha256FingerPrint, (byte)0);
+ // Setup certificates and private key.
+ cred.caCertificate = FakeKeys.CA_CERT0;
+ cred.clientCertificateChain = new X509Certificate[] {FakeKeys.CLIENT_CERT};
+ cred.clientPrivateKey = FakeKeys.RSA_KEY1;
+ assertFalse(cred.validate());
+ }
+
+ /**
+ * Verify a SIM credential using EAP-SIM.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateSimCredentialWithEapSim() throws Exception {
+ Credential cred = new Credential();
+ cred.realm = "realm";
+ // Setup SIM credential.
+ cred.simCredential = new Credential.SimCredential();
+ cred.simCredential.imsi = "1234*";
+ cred.simCredential.eapType = EAPConstants.EAP_SIM;
+ assertTrue(cred.validate());
+ }
+
+ /**
+ * Verify a SIM credential using EAP-AKA.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateSimCredentialWithEapAka() throws Exception {
+ Credential cred = new Credential();
+ cred.realm = "realm";
+ // Setup SIM credential.
+ cred.simCredential = new Credential.SimCredential();
+ cred.simCredential.imsi = "1234*";
+ cred.simCredential.eapType = EAPConstants.EAP_AKA;
+ assertTrue(cred.validate());
+ }
+
+ /**
+ * Verify a SIM credential using EAP-AKA-PRIME.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateSimCredentialWithEapAkaPrime() throws Exception {
+ Credential cred = new Credential();
+ cred.realm = "realm";
+ // Setup SIM credential.
+ cred.simCredential = new Credential.SimCredential();
+ cred.simCredential.imsi = "1234*";
+ cred.simCredential.eapType = EAPConstants.EAP_AKA_PRIME;
+ assertTrue(cred.validate());
+ }
+
+ /**
+ * Verify that a SIM credential without IMSI is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateSimCredentialWithoutIMSI() throws Exception {
+ Credential cred = new Credential();
+ cred.realm = "realm";
+ // Setup SIM credential.
+ cred.simCredential = new Credential.SimCredential();
+ cred.simCredential.eapType = EAPConstants.EAP_SIM;
+ assertFalse(cred.validate());
+ }
+
+ /**
+ * Verify that a SIM credential with an invalid IMSI is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateSimCredentialWithInvalidIMSI() throws Exception {
+ Credential cred = new Credential();
+ cred.realm = "realm";
+ // Setup SIM credential.
+ cred.simCredential = new Credential.SimCredential();
+ cred.simCredential.imsi = "dummy";
+ cred.simCredential.eapType = EAPConstants.EAP_SIM;
+ assertFalse(cred.validate());
+ }
+
+ /**
+ * Verify that a SIM credential with invalid EAP type is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateSimCredentialWithEapTls() throws Exception {
+ Credential cred = new Credential();
+ cred.realm = "realm";
+ // Setup SIM credential.
+ cred.simCredential = new Credential.SimCredential();
+ cred.simCredential.imsi = "1234*";
+ cred.simCredential.eapType = EAPConstants.EAP_TLS;
+ assertFalse(cred.validate());
+ }
+
+ /**
+ * Verify that a credential contained both an user and a SIM credential is invalid.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateCredentialWithUserAndSimCredential() throws Exception {
+ Credential cred = new Credential();
+ cred.realm = "realm";
+ // Setup user credential with EAP-TTLS.
+ cred.userCredential = new Credential.UserCredential();
+ cred.userCredential.username = "username";
+ cred.userCredential.password = "password";
+ cred.userCredential.eapType = EAPConstants.EAP_TTLS;
+ cred.userCredential.nonEapInnerMethod = "MS-CHAP";
+ cred.caCertificate = FakeKeys.CA_CERT0;
+ // Setup SIM credential.
+ cred.simCredential = new Credential.SimCredential();
+ cred.simCredential.imsi = "1234*";
+ cred.simCredential.eapType = EAPConstants.EAP_SIM;
+ assertFalse(cred.validate());
+ }
+
+ /**
+ * Verify that copy constructor works when pass in a null source.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateCopyConstructorWithNullSource() throws Exception {
+ Credential copyCred = new Credential(null);
+ Credential defaultCred = new Credential();
+ assertTrue(copyCred.equals(defaultCred));
+ }
+
+ /**
+ * Verify that copy constructor works when pass in a source with user credential.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateCopyConstructorWithSourceWithUserCred() throws Exception {
+ Credential sourceCred = createCredentialWithUserCredential();
+ Credential copyCred = new Credential(sourceCred);
+ assertTrue(copyCred.equals(sourceCred));
+ }
+
+ /**
+ * Verify that copy constructor works when pass in a source with certificate credential.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateCopyConstructorWithSourceWithCertCred() throws Exception {
+ Credential sourceCred = createCredentialWithCertificateCredential();
+ Credential copyCred = new Credential(sourceCred);
+ assertTrue(copyCred.equals(sourceCred));
+ }
+
+ /**
+ * Verify that copy constructor works when pass in a source with SIM credential.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateCopyConstructorWithSourceWithSimCred() throws Exception {
+ Credential sourceCred = createCredentialWithSimCredential();
+ Credential copyCred = new Credential(sourceCred);
+ assertTrue(copyCred.equals(sourceCred));
+ }
+}
\ No newline at end of file
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSPTest.java b/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSPTest.java
index 0d2da64..c707993 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSPTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/pps/HomeSPTest.java
@@ -16,13 +16,12 @@
package android.net.wifi.hotspot2.pps;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import android.os.Parcel;
import android.test.suitebuilder.annotation.SmallTest;
-import java.util.HashMap;
-
import org.junit.Test;
/**
@@ -47,13 +46,103 @@
assertTrue(readHomeSp.equals(writeHomeSp));
}
+ /**
+ * Verify parcel read/write for an empty HomeSP.
+ *
+ * @throws Exception
+ */
@Test
public void verifyParcelWithEmptyHomeSP() throws Exception {
verifyParcel(new HomeSP());
}
+ /**
+ * Verify parcel read/write for a valid HomeSP.
+ *
+ * @throws Exception
+ */
@Test
public void verifyParcelWithValidHomeSP() throws Exception {
verifyParcel(createHomeSp());
}
+
+ /**
+ * Verify that a HomeSP is valid when both FQDN and Friendly Name
+ * are provided.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateValidHomeSP() throws Exception {
+ HomeSP homeSp = new HomeSP();
+ homeSp.fqdn = "fqdn";
+ homeSp.friendlyName = "friendly name";
+ assertTrue(homeSp.validate());
+ }
+
+ /**
+ * Verify that a HomeSP is not valid when FQDN is not provided
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateHomeSpWithoutFqdn() throws Exception {
+ HomeSP homeSp = new HomeSP();
+ homeSp.friendlyName = "friendly name";
+ assertFalse(homeSp.validate());
+ }
+
+ /**
+ * Verify that a HomeSP is not valid when Friendly Name is not provided
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateHomeSpWithoutFriendlyName() throws Exception {
+ HomeSP homeSp = new HomeSP();
+ homeSp.fqdn = "fqdn";
+ assertFalse(homeSp.validate());
+ }
+
+ /**
+ * Verify that a HomeSP is valid when the optional Roaming Consortium OIs are
+ * provided.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateHomeSpWithRoamingConsoritums() throws Exception {
+ HomeSP homeSp = new HomeSP();
+ homeSp.fqdn = "fqdn";
+ homeSp.friendlyName = "friendly name";
+ homeSp.roamingConsortiumOIs = new long[] {0x55, 0x66};
+ assertTrue(homeSp.validate());
+ }
+
+ /**
+ * Verify that copy constructor works when pass in a null source.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateCopyConstructorFromNullSource() throws Exception {
+ HomeSP copySp = new HomeSP(null);
+ HomeSP defaultSp = new HomeSP();
+ assertTrue(copySp.equals(defaultSp));
+ }
+
+ /**
+ * Verify that copy constructor works when pass in a valid source.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void validateCopyConstructorFromValidSource() throws Exception {
+ HomeSP sourceSp = new HomeSP();
+ sourceSp.fqdn = "fqdn";
+ sourceSp.friendlyName = "friendlyName";
+ sourceSp.roamingConsortiumOIs = new long[] {0x55, 0x66};
+ HomeSP copySp = new HomeSP(sourceSp);
+ assertTrue(copySp.equals(sourceSp));
+ }
}