diff options
155 files changed, 5852 insertions, 2077 deletions
diff --git a/Android.mk b/Android.mk index db5dd019fa56..cdb3834e3043 100644 --- a/Android.mk +++ b/Android.mk @@ -494,7 +494,7 @@ framework_res_R_stamp := \ 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 diff --git a/api/current.txt b/api/current.txt index 9cfbbc7d4ef3..d22006d3c9e8 100644 --- a/api/current.txt +++ b/api/current.txt @@ -36363,6 +36363,7 @@ package android.telecom { 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 @@ package android.telephony { 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 @@ package android.telephony { 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 @@ package android.telephony { 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 @@ package android.telephony { 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 @@ package android.telephony { 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 @@ package android.telephony { 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 @@ package android.telephony { 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 @@ package android.telephony { 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 @@ package java.util.zip { 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 @@ package java.util.zip { 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 @@ package java.util.zip { 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 fab5e0091923..01d4f47bdf33 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -35032,6 +35032,7 @@ package android.provider { 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 @@ package android.telecom { 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 @@ package android.telephony { 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 @@ package android.telephony { 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 @@ package android.telephony { 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 @@ package android.telephony { 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 @@ package android.telephony { 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 @@ package java.util.zip { 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 @@ package java.util.zip { 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 @@ package java.util.zip { 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 7a58e1e17177..e21afe2a4c7c 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -36440,6 +36440,7 @@ package android.telecom { 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 @@ package android.telephony { 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 @@ package android.telephony { 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 @@ package android.telephony { 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 @@ package android.telephony { 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 @@ package android.telephony { 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 @@ package android.telephony { 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 @@ package android.telephony { 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 @@ package android.telephony { 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 @@ package java.util.zip { 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 @@ package java.util.zip { 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 @@ package java.util.zip { 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 b5f1c2aa2b4f..eaad3a7ec529 100644 --- a/cmds/app_process/Android.mk +++ b/cmds/app_process/Android.mk @@ -1,79 +1,67 @@ LOCAL_PATH:= $(call my-dir) -# 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 := \ - libwilhelm \ - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= \ - app_main.cpp - -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_SHARED_LIBRARIES := \ - libdl \ + libandroid_runtime \ + libbinder \ libcutils \ - libutils \ + libdl \ liblog \ - libbinder \ libnativeloader \ - libandroid_runtime \ - $(app_process_common_shared_libs) \ - -LOCAL_WHOLE_STATIC_LIBRARIES := libsigchain + libutils \ -LOCAL_MODULE:= app_process -LOCAL_MULTILIB := both -LOCAL_MODULE_STEM_32 := app_process32 -LOCAL_MODULE_STEM_64 := app_process64 +# 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 += \ + libwilhelm \ -LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code +app_process_common_static_libs := \ + libsigchain \ -include $(BUILD_EXECUTABLE) +app_process_src_files := \ + app_main.cpp \ -# Create a symlink from app_process to app_process32 or 64 -# depending on the target configuration. -include $(BUILD_SYSTEM)/executable_prefer_symlink.mk +app_process_cflags := \ + -Wall -Werror -Wunused -Wunreachable-code -# Build a variant of app_process binary linked with ASan runtime. -# ARM-only at the moment. -ifeq ($(TARGET_ARCH),arm) +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_SHARED_LIBRARIES := \ - libcutils \ - libutils \ - liblog \ - libbinder \ - libandroid_runtime \ - $(app_process_common_shared_libs) \ +LOCAL_LDFLAGS_32 := $(app_process_ldflags_32) +LOCAL_LDFLAGS_64 := $(app_process_ldflags_64) -LOCAL_WHOLE_STATIC_LIBRARIES := libsigchain +LOCAL_SHARED_LIBRARIES := $(app_process_common_shared_libs) -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_WHOLE_STATIC_LIBRARIES := $(app_process_common_static_libs) -LOCAL_MODULE := app_process__asan +LOCAL_MODULE:= app_process 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 += $(app_process_cflags) -LOCAL_CFLAGS += -Wall -Werror -Wunused -Wunreachable-code +# 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) -endif # ifeq($(TARGET_ARCH),arm) +# 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 +endif diff --git a/cmds/svc/src/com/android/commands/svc/UsbCommand.java b/cmds/svc/src/com/android/commands/svc/UsbCommand.java index a6ef25fc4479..4dcb05e4f85d 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 @@ public class UsbCommand extends Svc.Command { 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 94214ff9694f..633dd9787cb1 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 @@ public class WifiCommand extends Svc.Command { 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 c1b37e6d2623..d242ba194c6a 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -5149,7 +5149,14 @@ public final class ActivityThread { } 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 6a73829da154..ef2db4a0d795 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 @@ class ApplicationLoaders { } } + /** + * 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 b889c8f9a81b..8824c981dc45 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -339,39 +339,43 @@ public final class LoadedApk { * 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; - - 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); + // 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; + + 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 (!instrumentationAppDir.equals(instrumentedAppDir)) { + outZipPaths.add(instrumentedAppDir); + if (instrumentedSplitAppDirs != null) { + Collections.addAll(outZipPaths, instrumentedSplitAppDirs); + } } - } - if (outLibPaths != null) { - outLibPaths.add(instrumentationLibDir); - if (!instrumentationLibDir.equals(instrumentedLibDir)) { - outLibPaths.add(instrumentedLibDir); + if (outLibPaths != null) { + outLibPaths.add(instrumentationLibDir); + if (!instrumentationLibDir.equals(instrumentedLibDir)) { + outLibPaths.add(instrumentedLibDir); + } } - } - if (!instrumentedAppDir.equals(instrumentationAppDir)) { - instrumentationLibs = getLibrariesFor(instrumentationPackageName); + if (!instrumentedAppDir.equals(instrumentationAppDir)) { + instrumentationLibs = getLibrariesFor(instrumentationPackageName); + } } } diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index c4673a3b0b4c..744bd305a9cc 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -793,7 +793,8 @@ public class ResourcesManager { 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 @@ public class ResourcesManager { 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/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index 11c27355c787..4271e3f99dff 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -601,10 +601,6 @@ public final class BluetoothAdapter { */ 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 @@ public final class BluetoothAdapter { } /** - * 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 diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl index 96a1ae8b9455..7c5458b7704b 100644 --- a/core/java/android/bluetooth/IBluetooth.aidl +++ b/core/java/android/bluetooth/IBluetooth.aidl @@ -100,7 +100,6 @@ interface IBluetooth boolean factoryReset(); boolean isMultiAdvertisementSupported(); - boolean isPeripheralModeSupported(); boolean isOffloadedFilteringSupported(); boolean isOffloadedScanBatchingSupported(); boolean isActivityAndEnergyReportingSupported(); diff --git a/core/java/android/bluetooth/OobData.java b/core/java/android/bluetooth/OobData.java index f53ca94a1d5c..9e87230c686e 100644 --- a/core/java/android/bluetooth/OobData.java +++ b/core/java/android/bluetooth/OobData.java @@ -30,10 +30,24 @@ import android.util.Log; * @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 class OobData implements Parcelable { public OobData() { } private OobData(Parcel in) { + leBluetoothDeviceAddress = in.createByteArray(); securityManagerTk = in.createByteArray(); leSecureConnectionsConfirmation = in.createByteArray(); leSecureConnectionsRandom = in.createByteArray(); @@ -77,6 +92,7 @@ public class OobData implements Parcelable { @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 26f2dea9022f..94d03e533dff 100644 --- a/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java +++ b/core/java/android/bluetooth/le/BluetoothLeAdvertiser.java @@ -111,12 +111,6 @@ public final class BluetoothLeAdvertiser { 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 @@ public final class BluetoothLeAdvertiser { 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 @@ public final class BluetoothLeAdvertiser { 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 @@ public final class BluetoothLeAdvertiser { } 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 @@ public final class BluetoothLeAdvertiser { 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 @@ public final class BluetoothLeAdvertiser { } 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 @@ public final class BluetoothLeAdvertiser { // 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/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl index 6e4c9de1c8ef..00b0bffdd1f5 100644 --- a/core/java/android/hardware/usb/IUsbManager.aidl +++ b/core/java/android/hardware/usb/IUsbManager.aidl @@ -87,15 +87,13 @@ interface IUsbManager /* 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 f9a7d192b5bb..6341cbc79f3f 100644 --- a/core/java/android/hardware/usb/UsbManager.java +++ b/core/java/android/hardware/usb/UsbManager.java @@ -509,33 +509,23 @@ public class UsbManager { * {@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) { - 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) { + public void setCurrentFunction(String function, boolean usbDataUnlocked) { try { - mService.setUsbDataUnlocked(unlocked); + mService.setCurrentFunction(function, usbDataUnlocked); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/core/java/android/net/NetworkScoreManager.java b/core/java/android/net/NetworkScoreManager.java index 01c160f1bc45..b93bf59d5ac4 100644 --- a/core/java/android/net/NetworkScoreManager.java +++ b/core/java/android/net/NetworkScoreManager.java @@ -144,7 +144,7 @@ public class NetworkScoreManager { * 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 29291ca90467..ebb31c9056a9 100644 --- a/core/java/android/net/NetworkScorerAppManager.java +++ b/core/java/android/net/NetworkScorerAppManager.java @@ -41,14 +41,17 @@ import java.util.List; * * @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 @@ public final class NetworkScorerAppManager { * * @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 @@ public final class NetworkScorerAppManager { } 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 @@ public final class NetworkScorerAppManager { * 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 @@ public final class NetworkScorerAppManager { * * <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 @@ public final class NetworkScorerAppManager { 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 @@ public final class NetworkScorerAppManager { } /** 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 @@ public final class NetworkScorerAppManager { } // 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 1ac9fca51b77..e7436be273a3 100644 --- a/core/java/android/net/TrafficStats.java +++ b/core/java/android/net/TrafficStats.java @@ -108,6 +108,26 @@ public class TrafficStats { */ 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 ea8ba2f1e6df..50a3f4c01051 100644 --- a/core/java/android/os/Binder.java +++ b/core/java/android/os/Binder.java @@ -129,6 +129,17 @@ public class Binder implements IBinder { 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 58acbcf5684b..c7bf0fd6ba49 100644 --- a/core/java/android/os/CountDownTimer.java +++ b/core/java/android/os/CountDownTimer.java @@ -125,19 +125,28 @@ public abstract class CountDownTimer { 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(); - - // special case: user's onTick took more than interval to - // complete, skip to next interval - while (delay < 0) delay += mCountdownInterval; + long lastTickDuration = SystemClock.elapsedRealtime() - lastTickStart; + long delay; + + 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 0e7da63a2215..481b2dc096d5 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 @@ public abstract class HwBinder implements IHwBinder { 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 1479035df978..e47c2380ca83 100644 --- a/core/java/android/os/SystemProperties.java +++ b/core/java/android/os/SystemProperties.java @@ -39,6 +39,7 @@ public class SystemProperties 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 @@ public class SystemProperties } } } + + /* + * 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 5e9380ed159a..2f93afdc177d 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -370,6 +370,22 @@ public final class Settings { "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 diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java index f41a8380beab..f1e8fc2d310b 100644 --- a/core/java/android/webkit/WebViewFactory.java +++ b/core/java/android/webkit/WebViewFactory.java @@ -56,7 +56,9 @@ import java.util.zip.ZipFile; @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 bc6e7b4a9dd3..e0d589a8a8fb 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 */ @@ -48,6 +53,10 @@ public class WebViewZygote { return sPackage.packageName; } + public static boolean isMultiprocessEnabled() { + return sMultiprocessEnabled && sPackage != null; + } + public static void setMultiprocessEnabled(boolean enabled) { sMultiprocessEnabled = enabled; @@ -122,11 +131,21 @@ public class WebViewZygote { try { sZygote = new ZygoteProcess("webview_zygote", null); - String packagePath = sPackage.applicationInfo.sourceDir; - String libsPath = sPackage.applicationInfo.nativeLibraryDir; - - Log.d(LOGTAG, "Preloading package " + packagePath + " " + libsPath); - sZygote.preloadPackageForAbi(packagePath, libsPath, Build.SUPPORTED_ABIS[0]); + // 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 " + 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 11dd0e8771a7..d968e3c939ab 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 @@ class WebViewZygoteInit { @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/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java index b0d45e1d1db9..be10608df2a3 100644 --- a/core/java/com/android/internal/util/StateMachine.java +++ b/core/java/com/android/internal/util/StateMachine.java @@ -1190,6 +1190,26 @@ public class StateMachine { } /** + * 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 @@ public class StateMachine { } /** + * 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 143cc2e66253..9a596c6dc105 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -267,7 +267,8 @@ LOCAL_SHARED_LIBRARIES := \ 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 1a33d9111fdb..10090a1f6942 100644 --- a/core/jni/android_os_HwBinder.cpp +++ b/core/jni/android_os_HwBinder.cpp @@ -34,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" @@ -41,10 +43,15 @@ using android::AndroidRuntime; namespace android { +static jclass gArrayListClass; +static struct { + jmethodID size; + jmethodID get; +} gArrayListMethods; + static struct fields_t { jfieldID contextID; jmethodID onTransactID; - } gFields; // static @@ -199,45 +206,46 @@ static void JHwBinder_native_transact( 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? } - using android::hidl::manager::V1_0::IServiceManager; + 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); + } - const IServiceManager::Version kVersion { - .major = static_cast<uint16_t>(versionMajor), - .minor = static_cast<uint16_t>(versionMinor), - }; + 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); bool ok = hardware::defaultServiceManager()->add( - String8(String16( - reinterpret_cast<const char16_t *>(serviceName), - env->GetStringLength(serviceNameObj))).string(), - binder, - kVersion); + interfaceChain, + serviceName, + binder); - env->ReleaseStringCritical(serviceNameObj, serviceName); + env->ReleaseStringUTFChars(serviceNameObj, serviceName); serviceName = NULL; if (ok) { @@ -251,52 +259,43 @@ static void JHwBinder_native_registerService( static jobject JHwBinder_native_getService( JNIEnv *env, jclass /* clazzObj */, - jstring serviceNameObj, - jint versionMajor, - jint versionMinor) { - if (serviceNameObj == NULL) { + jstring ifaceNameObj, + jstring serviceNameObj) { + + if (ifaceNameObj == NULL) { jniThrowException(env, "java/lang/NullPointerException", NULL); return NULL; } - - if (versionMajor < 0 - || versionMajor > 65535 - || versionMinor < 0 - || versionMinor > 65535) { - jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + if (serviceNameObj == NULL) { + jniThrowException(env, "java/lang/NullPointerException", NULL); return NULL; } - const jchar *serviceName = env->GetStringCritical(serviceNameObj, NULL); - + const char *ifaceName = env->GetStringUTFChars(ifaceNameObj, NULL); + if (ifaceName == NULL) { + return NULL; // XXX exception already pending? + } + 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? } - using android::hidl::manager::V1_0::IServiceManager; - - const IServiceManager::Version kVersion { - .major = static_cast<uint16_t>(versionMajor), - .minor = static_cast<uint16_t>(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()->get( - String8(String16( - reinterpret_cast<const char16_t *>(serviceName), - env->GetStringLength(serviceNameObj))).string(), - kVersion, + 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) { @@ -318,16 +317,21 @@ static JNINativeMethod gMethods[] = { "(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 6bd82e98e911..09204880881b 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 7387b294bbcd..a10d80746e46 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 @@ static void JHwParcel_native_writeInterfaceToken( 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 @@ static void JHwParcel_native_enforceInterface( 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 @@ static void JHwParcel_native_writeStatus( 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 @@ static void JHwParcel_native_verifySuccess(JNIEnv *env, jobject thiz) { 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 @@ static void JHwParcel_native_writeString( 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 @@ static void JHwParcel_native_write ## Suffix ## Vector( \ if (err == OK) { \ size_t childHandle; \ \ - err = vec->writeEmbeddedToParcel( \ + err = ::android::hardware::writeEmbeddedToParcel( \ + *vec, \ parcel, \ parentHandle, \ 0 /* parentOffset */, \ @@ -507,7 +510,8 @@ static void JHwParcel_native_writeBoolVector( if (err == OK) { size_t childHandle; - err = vec->writeEmbeddedToParcel( + err = ::android::hardware::writeEmbeddedToParcel( + *vec, parcel, parentHandle, 0 /* parentOffset */, @@ -567,7 +571,8 @@ static jstring JHwParcel_native_readString(JNIEnv *env, jobject thiz) { 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 @@ static Type ## Array JHwParcel_native_read ## Suffix ## Vector( \ \ 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 @@ static jbooleanArray JHwParcel_native_readBoolVector( 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 @@ static jobjectArray JHwParcel_native_readStringVector( } 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 @@ static void JHwParcel_native_writeStringVector( 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_SystemProperties.cpp b/core/jni/android_os_SystemProperties.cpp index 5dace6b7e536..8844fb0a261f 100644 --- a/core/jni/android_os_SystemProperties.cpp +++ b/core/jni/android_os_SystemProperties.cpp @@ -220,6 +220,11 @@ static void SystemProperties_add_change_callback(JNIEnv *env, jobject clazz) } } +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 @@ static const JNINativeMethod method_table[] = { (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_EventLog.cpp b/core/jni/android_util_EventLog.cpp index 173afd8c0f05..3219d594e2e2 100644 --- a/core/jni/android_util_EventLog.cpp +++ b/core/jni/android_util_EventLog.cpp @@ -16,16 +16,14 @@ #include <fcntl.h> +#include <log/log_event_list.h> + #include "JNIHelp.h" #include "core_jni_helpers.h" #include "jni.h" -#include <log/logger.h> #define UNUSED __attribute__((__unused__)) -// The size of the tag number comes out of the payload size. -#define MAX_EVENT_PAYLOAD (LOGGER_ENTRY_MAX_PAYLOAD - sizeof(int32_t)) - namespace android { static jclass gCollectionClass; @@ -53,7 +51,9 @@ static jint android_util_EventLog_writeEvent_Integer(JNIEnv* env UNUSED, jobject clazz UNUSED, jint tag, jint value) { - return android_btWriteLog(tag, EVENT_TYPE_INT, &value, sizeof(value)); + android_log_event_list ctx(tag); + ctx << (int32_t)value; + return ctx.write(); } /* @@ -64,7 +64,9 @@ static jint android_util_EventLog_writeEvent_Long(JNIEnv* env UNUSED, jobject clazz UNUSED, jint tag, jlong value) { - return android_btWriteLog(tag, EVENT_TYPE_LONG, &value, sizeof(value)); + android_log_event_list ctx(tag); + ctx << (int64_t)value; + return ctx.write(); } /* @@ -75,7 +77,9 @@ static jint android_util_EventLog_writeEvent_Float(JNIEnv* env UNUSED, jobject clazz UNUSED, jint tag, jfloat value) { - return android_btWriteLog(tag, EVENT_TYPE_FLOAT, &value, sizeof(value)); + android_log_event_list ctx(tag); + ctx << (float)value; + return ctx.write(); } /* @@ -85,22 +89,17 @@ static jint android_util_EventLog_writeEvent_Float(JNIEnv* env UNUSED, static jint android_util_EventLog_writeEvent_String(JNIEnv* env, jobject clazz UNUSED, jint tag, jstring value) { - uint8_t buf[MAX_EVENT_PAYLOAD]; - + android_log_event_list ctx(tag); // Don't throw NPE -- I feel like it's sort of mean for a logging function // to be all crashy if you pass in NULL -- but make the NULL value explicit. - const char *str = value != NULL ? env->GetStringUTFChars(value, NULL) : "NULL"; - uint32_t len = strlen(str); - size_t max = sizeof(buf) - sizeof(len) - 2; // Type byte, final newline - if (len > max) len = max; - - buf[0] = EVENT_TYPE_STRING; - memcpy(&buf[1], &len, sizeof(len)); - memcpy(&buf[1 + sizeof(len)], str, len); - buf[1 + sizeof(len) + len] = '\n'; - - if (value != NULL) env->ReleaseStringUTFChars(value, str); - return android_bWriteLog(tag, buf, 2 + sizeof(len) + len); + if (value != NULL) { + const char *str = env->GetStringUTFChars(value, NULL); + ctx << str; + env->ReleaseStringUTFChars(value, str); + } else { + ctx << "NULL"; + } + return ctx.write(); } /* @@ -109,45 +108,29 @@ static jint android_util_EventLog_writeEvent_String(JNIEnv* env, */ static jint android_util_EventLog_writeEvent_Array(JNIEnv* env, jobject clazz, jint tag, jobjectArray value) { + android_log_event_list ctx(tag); + if (value == NULL) { - return android_util_EventLog_writeEvent_String(env, clazz, tag, NULL); + ctx << "[NULL]"; + return ctx.write(); } - uint8_t buf[MAX_EVENT_PAYLOAD]; - const size_t max = sizeof(buf) - 1; // leave room for final newline - size_t pos = 2; // Save room for type tag & array count - jsize copied = 0, num = env->GetArrayLength(value); for (; copied < num && copied < 255; ++copied) { + if (ctx.status()) break; jobject item = env->GetObjectArrayElement(value, copied); - if (item == NULL || env->IsInstanceOf(item, gStringClass)) { - if (pos + 1 + sizeof(jint) > max) break; - const char *str = item != NULL ? env->GetStringUTFChars((jstring) item, NULL) : "NULL"; - jint len = strlen(str); - if (pos + 1 + sizeof(len) + len > max) len = max - pos - 1 - sizeof(len); - buf[pos++] = EVENT_TYPE_STRING; - memcpy(&buf[pos], &len, sizeof(len)); - memcpy(&buf[pos + sizeof(len)], str, len); - pos += sizeof(len) + len; - if (item != NULL) env->ReleaseStringUTFChars((jstring) item, str); + if (item == NULL) { + ctx << "NULL"; + } else if (env->IsInstanceOf(item, gStringClass)) { + const char *str = env->GetStringUTFChars((jstring) item, NULL); + ctx << str; + env->ReleaseStringUTFChars((jstring) item, str); } else if (env->IsInstanceOf(item, gIntegerClass)) { - jint intVal = env->GetIntField(item, gIntegerValueID); - if (pos + 1 + sizeof(intVal) > max) break; - buf[pos++] = EVENT_TYPE_INT; - memcpy(&buf[pos], &intVal, sizeof(intVal)); - pos += sizeof(intVal); + ctx << (int32_t)env->GetIntField(item, gIntegerValueID); } else if (env->IsInstanceOf(item, gLongClass)) { - jlong longVal = env->GetLongField(item, gLongValueID); - if (pos + 1 + sizeof(longVal) > max) break; - buf[pos++] = EVENT_TYPE_LONG; - memcpy(&buf[pos], &longVal, sizeof(longVal)); - pos += sizeof(longVal); + ctx << (int64_t)env->GetLongField(item, gLongValueID); } else if (env->IsInstanceOf(item, gFloatClass)) { - jfloat floatVal = env->GetFloatField(item, gFloatValueID); - if (pos + 1 + sizeof(floatVal) > max) break; - buf[pos++] = EVENT_TYPE_FLOAT; - memcpy(&buf[pos], &floatVal, sizeof(floatVal)); - pos += sizeof(floatVal); + ctx << (float)env->GetFloatField(item, gFloatValueID); } else { jniThrowException(env, "java/lang/IllegalArgumentException", @@ -156,11 +139,7 @@ static jint android_util_EventLog_writeEvent_Array(JNIEnv* env, jobject clazz, } env->DeleteLocalRef(item); } - - buf[0] = EVENT_TYPE_LIST; - buf[1] = copied; - buf[pos++] = '\n'; - return android_bWriteLog(tag, buf, pos); + return ctx.write(); } /* diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 5202a98bc030..ca0970850ef1 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -297,12 +297,6 @@ static bool MountEmulatedStorage(uid_t uid, jint mount_mode, 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 @@ static bool MountEmulatedStorage(uid_t uid, jint mount_mode, 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 33ba8860c3b8..7ec3ba760d5c 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -181,6 +181,10 @@ android:name="android.bluetooth.input.profile.action.VIRTUAL_UNPLUG_STATUS" /> <protected-broadcast android:name="android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED" /> + <protected-broadcast android:name="android.bluetooth.mapmce.profile.action.CONNECTION_STATE_CHANGED" /> + <protected-broadcast android:name="android.bluetooth.mapmce.profile.action.MESSAGE_RECEIVED" /> + <protected-broadcast android:name="android.bluetooth.mapmce.profile.action.MESSAGE_SENT_SUCCESSFULLY" /> + <protected-broadcast android:name="android.bluetooth.mapmce.profile.action.MESSAGE_DELIVERED_SUCCESSFULLY" /> <protected-broadcast android:name="com.android.bluetooth.BluetoothMapContentObserver.action.MESSAGE_SENT" /> <protected-broadcast @@ -188,6 +192,7 @@ <protected-broadcast android:name="android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED" /> <protected-broadcast android:name="android.bluetooth.pbap.intent.action.PBAP_STATE_CHANGED" /> + <protected-broadcast android:name="android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED" /> <protected-broadcast android:name="android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED" /> <protected-broadcast android:name="android.btopp.intent.action.INCOMING_FILE_NOTIFICATION" /> <protected-broadcast android:name="android.btopp.intent.action.USER_CONFIRMATION_TIMEOUT" /> diff --git a/core/res/res/values-mcc204-mnc04/config.xml b/core/res/res/values-mcc204-mnc04/config.xml index ddf0e9fa9a68..81e7a6f9a83f 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 065668c61efa..000000000000 --- 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 8d7fd6177500..378dee636b36 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 a8be8d34e19a..4c282845027c 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 6f2822bd2d98..27ddb5a69ce8 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 9e45d099b669..d7b38e844b5e 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 @@ include $(CLEAR_VARS) 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 8d9119275bc8..0cf3a84a3859 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 e7aca78a0720..02c25170bb74 100644 --- a/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java +++ b/core/tests/coretests/src/android/net/NetworkScorerAppManagerTest.java @@ -42,6 +42,8 @@ public class NetworkScorerAppManagerTest extends InstrumentationTestCase { @Mock private Context mMockContext; @Mock private PackageManager mMockPm; + private NetworkScorerAppManager mNetworkScorerAppManager; + @Override public void setUp() throws Exception { super.setUp(); @@ -54,6 +56,7 @@ public class NetworkScorerAppManagerTest extends InstrumentationTestCase { MockitoAnnotations.initMocks(this); Mockito.when(mMockContext.getPackageManager()).thenReturn(mMockPm); + mNetworkScorerAppManager = new NetworkScorerAppManager(mMockContext); } public void testGetAllValidScorers() throws Exception { @@ -81,7 +84,7 @@ public class NetworkScorerAppManagerTest extends InstrumentationTestCase { 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 e6d315844d72..0a32e4353e1c 100644 --- a/core/tests/coretests/src/android/provider/SettingsProviderTest.java +++ b/core/tests/coretests/src/android/provider/SettingsProviderTest.java @@ -349,6 +349,7 @@ public class SettingsProviderTest extends AndroidTestCase { 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 000000000000..fe5d8ca66b6a --- /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 76d521dde04a..9636ea72f47d 100644 --- a/libs/androidfw/Android.mk +++ b/libs/androidfw/Android.mk @@ -64,8 +64,9 @@ LOCAL_MODULE:= libandroidfw LOCAL_SRC_FILES:= $(deviceSources) LOCAL_C_INCLUDES := \ system/core/include -LOCAL_STATIC_LIBRARIES := libziparchive libbase LOCAL_SHARED_LIBRARIES := \ + libziparchive \ + libbase \ libbinder \ liblog \ libcutils \ diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index fd30c25bec41..e10db05e8557 100644 --- a/libs/androidfw/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -3166,7 +3166,7 @@ struct ResTable::Package { 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; diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 3c1c0bceba58..0d9ede063c79 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 @@ bool RenderThread::threadLoop() { "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 84ef9c2575f5..362890b52b61 100644 --- a/libs/hwui/tests/microbench/FrameBuilderBench.cpp +++ b/libs/hwui/tests/microbench/FrameBuilderBench.cpp @@ -113,7 +113,7 @@ static auto SCENES = { 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 @@ BENCHMARK(BM_FrameBuilder_defer_scene)->DenseRange(0, SCENES.size() - 1); 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 f90f1e24b1e8..fb91bbbbb321 100644 --- a/media/java/android/media/AmrInputStream.java +++ b/media/java/android/media/AmrInputStream.java @@ -18,45 +18,69 @@ package android.media; 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 @@ public final class AmrInputStream extends InputStream 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 @@ public final class AmrInputStream extends InputStream @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 */); + } + } + + // 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; + } } - - // encode it - mBufIn = GsmAmrEncoderEncode(mGae, mBuf, 0, mBuf, 0); } - - // 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 3355d420aa10..8640565bd91a 100644 --- a/media/jni/Android.mk +++ b/media/jni/Android.mk @@ -2,7 +2,6 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ - android_media_AmrInputStream.cpp \ android_media_ExifInterface.cpp \ android_media_ImageWriter.cpp \ android_media_ImageReader.cpp \ @@ -46,11 +45,9 @@ LOCAL_SHARED_LIBRARIES := \ libusbhost \ libexif \ libpiex \ - libstagefright_amrnb_common \ libandroidfw LOCAL_STATIC_LIBRARIES := \ - libstagefright_amrnbenc LOCAL_C_INCLUDES += \ external/libexif/ \ @@ -60,9 +57,6 @@ LOCAL_C_INCLUDES += \ 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 b56a3641ac9f..000000000000 --- 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 2fb1a3b9fbcf..8f14b79660be 100644 --- a/media/jni/android_media_MediaPlayer.cpp +++ b/media/jni/android_media_MediaPlayer.cpp @@ -1106,7 +1106,6 @@ extern int register_android_media_MediaScanner(JNIEnv *env); 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 @@ jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) 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/mca/filterfw/jni/jni_gl_environment.cpp b/media/mca/filterfw/jni/jni_gl_environment.cpp index 096120e5a26d..823e88ad64f4 100644 --- a/media/mca/filterfw/jni/jni_gl_environment.cpp +++ b/media/mca/filterfw/jni/jni_gl_environment.cpp @@ -63,7 +63,8 @@ class NativeWindowHandle : public WindowHandle { }; 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 b55bc5d0ef71..27b4cd2a4349 100644 --- a/media/mca/filterfw/jni/jni_gl_frame.cpp +++ b/media/mca/filterfw/jni/jni_gl_frame.cpp @@ -48,13 +48,11 @@ jboolean Java_android_filterfw_core_GLFrame_nativeAllocate(JNIEnv* env, 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 @@ jboolean Java_android_filterfw_core_GLFrame_nativeAllocateWithTexture(JNIEnv* en 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 @@ jboolean Java_android_filterfw_core_GLFrame_nativeAllocateWithFbo(JNIEnv* env, 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 @@ jboolean Java_android_filterfw_core_GLFrame_nativeAllocateExternal(JNIEnv* env, 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 c8f235271c43..1a11a193e0d5 100644 --- a/media/mca/filterfw/jni/jni_native_frame.cpp +++ b/media/mca/filterfw/jni/jni_native_frame.cpp @@ -35,7 +35,8 @@ typedef union { 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 b30b769b373e..14246078225e 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::NativeFrame; 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 19f43cd8a7e2..98be04cbad16 100644 --- a/media/mca/filterfw/jni/jni_shader_program.cpp +++ b/media/mca/filterfw/jni/jni_shader_program.cpp @@ -46,21 +46,14 @@ jboolean Java_android_filterfw_core_ShaderProgram_allocate(JNIEnv* env, // 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 11c087170106..803ed29bdf33 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 @@ bool WrapObjectInJava(T* c_object, JNIEnv* env, jobject j_object, bool owns) { 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 caae93827d11..d0439fe7a5d1 100644 --- a/media/mca/filterfw/jni/jni_vertex_frame.cpp +++ b/media/mca/filterfw/jni/jni_vertex_frame.cpp @@ -24,7 +24,8 @@ using android::filterfw::VertexFrame; 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/packages/Osu/src/com/android/hotspot2/WifiNetworkAdapter.java b/packages/Osu/src/com/android/hotspot2/WifiNetworkAdapter.java index 518e64e1eb90..63b190332de1 100644 --- a/packages/Osu/src/com/android/hotspot2/WifiNetworkAdapter.java +++ b/packages/Osu/src/com/android/hotspot2/WifiNetworkAdapter.java @@ -134,8 +134,8 @@ public class WifiNetworkAdapter { 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 9608daad70e9..24ede164fbdd 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 @@ public final class A2dpProfile implements LocalBluetoothProfile { 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/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserTracker.java index dd8075057e17..005206fcd14c 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.Context; 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 ebfa0183f8b3..fa143467b1cf 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 @@ public class StatusBarWindowView extends FrameLayout { 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 70f2fdcfa8d3..7afdbcb2e277 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 @@ public class TunerFragment extends PreferenceFragment { 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 2a5b1946b90d..5b365983f07c 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -58,6 +58,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; 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 @@ class BluetoothManagerService extends IBluetoothManager.Stub { 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 @@ class BluetoothManagerService extends IBluetoothManager.Stub { 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 BluetoothManagerService extends IBluetoothManager.Stub { 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 @@ class BluetoothManagerService extends IBluetoothManager.Stub { 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 @@ class BluetoothManagerService extends IBluetoothManager.Stub { 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 @@ class BluetoothManagerService extends IBluetoothManager.Stub { // 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 @@ class BluetoothManagerService extends IBluetoothManager.Stub { 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 @@ class BluetoothManagerService extends IBluetoothManager.Stub { quietMode ? 1 : 0, 0)); } - private void recoverBluetoothServiceFromError() { + private void recoverBluetoothServiceFromError(boolean clearBle) { Slog.e(TAG,"recoverBluetoothServiceFromError"); try { mBluetoothLock.readLock().lock(); @@ -1875,6 +1867,10 @@ class BluetoothManagerService extends IBluetoothManager.Stub { 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 83d6344c95b0..756cb301fefa 100644 --- a/services/core/java/com/android/server/NetworkScoreService.java +++ b/services/core/java/com/android/server/NetworkScoreService.java @@ -41,6 +41,7 @@ import android.util.Log; 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 @@ public class NetworkScoreService extends INetworkScoreService.Stub { 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 @@ public class NetworkScoreService extends INetworkScoreService.Stub { + ", 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 class NetworkScoreService extends INetworkScoreService.Stub { } 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 @@ public class NetworkScoreService extends INetworkScoreService.Stub { 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 @@ public class NetworkScoreService extends INetworkScoreService.Stub { 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 @@ public class NetworkScoreService extends INetworkScoreService.Stub { private void bindToScoringServiceIfNeeded() { if (DBG) Log.d(TAG, "bindToScoringServiceIfNeeded"); - NetworkScorerAppData scorerData = NetworkScorerAppManager.getActiveScorer(mContext); + NetworkScorerAppData scorerData = mNetworkScorerAppManager.getActiveScorer(); bindToScoringServiceIfNeeded(scorerData); } @@ -257,7 +265,7 @@ public class NetworkScoreService extends INetworkScoreService.Stub { @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 class NetworkScoreService extends INetworkScoreService.Stub { 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 class NetworkScoreService extends INetworkScoreService.Stub { 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 @@ public class NetworkScoreService extends INetworkScoreService.Stub { // 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 @@ public class NetworkScoreService extends INetworkScoreService.Stub { @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 2932a1ac79a4..62f4f19a6028 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -1381,7 +1381,6 @@ class TelephonyRegistry extends ITelephonyRegistry.Stub { } 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/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 2d534171b94d..8bd729b4e669 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -75,6 +75,7 @@ import android.util.EventLog; import android.util.Slog; import android.util.SparseArray; import android.util.TimeUtils; +import android.webkit.WebViewZygote; public final class ActiveServices { private static final String TAG = TAG_WITH_CLASS_NAME ? "ActiveServices" : TAG_AM; @@ -1689,6 +1690,7 @@ public final class ActiveServices { final boolean isolated = (r.serviceInfo.flags&ServiceInfo.FLAG_ISOLATED_PROCESS) != 0; final String procName = r.processName; + String hostingType = "service"; ProcessRecord app; if (!isolated) { @@ -1717,13 +1719,17 @@ public final class ActiveServices { // in the service any current isolated process it is running in or // waiting to have come up. app = r.isolatedProc; + if (WebViewZygote.isMultiprocessEnabled() + && r.serviceInfo.packageName.equals(WebViewZygote.getPackageName())) { + hostingType = "webview_service"; + } } // Not running -- get it started, and enqueue this service record // to be executed when the app comes up. if (app == null && !permissionsReviewRequired) { if ((app=mAm.startProcessLocked(procName, r.appInfo, true, intentFlags, - "service", r.name, false, isolated, false)) == null) { + hostingType, r.name, false, isolated, false)) == null) { String msg = "Unable to launch app " + r.appInfo.packageName + "/" + r.appInfo.uid + " for service " diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index b59f4686998e..f6ed0a3f8f78 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 @@ public final class ActivityManagerService extends ActivityManagerNative @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); + } } /** @@ -3745,10 +3747,18 @@ public final class ActivityManagerService extends ActivityManagerNative Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " + app.processName); checkTime(startTime, "startProcess: asking zygote to start proc"); - Process.ProcessStartResult startResult = Process.start(entryPoint, - app.processName, uid, uid, gids, debugFlags, mountExternal, - app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet, - app.info.dataDir, entryPointArgs); + Process.ProcessStartResult startResult; + if (hostingType.equals("webview_service")) { + startResult = Process.startWebView(entryPoint, + app.processName, uid, uid, gids, debugFlags, mountExternal, + app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet, + app.info.dataDir, entryPointArgs); + } else { + startResult = Process.start(entryPoint, + app.processName, uid, uid, gids, debugFlags, mountExternal, + app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet, + app.info.dataDir, entryPointArgs); + } checkTime(startTime, "startProcess: returned from zygote!"); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); @@ -6351,13 +6361,18 @@ public final class ActivityManagerService extends ActivityManagerNative 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 +16781,18 @@ public final class ActivityManagerService extends ActivityManagerNative 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 ba799f2ca3d1..96a804b753f9 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 @@ class AppErrors { * @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 @@ class AppErrors { * 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 @@ class AppErrors { 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/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java index e888faaac075..cec4141a41f6 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 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering } } 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 @@ public class Tethering extends BaseNetworkObserver implements IControlsTethering 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 1ef4a9fa5849..acdcc72c7f71 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 @@ class PackageDexOptimizer { 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 @@ class PackageDexOptimizer { 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 dfd6dfe1c775..99a6979d3436 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 @@ final class Settings { VersionInfo ver = mVersion.get(volumeUuid); if (ver == null) { ver = new VersionInfo(); - ver.forceCurrent(); mVersion.put(volumeUuid, ver); } return ver; @@ -2795,8 +2794,8 @@ final class Settings { "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/jni/Android.mk b/services/core/jni/Android.mk index ac55a7eec21c..0dbfa5674b5d 100644 --- a/services/core/jni/Android.mk +++ b/services/core/jni/Android.mk @@ -52,6 +52,7 @@ LOCAL_C_INCLUDES += \ LOCAL_SHARED_LIBRARIES += \ libandroid_runtime \ libandroidfw \ + libbase \ libbinder \ libcutils \ liblog \ diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index df9242dc0aa1..43b8fa51a80e 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 @@ public class UsbDeviceManager { 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 @@ public class UsbDeviceManager { if (functions != null) { mAccessoryModeRequestTime = SystemClock.elapsedRealtime(); - setCurrentFunctions(functions); + setCurrentFunctions(functions, false); } } @@ -335,14 +334,22 @@ public class UsbDeviceManager { // 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 @@ public class UsbDeviceManager { 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 @@ public class UsbDeviceManager { 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; + + // Persist the adb setting + String newFunction = applyAdbFunction(SystemProperties.get( + USB_PERSISTENT_CONFIG_PROPERTY, UsbManager.USB_FUNCTION_NONE)); + SystemProperties.set(USB_PERSISTENT_CONFIG_PROPERTY, newFunction); - // 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); + // 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 @@ public class UsbDeviceManager { /** * 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 @@ public class UsbDeviceManager { } 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 @@ public class UsbDeviceManager { // 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 @@ public class UsbDeviceManager { 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 @@ public class UsbDeviceManager { 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 @@ public class UsbDeviceManager { updateCurrentAccessory(); } else if (!mConnected) { // restore defaults when USB is disconnected - setEnabledFunctions(null, false); + setEnabledFunctions(null, false, false); } if (mBootCompleted) { updateUsbStateBroadcastIfNeeded(); @@ -730,13 +741,10 @@ public class UsbDeviceManager { 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 @@ public class UsbDeviceManager { 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 @@ public class UsbDeviceManager { 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 d6dbe90584f5..daccc00ad80c 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 @@ public class UsbService extends IUsbManager.Stub { } @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 @@ public class UsbService extends IUsbManager.Stub { } if (mDeviceManager != null) { - mDeviceManager.setCurrentFunctions(function); + mDeviceManager.setCurrentFunctions(function, usbDataUnlocked); } else { throw new IllegalStateException("USB device mode not supported"); } @@ -320,12 +320,6 @@ public class UsbService extends IUsbManager.Stub { } @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 66d704b2fe81..f177a419e4c6 100644 --- a/telecomm/java/android/telecom/ConnectionService.java +++ b/telecomm/java/android/telecom/ConnectionService.java @@ -515,9 +515,12 @@ public abstract class ConnectionService extends Service { 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 @@ public abstract class ConnectionService extends Service { isIncoming, isUnknown); } - }); + }.prepare()); } else { createConnection( connectionManagerPhoneAccount, @@ -1378,9 +1381,9 @@ public abstract class ConnectionService extends Service { 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 @@ public abstract class ConnectionService extends Service { 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/Log.java b/telecomm/java/android/telecom/Log.java index 446bbbb69996..ced6627e8d34 100644 --- a/telecomm/java/android/telecom/Log.java +++ b/telecomm/java/android/telecom/Log.java @@ -48,13 +48,13 @@ public class Log { // 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 class Log { 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 56c52bfde3b3..6e810538cc62 100644 --- a/telecomm/java/android/telecom/Logging/Runnable.java +++ b/telecomm/java/android/telecom/Logging/Runnable.java @@ -27,7 +27,7 @@ public abstract class Runnable { 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 abstract class Runnable { } }; - 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 @@ public abstract class Runnable { */ 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 3a7b8c09203a..c45bd6b04145 100644 --- a/telecomm/java/android/telecom/Logging/Session.java +++ b/telecomm/java/android/telecom/Logging/Session.java @@ -19,6 +19,7 @@ package android.telecom.Logging; 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 com.android.internal.annotations.VisibleForTesting; 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 class Session { 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 @@ public class Session { @Override public void writeToParcel(Parcel destination, int flags) { destination.writeString(sessionId); - destination.writeString(shortMethodName); + destination.writeString(methodPath); } } @@ -226,7 +234,15 @@ public class Session { 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 @@ public class Session { } // 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 @@ public class Session { // 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 @@ public class Session { 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 8ced7f8181c5..949f7b7a89ae 100644 --- a/telecomm/java/android/telecom/Logging/SessionManager.java +++ b/telecomm/java/android/telecom/Logging/SessionManager.java @@ -177,8 +177,9 @@ public class SessionManager { } // 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 @@ public class SessionManager { // 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 dbc2b0c9f850..615ae49d30fb 100644 --- a/telecomm/java/android/telecom/PhoneAccount.java +++ b/telecomm/java/android/telecom/PhoneAccount.java @@ -114,7 +114,10 @@ public final class PhoneAccount implements Parcelable { 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 final class PhoneAccount implements Parcelable { 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 @@ public final class PhoneAccount implements Parcelable { */ 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/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index d064cd9c79de..a1316a98fd46 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -208,6 +208,14 @@ public class CarrierConfigManager { 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 @@ public class CarrierConfigManager { 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 addf7ef025aa..4137853e79aa 100644 --- a/telephony/java/android/telephony/CellSignalStrengthGsm.java +++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java @@ -141,6 +141,16 @@ public final class CellSignalStrengthGsm extends CellSignalStrength implements P } /** + * 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 3c0a8d6f8a38..0d07a40822d4 100644 --- a/telephony/java/android/telephony/CellSignalStrengthLte.java +++ b/telephony/java/android/telephony/CellSignalStrengthLte.java @@ -168,20 +168,34 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P } /** - * @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 @@ public final class CellSignalStrengthLte extends CellSignalStrength implements P } /** - * 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 bb2b4478433d..32f487bb5dbe 100644 --- a/telephony/java/android/telephony/PhoneStateListener.java +++ b/telephony/java/android/telephony/PhoneStateListener.java @@ -233,7 +233,7 @@ public class PhoneStateListener { * @hide */ /** @hide */ - protected int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + protected Integer mSubId; private final Handler mHandler; @@ -242,7 +242,7 @@ public class PhoneStateListener { * 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 @@ public class PhoneStateListener { * @hide */ public PhoneStateListener(Looper looper) { - this(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, looper); + this(null, looper); } /** @@ -260,7 +260,7 @@ public class PhoneStateListener { * 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 @@ public class PhoneStateListener { * 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/ServiceState.java b/telephony/java/android/telephony/ServiceState.java index ea87fa353169..fe333002fcf4 100644 --- a/telephony/java/android/telephony/ServiceState.java +++ b/telephony/java/android/telephony/ServiceState.java @@ -20,6 +20,7 @@ import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.telephony.Rlog; +import android.text.TextUtils; /** * Contains phone state and service related information. @@ -579,6 +580,24 @@ public class ServiceState implements Parcelable { } /** + * Get current registered operator name in long alphanumeric format if + * available or short otherwise. + * + * @see #getOperatorAlphaLong + * @see #getOperatorAlphaShort + * + * @return name of operator, null if unregistered or unknown + * @hide + */ + public String getOperatorAlpha() { + if (TextUtils.isEmpty(mVoiceOperatorAlphaLong)) { + return mVoiceOperatorAlphaShort; + } + + return mVoiceOperatorAlphaLong; + } + + /** * Get current registered operator numeric id. * * In GSM/UMTS, numeric format is 3 digit country code plus 2 or 3 digit diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java index e87cba137483..c484fd31c467 100644 --- a/telephony/java/android/telephony/SignalStrength.java +++ b/telephony/java/android/telephony/SignalStrength.java @@ -20,6 +20,7 @@ import android.os.Bundle; 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 @@ public class SignalStrength implements Parcelable { //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 @@ public class SignalStrength implements Parcelable { */ 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 79130e4b16ec..ca17c066e3ef 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -255,6 +255,22 @@ public class TelephonyManager { 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 class TelephonyManager { 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"; @@ -2783,6 +2806,12 @@ public class TelephonyManager { 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 @@ public class TelephonyManager { 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 @@ public class TelephonyManager { } } - /** @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 @@ public class TelephonyManager { 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 @@ public class TelephonyManager { } /** + * 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 541dbb53c3e6..000000000000 --- 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 f48ddee6855f..000000000000 --- 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 c846191fdd54..000000000000 --- 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 9101900892c1..000000000000 --- 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 c4d80d0802ae..000000000000 --- 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 6cb0e19d65dc..000000000000 --- 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 8d9341d23949..000000000000 --- 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 09eec7f2e903..000000000000 --- 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 3ac9d9eeeda2..000000000000 --- 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 32846283e6cc..000000000000 --- 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 f08fa7013299..000000000000 --- 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 723f2ae5151c..000000000000 --- 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 8a81d0b684db..465647606e52 100644 --- a/tools/layoutlib/bridge/tests/Android.mk +++ b/tools/layoutlib/bridge/tests/Android.mk @@ -28,7 +28,7 @@ LOCAL_JAVA_LIBRARIES := layoutlib \ 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 dafb9c6f9402..61e381d48b16 100644 --- a/tools/layoutlib/create/tests/Android.mk +++ b/tools/layoutlib/create/tests/Android.mk @@ -23,7 +23,7 @@ LOCAL_JAVA_RESOURCE_DIRS := data mock_data 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 ce877b3696c2..769db6bd3ead 100644 --- a/tools/preload2/Android.mk +++ b/tools/preload2/Android.mk @@ -5,14 +5,14 @@ include $(CLEAR_VARS) 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 36dbc1c775b1..322b62fda071 100644 --- a/tools/preload2/preload-tool +++ b/tools/preload2/preload-tool @@ -34,4 +34,4 @@ PROG_NAME="$(follow_links)" 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 ca5b0e005a1d..c42a19b58811 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.actions.ShowDataAction; 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 @@ public class Main { 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 @@ public class Main { + "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 @@ public class Main { } } - ui = new UI(clientListModel, dataTableModel, actions); - ui.setVisible(true); + ui.prepare(clientListModel, dataTableModel, actions); + } + + /** + * @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 UI getUI() { + public static IUI getUI() { return top.ui; } @@ -176,6 +258,7 @@ public class Main { 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 fbf83d2e2339..5787d8507230 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 @@ public abstract class AbstractThreadedAction extends AbstractAction implements R @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 b524716fc2cb..3a7f7f74d755 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.TreeSet; 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 @@ public class ComputeThresholdAction extends AbstractAction implements Runnable { return; } - new Thread(this).start(); + super.actionPerformed(e); } @Override @@ -92,10 +91,8 @@ public class ComputeThresholdAction extends AbstractAction implements Runnable { 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 cb8b3df75b18..848a56826788 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 @@ package com.android.preload.actions; 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 class ExportAction extends AbstractAction implements Runnable { 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 5c1976580f94..bfeeb836fd45 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 java.util.Collection; 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 class ImportAction extends AbstractAction implements Runnable { 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 385e8577b1c8..29464fc7abdf 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 @@ public class RunMonkeyAction extends AbstractAction implements DeviceSpecific { 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 000000000000..9371463e9a79 --- /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 000000000000..dc6a4f389b10 --- /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 index 47174ddd0e07..cab3744ad74c 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.ListModel; 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 @@ public class UI extends JFrame { // 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 @@ public class UI extends JFrame { 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 @@ public class UI extends JFrame { }); } + @Override public void updateWaitDialog(String s) { if (currentWaitDialog != null) { ((JLabel) currentWaitDialog.getContentPane().getComponent(0)).setText(s); @@ -124,6 +141,7 @@ public class UI extends JFrame { } } + @Override public void hideWaitDialog() { if (currentWaitDialog != null) { currentWaitDialog.setVisible(false); @@ -131,6 +149,7 @@ public class UI extends JFrame { } } + @Override public void showMessageDialog(String s) { // Hide the wait dialog... if (currentWaitDialog != null) { @@ -147,6 +166,7 @@ public class UI extends JFrame { } } + @Override public boolean showConfirmDialog(String title, String message) { // Hide the wait dialog... if (currentWaitDialog != null) { @@ -164,6 +184,7 @@ public class UI extends JFrame { } } + @Override public String showInputDialog(String message) { // Hide the wait dialog... if (currentWaitDialog != null) { @@ -180,6 +201,7 @@ public class UI extends JFrame { } } + @Override @SuppressWarnings("unchecked") public <T> T showChoiceDialog(String title, String message, T[] choices) { // Hide the wait dialog... @@ -203,6 +225,7 @@ public class UI extends JFrame { } } + @Override public File showSaveDialog() { // Hide the wait dialog... if (currentWaitDialog != null) { @@ -228,6 +251,7 @@ public class UI extends JFrame { } } + @Override public File[] showOpenDialog(boolean multi) { // Hide the wait dialog... if (currentWaitDialog != null) { diff --git a/wifi/java/android/net/wifi/EAPConstants.java b/wifi/java/android/net/wifi/EAPConstants.java new file mode 100644 index 000000000000..b5f7c946ff05 --- /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 a91c9491c60a..9e897bff1122 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 @@ interface IWifiManager 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 @@ interface IWifiManager WifiInfo getConnectionInfo(); - boolean setWifiEnabled(boolean enable); + boolean setWifiEnabled(String packageName, boolean enable); int getWifiEnabledState(); @@ -125,8 +127,6 @@ interface IWifiManager 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 465addfc1469..da8713555889 100644 --- a/wifi/java/android/net/wifi/ScanResult.java +++ b/wifi/java/android/net/wifi/ScanResult.java @@ -66,6 +66,93 @@ public class ScanResult implements Parcelable { * 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 24cd275fc254..82d41e3dc7ae 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -685,12 +685,22 @@ public class WifiConfiguration implements Parcelable { /** * @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 @@ public class WifiConfiguration implements Parcelable { didSelfAdd = false; ephemeral = false; meteredHint = false; + meteredOverride = false; useExternalScores = false; validatedInternetAccess = false; mIpConfiguration = new IpConfiguration(); @@ -1470,9 +1481,11 @@ public class WifiConfiguration implements Parcelable { 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 @@ public class WifiConfiguration implements Parcelable { 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 @@ public class WifiConfiguration implements Parcelable { 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 @@ public class WifiConfiguration implements Parcelable { 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 8d5efba8620e..e48f7bdb27ee 100644 --- a/wifi/java/android/net/wifi/WifiInfo.java +++ b/wifi/java/android/net/wifi/WifiInfo.java @@ -22,6 +22,7 @@ import android.net.NetworkInfo.DetailedState; 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 class WifiInfo implements Parcelable { */ 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 class WifiInfo implements Parcelable { 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 @@ public class WifiInfo implements Parcelable { 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 @@ public class WifiInfo implements Parcelable { txSuccess = txgood; rxSuccess = rxgood; txRetries = txretries; + mLastPacketCountUpdateTimeStamp = timeStamp; } else { txBad = 0; txSuccess = 0; @@ -200,6 +220,7 @@ public class WifiInfo implements Parcelable { txSuccessRate = 0; rxSuccessRate = 0; txRetriesRate = 0; + mLastPacketCountUpdateTimeStamp = RESET_TIME_STAMP; } } @@ -243,6 +264,7 @@ public class WifiInfo implements Parcelable { mRssi = INVALID_RSSI; mLinkSpeed = -1; mFrequency = -1; + mLastPacketCountUpdateTimeStamp = RESET_TIME_STAMP; } /** @hide */ @@ -268,6 +290,7 @@ public class WifiInfo implements Parcelable { badRssiCount = 0; linkStuckCount = 0; score = 0; + mLastPacketCountUpdateTimeStamp = RESET_TIME_STAMP; } /** @@ -295,6 +318,8 @@ public class WifiInfo implements Parcelable { 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 1647c438e375..a42bef465b75 100644 --- a/wifi/java/android/net/wifi/WifiManager.java +++ b/wifi/java/android/net/wifi/WifiManager.java @@ -19,12 +19,14 @@ package android.net.wifi; 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 class WifiManager { 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 @@ public class WifiManager { } /** - * 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(); } @@ -1409,7 +1453,7 @@ public class WifiManager { */ 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 @@ public class WifiManager { } /** - * 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/aware/ConfigRequest.java b/wifi/java/android/net/wifi/aware/ConfigRequest.java index 4aacbae6272f..4b21b15d4994 100644 --- a/wifi/java/android/net/wifi/aware/ConfigRequest.java +++ b/wifi/java/android/net/wifi/aware/ConfigRequest.java @@ -22,7 +22,7 @@ import android.os.Parcelable; /** * Defines a request object to configure a Wi-Fi Aware network. Built using * {@link ConfigRequest.Builder}. Configuration is requested using - * {@link WifiAwareManager#attach(android.os.Handler, WifiAwareAttachCallback)}. + * {@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. diff --git a/wifi/java/android/net/wifi/aware/LvBufferUtils.java b/wifi/java/android/net/wifi/aware/LvBufferUtils.java deleted file mode 100644 index 3265243e28d6..000000000000 --- a/wifi/java/android/net/wifi/aware/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.aware; - -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_AWARE_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/aware/PublishConfig.java b/wifi/java/android/net/wifi/aware/PublishConfig.java index 5d3ad0582f9c..3925bd71ab9e 100644 --- a/wifi/java/android/net/wifi/aware/PublishConfig.java +++ b/wifi/java/android/net/wifi/aware/PublishConfig.java @@ -28,13 +28,13 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.nio.charset.StandardCharsets; import java.util.Arrays; +import java.util.List; /** * Defines the configuration of a Aware publish session. Built using * {@link PublishConfig.Builder}. A publish session is created using - * {@link WifiAwareSession#publish(android.os.Handler, PublishConfig, - * WifiAwareDiscoverySessionCallback)} - * or updated using + * {@link WifiAwareSession#publish(PublishConfig, WifiAwareDiscoverySessionCallback, + * android.os.Handler)} or updated using * {@link WifiAwarePublishDiscoverySession#updatePublish(PublishConfig)}. * * @hide PROPOSED_AWARE_API @@ -85,7 +85,8 @@ public final class PublishConfig implements Parcelable { /** @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; @@ -100,9 +101,9 @@ public final class PublishConfig implements Parcelable { 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 + "]"; } @@ -187,7 +188,7 @@ public final class PublishConfig implements Parcelable { 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"); } @@ -282,18 +283,17 @@ public final class PublishConfig implements Parcelable { * 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; } diff --git a/wifi/java/android/net/wifi/aware/SubscribeConfig.java b/wifi/java/android/net/wifi/aware/SubscribeConfig.java index 2a6cc93146b4..bf35445ff88a 100644 --- a/wifi/java/android/net/wifi/aware/SubscribeConfig.java +++ b/wifi/java/android/net/wifi/aware/SubscribeConfig.java @@ -28,13 +28,13 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.nio.charset.StandardCharsets; import java.util.Arrays; +import java.util.List; /** * Defines the configuration of a Aware subscribe session. Built using * {@link SubscribeConfig.Builder}. Subscribe is done using - * {@link WifiAwareSession#subscribe(android.os.Handler, SubscribeConfig, - * WifiAwareDiscoverySessionCallback)} - * or + * {@link WifiAwareSession#subscribe(SubscribeConfig, WifiAwareDiscoverySessionCallback, + * android.os.Handler)} or * {@link WifiAwareSubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}. * * @hide PROPOSED_AWARE_API @@ -107,7 +107,8 @@ public final class SubscribeConfig implements Parcelable { /** @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; @@ -124,10 +125,11 @@ public final class SubscribeConfig implements Parcelable { 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 @@ -214,7 +216,7 @@ public final class SubscribeConfig implements Parcelable { 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"); } @@ -314,18 +316,17 @@ public final class SubscribeConfig implements Parcelable { * 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; } @@ -403,8 +404,8 @@ public final class SubscribeConfig implements Parcelable { * 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 WifiAwareDiscoverySessionCallback#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). * diff --git a/wifi/java/android/net/wifi/aware/TlvBufferUtils.java b/wifi/java/android/net/wifi/aware/TlvBufferUtils.java index 56c90692f1b8..29f10e932e48 100644 --- a/wifi/java/android/net/wifi/aware/TlvBufferUtils.java +++ b/wifi/java/android/net/wifi/aware/TlvBufferUtils.java @@ -22,8 +22,10 @@ import libcore.io.Memory; 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 @@ import java.util.NoSuchElementException; * 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_AWARE_API + * @hide */ public class TlvBufferUtils { private TlvBufferUtils() { @@ -111,6 +113,31 @@ public class TlvBufferUtils { } /** + * 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 @@ public class TlvBufferUtils { this.length = length; this.refArray = refArray; this.offset = offset; + + if (offset + length > refArray.length) { + throw new BufferOverflowException(); + } } /** @@ -393,7 +424,7 @@ public class TlvBufferUtils { * @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 @@ public class TlvBufferUtils { } /** + * 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 index 2cace6143f4f..1e8dbd945b6e 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareAttachCallback.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareAttachCallback.java @@ -18,7 +18,7 @@ package android.net.wifi.aware; /** * Base class for Aware attach callbacks. Should be extended by applications and set when calling - * {@link WifiAwareManager#attach(android.os.Handler, WifiAwareAttachCallback)}. These are callbacks + * {@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}. * @@ -27,7 +27,7 @@ package android.net.wifi.aware; public class WifiAwareAttachCallback { /** * Called when Aware attach operation - * {@link WifiAwareManager#attach(android.os.Handler, WifiAwareAttachCallback)} + * {@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. @@ -39,7 +39,7 @@ public class WifiAwareAttachCallback { /** * Called when Aware attach operation - * {@link WifiAwareManager#attach(android.os.Handler, WifiAwareAttachCallback)} failed. + * {@link WifiAwareManager#attach(WifiAwareAttachCallback, android.os.Handler)} failed. */ public void onAttachFailed() { /* empty */ diff --git a/wifi/java/android/net/wifi/aware/WifiAwareCharacteristics.java b/wifi/java/android/net/wifi/aware/WifiAwareCharacteristics.java index 6232c14e0f18..95d128d8e514 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareCharacteristics.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareCharacteristics.java @@ -58,7 +58,8 @@ public class WifiAwareCharacteristics implements Parcelable { * message exchange. Restricts the parameters of the * {@link PublishConfig.Builder#setServiceSpecificInfo(byte[])}, * {@link SubscribeConfig.Builder#setServiceSpecificInfo(byte[])}, and - * {@link WifiAwareDiscoveryBaseSession#sendMessage(Object, int, byte[])} variants. + * {@link WifiAwareDiscoveryBaseSession#sendMessage(WifiAwareManager.PeerHandle, int, byte[])} + * variants. * * @return A positive integer, maximum length of byte array for Aware messaging. */ @@ -68,8 +69,9 @@ public class WifiAwareCharacteristics implements Parcelable { /** * 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(byte[])} and - * {@link SubscribeConfig.Builder#setMatchFilter(byte[])}. + * 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 Aware discovery match filter. */ diff --git a/wifi/java/android/net/wifi/aware/WifiAwareDiscoveryBaseSession.java b/wifi/java/android/net/wifi/aware/WifiAwareDiscoveryBaseSession.java index 07f752396d3d..451d8a594cc5 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareDiscoveryBaseSession.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareDiscoveryBaseSession.java @@ -32,10 +32,10 @@ import java.lang.ref.WeakReference; * {@link WifiAwarePublishDiscoverySession} and {@link WifiAwareSubscribeDiscoverySession}. 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>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(int, Object, byte[])}. + * {@link #createNetworkSpecifier(WifiAwareManager.PeerHandle, byte[])}. * </ul> * The {@link #destroy()} method must be called to destroy discovery sessions once they are * no longer needed. @@ -62,7 +62,7 @@ public class WifiAwareDiscoveryBaseSession { /** * Return the maximum permitted retry count when sending messages using - * {@link #sendMessage(Object, int, byte[], int)}. + * {@link #sendMessage(WifiAwareManager.PeerHandle, int, byte[], int)}. * * @return Maximum retry count when sending messages. */ @@ -139,21 +139,24 @@ public class WifiAwareDiscoveryBaseSession { /** * 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(Object, byte[], byte[])} event. + * {@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#onMessageSent(int)}, or transmission failed - * (possibly after several retries) - + * {@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(Object, byte[])}. + * {@link WifiAwareDiscoverySessionCallback#onMessageReceived(WifiAwareManager.PeerHandle, + * byte[])}. * * @param peerHandle The peer's handle for the message. Must be a result of an - * {@link WifiAwareDiscoverySessionCallback#onServiceDiscovered(Object, byte[], byte[])} - * or - * {@link WifiAwareDiscoverySessionCallback#onMessageReceived(Object, byte[])} events. + * {@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 @@ -164,8 +167,8 @@ public class WifiAwareDiscoveryBaseSession { * (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) { + 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; @@ -183,37 +186,43 @@ public class WifiAwareDiscoveryBaseSession { /** * 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(Object, byte[], byte[])} event. + * {@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#onMessageSent(int)}, or transmission failed - * (possibly after several retries) - + * {@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(Object, byte[])}. - * Equivalent to {@link #sendMessage(Object, int, byte[], int)} with a {@code retryCount} of - * 0. + * 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(Object, byte[], byte[])} - * or - * {@link WifiAwareDiscoverySessionCallback#onMessageReceived(Object, byte[])} events. + * {@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 Object peerHandle, int messageId, @Nullable byte[] message) { + 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(Object, byte[], byte[])} or - * {@link WifiAwareDiscoverySessionCallback#onMessageReceived(Object, byte[])} operation - can + * {@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 @@ -251,16 +260,17 @@ public class WifiAwareDiscoveryBaseSession { * 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 role The role of this device: - * {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_INITIATOR} or - * {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_RESPONDER} * @param peerHandle The peer's handle obtained through - * {@link WifiAwareDiscoverySessionCallback#onServiceDiscovered(Object, byte[], byte[])} or - * {@link WifiAwareDiscoverySessionCallback#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. + * {@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 @@ -273,8 +283,8 @@ public class WifiAwareDiscoveryBaseSession { * android.net.ConnectivityManager.NetworkCallback)} * [or other varieties of that API]. */ - public String createNetworkSpecifier(@WifiAwareManager.DataPathRole int role, - @Nullable Object peerHandle, @Nullable byte[] token) { + public String createNetworkSpecifier(@Nullable WifiAwareManager.PeerHandle peerHandle, + @Nullable byte[] token) { if (mTerminated) { Log.w(TAG, "createNetworkSpecifier: called on terminated session"); return null; @@ -285,6 +295,10 @@ public class WifiAwareDiscoveryBaseSession { 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 index 9dfa24fd7232..fdf0d013334d 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareDiscoverySessionCallback.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareDiscoverySessionCallback.java @@ -21,16 +21,16 @@ 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(android.os.Handler, PublishConfig, - * WifiAwareDiscoverySessionCallback)} - * or - * {@link WifiAwareSession#subscribe(android.os.Handler, SubscribeConfig, - * WifiAwareDiscoverySessionCallback)} . + * {@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. * @@ -62,9 +62,8 @@ public class WifiAwareDiscoverySessionCallback { /** * Called when a publish operation is started successfully in response to a - * {@link WifiAwareSession#publish(android.os.Handler, PublishConfig, - * WifiAwareDiscoverySessionCallback)} - * operation. + * {@link WifiAwareSession#publish(PublishConfig, WifiAwareDiscoverySessionCallback, + * android.os.Handler)} operation. * * @param session The {@link WifiAwarePublishDiscoverySession} used to control the * discovery session. @@ -75,9 +74,8 @@ public class WifiAwareDiscoverySessionCallback { /** * Called when a subscribe operation is started successfully in response to a - * {@link WifiAwareSession#subscribe(android.os.Handler, SubscribeConfig, - * WifiAwareDiscoverySessionCallback)} - * operation. + * {@link WifiAwareSession#subscribe(SubscribeConfig, WifiAwareDiscoverySessionCallback, + * android.os.Handler)} operation. * * @param session The {@link WifiAwareSubscribeDiscoverySession} used to control the * discovery session. @@ -98,12 +96,10 @@ public class WifiAwareDiscoverySessionCallback { /** * Called when a publish or subscribe discovery session cannot be created: - * {@link WifiAwareSession#publish(android.os.Handler, PublishConfig, - * WifiAwareDiscoverySessionCallback)} - * or - * {@link WifiAwareSession#subscribe(android.os.Handler, SubscribeConfig, - * WifiAwareDiscoverySessionCallback)}, - * or when a configuration update fails: + * {@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> @@ -135,16 +131,16 @@ public class WifiAwareDiscoverySessionCallback { * @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. + * @param matchFilter The filter which resulted in this service discovery. */ - public void onServiceDiscovered(Object peerHandle, byte[] serviceSpecificInfo, - byte[] matchFilter) { + public void onServiceDiscovered(WifiAwareManager.PeerHandle peerHandle, + byte[] serviceSpecificInfo, List<byte[]> matchFilter) { /* empty */ } /** - * Called in response to {@link WifiAwareDiscoveryBaseSession#sendMessage(Object, int, byte[])} + * 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> @@ -154,18 +150,18 @@ public class WifiAwareDiscoverySessionCallback { * * @param messageId The arbitrary message ID specified when sending the message. */ - public void onMessageSent(@SuppressWarnings("unused") int messageId) { + 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(Object, int, byte[], int)} method) - - * this event is received after all retries are exhausted. + * 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#onMessageSent(int)} will be received + * {@link WifiAwareDiscoverySessionCallback#onMessageSendSucceeded(int)} will be received * - never both. * * @param messageId The arbitrary message ID specified when sending the message. @@ -176,13 +172,14 @@ public class WifiAwareDiscoverySessionCallback { /** * Called when a message is received from a discovery session peer - in response to the - * peer's {@link WifiAwareDiscoveryBaseSession#sendMessage(Object, int, byte[])} or - * {@link WifiAwareDiscoveryBaseSession#sendMessage(Object, int, byte[], int)}. + * 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(Object peerHandle, byte[] message) { + public void onMessageReceived(WifiAwareManager.PeerHandle peerHandle, byte[] message) { /* empty */ } } diff --git a/wifi/java/android/net/wifi/aware/WifiAwareManager.java b/wifi/java/android/net/wifi/aware/WifiAwareManager.java index 10b70abad9df..029794dfa380 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareManager.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareManager.java @@ -45,7 +45,9 @@ import org.json.JSONObject; 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 Aware operations: @@ -56,14 +58,14 @@ import java.util.Arrays; * The class provides access to: * <ul> * <li>Initialize a Aware cluster (peer-to-peer synchronization). Refer to - * {@link #attach(Handler, WifiAwareAttachCallback)}. + * {@link #attach(WifiAwareAttachCallback, Handler)}. * <li>Create discovery sessions (publish or subscribe sessions). Refer to - * {@link WifiAwareSession#publish(Handler, PublishConfig, WifiAwareDiscoverySessionCallback)} and - * {@link WifiAwareSession#subscribe(Handler, SubscribeConfig, WifiAwareDiscoverySessionCallback)}. + * {@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 Aware connection with a peer. Refer to - * {@link WifiAwareDiscoveryBaseSession#createNetworkSpecifier(int, Object, byte[])} and + * {@link WifiAwareDiscoveryBaseSession#createNetworkSpecifier(PeerHandle, byte[])} and * {@link WifiAwareSession#createNetworkSpecifier(int, byte[], byte[])}. * </ul> * <p> @@ -73,7 +75,7 @@ import java.util.Arrays; * 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, WifiAwareAttachCallback)} to initialize a + * 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 @@ -86,12 +88,11 @@ import java.util.Arrays; * application detaches. * <p> * Once a Aware attach is confirmed use the - * {@link WifiAwareSession#publish(Handler, PublishConfig, WifiAwareDiscoverySessionCallback)} + * {@link WifiAwareSession#publish(PublishConfig, WifiAwareDiscoverySessionCallback, Handler)} * or - * {@link WifiAwareSession#subscribe(Handler, SubscribeConfig, - * WifiAwareDiscoverySessionCallback)} - * to create publish or subscribe Aware discovery sessions. Events are called on the provided - * callback object {@link WifiAwareDiscoverySessionCallback}. Specifically, the + * {@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 WifiAwareDiscoverySessionCallback#onSubscribeStarted( @@ -102,7 +103,7 @@ import java.util.Arrays; * the session {@link WifiAwarePublishDiscoverySession#updatePublish(PublishConfig)} and * {@link WifiAwareSubscribeDiscoverySession#updateSubscribe(SubscribeConfig)}. Sessions can * also be used to send messages using the - * {@link WifiAwareDiscoveryBaseSession#sendMessage(Object, int, byte[])} APIs. When an + * {@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 WifiAwareDiscoveryBaseSession#destroy()} API. * <p> @@ -115,7 +116,7 @@ import java.util.Arrays; * {@link android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE}. * <li>{@link NetworkRequest.Builder#setNetworkSpecifier(String)} using * {@link WifiAwareSession#createNetworkSpecifier(int, byte[], byte[])} or - * {@link WifiAwareDiscoveryBaseSession#createNetworkSpecifier(int, Object, byte[])}. + * {@link WifiAwareDiscoveryBaseSession#createNetworkSpecifier(PeerHandle, byte[])}. * </ul> * * @hide PROPOSED_AWARE_API @@ -225,7 +226,7 @@ public class WifiAwareManager { * Connection creation role is that of INITIATOR. Used to create a network specifier string * when requesting a Aware network. * - * @see WifiAwareDiscoveryBaseSession#createNetworkSpecifier(int, Object, byte[]) + * @see WifiAwareDiscoveryBaseSession#createNetworkSpecifier(PeerHandle, byte[]) * @see WifiAwareSession#createNetworkSpecifier(int, byte[], byte[]) */ public static final int WIFI_AWARE_DATA_PATH_ROLE_INITIATOR = 0; @@ -234,7 +235,7 @@ public class WifiAwareManager { * Connection creation role is that of RESPONDER. Used to create a network specifier string * when requesting a Aware network. * - * @see WifiAwareDiscoveryBaseSession#createNetworkSpecifier(int, Object, byte[]) + * @see WifiAwareDiscoveryBaseSession#createNetworkSpecifier(PeerHandle, byte[]) * @see WifiAwareSession#createNetworkSpecifier(int, byte[], byte[]) */ public static final int WIFI_AWARE_DATA_PATH_ROLE_RESPONDER = 1; @@ -326,13 +327,13 @@ public class WifiAwareManager { * 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 WifiAwareAttachCallback}. */ - public void attach(@Nullable Handler handler, @NonNull WifiAwareAttachCallback attachCallback) { + public void attach(@NonNull WifiAwareAttachCallback attachCallback, @Nullable Handler handler) { attach(handler, null, attachCallback, null); } @@ -352,20 +353,21 @@ public class WifiAwareManager { * 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, WifiAwareAttachCallback)} 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 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 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. */ - public void attach(@Nullable Handler handler, @NonNull WifiAwareAttachCallback attachCallback, - @NonNull WifiAwareIdentityChangedListener identityChangedListener) { + public void attach(@NonNull WifiAwareAttachCallback attachCallback, + @NonNull WifiAwareIdentityChangedListener identityChangedListener, + @Nullable Handler handler) { attach(handler, null, attachCallback, identityChangedListener); } @@ -481,7 +483,7 @@ public class WifiAwareManager { } /** @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( @@ -490,13 +492,13 @@ public class WifiAwareManager { 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(); } @@ -524,12 +526,12 @@ public class WifiAwareManager { } /** @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; @@ -569,7 +571,7 @@ public class WifiAwareManager { 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, @@ -874,20 +876,30 @@ public class WifiAwareManager { 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; } @@ -966,7 +978,7 @@ public class WifiAwareManager { @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); @@ -1019,12 +1031,14 @@ public class WifiAwareManager { } } - /** @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/aware/WifiAwarePublishDiscoverySession.java b/wifi/java/android/net/wifi/aware/WifiAwarePublishDiscoverySession.java index 610a92ce28b5..68786d17d38d 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwarePublishDiscoverySession.java +++ b/wifi/java/android/net/wifi/aware/WifiAwarePublishDiscoverySession.java @@ -21,9 +21,8 @@ import android.util.Log; /** * A class representing a Aware publish session. Created when - * {@link WifiAwareSession#publish(android.os.Handler, PublishConfig, - * WifiAwareDiscoverySessionCallback)} - * is called and a discovery session is created and returned in + * {@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 diff --git a/wifi/java/android/net/wifi/aware/WifiAwareSession.java b/wifi/java/android/net/wifi/aware/WifiAwareSession.java index 357bd43f12db..005895a28043 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareSession.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareSession.java @@ -65,7 +65,7 @@ public class WifiAwareSession { * session-wide destroy. * <p> * An application may re-attach after a destroy using - * {@link WifiAwareManager#attach(Handler, WifiAwareAttachCallback)} . + * {@link WifiAwareManager#attach(WifiAwareAttachCallback, Handler)} . */ public void destroy() { WifiAwareManager mgr = mMgr.get(); @@ -116,15 +116,15 @@ public class WifiAwareSession { * <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 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 WifiAwareDiscoverySessionCallback callback) { + 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 WifiAwareManager"); @@ -162,15 +162,15 @@ public class WifiAwareSession { * <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 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 WifiAwareDiscoverySessionCallback callback) { + 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 WifiAwareManager"); @@ -193,7 +193,8 @@ public class WifiAwareSession { * This API is targeted for applications which can obtain the peer MAC address using OOB * (out-of-band) discovery. Aware discovery does not provide the MAC address of the peer - * when using Aware discovery use the alternative network specifier method - - * {@link WifiAwareDiscoveryBaseSession#createNetworkSpecifier(int, Object, byte[])}. + * {@link WifiAwareDiscoveryBaseSession#createNetworkSpecifier(WifiAwareManager.PeerHandle, + * byte[])}. * * @param role The role of this device: * {@link WifiAwareManager#WIFI_AWARE_DATA_PATH_ROLE_INITIATOR} or @@ -210,7 +211,7 @@ public class WifiAwareSession { * * @return A string to be used to construct * {@link android.net.NetworkRequest.Builder#setNetworkSpecifier(String)} to pass to - * {@link android.net.ConnectivityManager#requestNetwork(NetworkRequest, + * {@link android.net.ConnectivityManager#requestNetwork(android.net.NetworkRequest, * android.net.ConnectivityManager.NetworkCallback)} * [or other varieties of that API]. */ diff --git a/wifi/java/android/net/wifi/aware/WifiAwareSubscribeDiscoverySession.java b/wifi/java/android/net/wifi/aware/WifiAwareSubscribeDiscoverySession.java index 7c48f549eaf4..a0ec8093a3ab 100644 --- a/wifi/java/android/net/wifi/aware/WifiAwareSubscribeDiscoverySession.java +++ b/wifi/java/android/net/wifi/aware/WifiAwareSubscribeDiscoverySession.java @@ -21,8 +21,8 @@ import android.util.Log; /** * A class representing a Aware subscribe session. Created when - * {@link WifiAwareSession#subscribe(android.os.Handler, SubscribeConfig, - * WifiAwareDiscoverySessionCallback)} + * {@link WifiAwareSession#subscribe(SubscribeConfig, + * WifiAwareDiscoverySessionCallback, android.os.Handler)} * is called and a discovery session is created and returned in * {@link WifiAwareDiscoverySessionCallback#onSubscribeStarted(WifiAwareSubscribeDiscoverySession)}. * See baseline functionality of all discovery sessions in {@link WifiAwareDiscoveryBaseSession}. 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 000000000000..96db5d02679e --- /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 18aae534d098..643753abf5dc 100644 --- a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java +++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java @@ -36,6 +36,27 @@ public final class PasspointConfiguration implements Parcelable { 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 @@ public final class PasspointConfiguration implements Parcelable { 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 92dbd8afb2d3..790dfaf643ca 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 @@ import java.util.Arrays; * @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 final class Credential implements Parcelable { */ 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 final class Credential implements Parcelable { */ 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 @@ public final class Credential implements Parcelable { 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 final class Credential implements Parcelable { 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 final class Credential implements Parcelable { */ 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 @@ public final class Credential implements Parcelable { 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 final class Credential implements Parcelable { */ 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 final class Credential implements Parcelable { */ 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 @@ public final class Credential implements Parcelable { 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 @@ public final class Credential implements Parcelable { 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 final class Credential implements Parcelable { */ 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 @@ public final class Credential implements Parcelable { 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 @@ public final class Credential implements Parcelable { } }; + /** + * 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 @@ public final class Credential implements Parcelable { 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 2acc8bec8007..d4a5792d93fc 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 @@ package android.net.wifi.hotspot2.pps; import android.os.Parcelable; import android.os.Parcel; import android.text.TextUtils; +import android.util.Log; import java.util.Arrays; @@ -34,6 +35,8 @@ import java.util.Arrays; * @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 final class HomeSP implements Parcelable { */ 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 @@ public final class HomeSP implements Parcelable { 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/p2p/IWifiP2pManager.aidl b/wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl index ee2e895af537..8b1cfaee8119 100644 --- a/wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl +++ b/wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl @@ -28,5 +28,6 @@ interface IWifiP2pManager 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 8d5cf6319d24..c93ac7b5f8f1 100644 --- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java +++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java @@ -283,6 +283,13 @@ public class WifiP2pManager { 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 class WifiP2pManager { */ 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 @@ public class WifiP2pManager { 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 000000000000..8c1eb0867298 --- /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 000000000000..6d86dd53eb76 --- /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 000000000000..906bfb397464 --- /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 000000000000..3fa97d1cdd76 --- /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 000000000000..975f8e539cc3 --- /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 000000000000..833c52751b95 --- /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 000000000000..d1f8384fc979 --- /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 000000000000..15641abd11f9 --- /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 000000000000..24c0127cb11b --- /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 000000000000..6095929758f0 --- /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 be11f0ee64c0..2350d3201171 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 @@ import org.junit.Test; @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 @@ public class PasspointConfigurationTest { 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 @@ public class PasspointConfigurationTest { 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 @@ public class PasspointConfigurationTest { 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 @@ public class PasspointConfigurationTest { 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 68ac4efa214f..9c8b749e1c93 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 @@ public class CredentialTest { 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 @@ public class CredentialTest { 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 @@ public class CredentialTest { 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 0d2da6404e7f..c70799332b02 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 @@ public class HomeSPTest { 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)); + } } |