summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp1
-rw-r--r--Android.mk6
-rw-r--r--api/current.txt139
-rw-r--r--api/system-current.txt205
-rw-r--r--api/test-current.txt139
-rw-r--r--cmds/am/Android.bp11
-rw-r--r--cmds/am/Android.mk11
-rw-r--r--cmds/hid/Android.bp1
-rw-r--r--cmds/hid/jni/Android.bp18
-rw-r--r--cmds/hid/jni/Android.mk23
-rw-r--r--cmds/idmap/create.cpp8
-rw-r--r--cmds/idmap/inspect.cpp4
-rw-r--r--cmds/interrupter/Android.bp11
-rw-r--r--cmds/interrupter/Android.mk23
-rw-r--r--core/java/android/app/ResourcesManager.java2
-rwxr-xr-xcore/java/android/bluetooth/BluetoothA2dpSink.java81
-rw-r--r--core/java/android/bluetooth/BluetoothAvrcpController.java54
-rw-r--r--core/java/android/bluetooth/BluetoothDevice.java143
-rw-r--r--core/java/android/bluetooth/BluetoothGatt.java88
-rw-r--r--core/java/android/bluetooth/BluetoothGattServerCallback.java4
-rw-r--r--core/java/android/bluetooth/BluetoothHeadset.java181
-rw-r--r--core/java/android/bluetooth/BluetoothHeadsetClient.java188
-rw-r--r--core/java/android/bluetooth/BluetoothHealth.java58
-rw-r--r--core/java/android/bluetooth/BluetoothInputDevice.java123
-rw-r--r--core/java/android/bluetooth/BluetoothInputHost.java65
-rw-r--r--core/java/android/bluetooth/BluetoothMap.java68
-rw-r--r--core/java/android/bluetooth/BluetoothMapClient.java72
-rw-r--r--core/java/android/bluetooth/BluetoothPan.java61
-rw-r--r--core/java/android/bluetooth/BluetoothPbap.java22
-rw-r--r--core/java/android/bluetooth/BluetoothPbapClient.java63
-rw-r--r--core/java/android/bluetooth/BluetoothSap.java72
-rw-r--r--core/java/android/bluetooth/BluetoothUuid.java2
-rw-r--r--core/java/android/bluetooth/le/BluetoothLeScanner.java7
-rw-r--r--core/java/android/bluetooth/le/ScanSettings.java2
-rw-r--r--core/java/android/net/LinkProperties.java90
-rw-r--r--core/java/android/net/NetworkStats.java47
-rw-r--r--core/java/android/net/metrics/WakeupEvent.java34
-rw-r--r--core/java/android/net/metrics/WakeupStats.java87
-rw-r--r--core/java/android/net/nsd/NsdManager.java14
-rw-r--r--core/java/android/os/Process.java14
-rw-r--r--core/java/android/os/SystemProperties.java80
-rw-r--r--core/java/android/os/ZygoteProcess.java48
-rw-r--r--core/java/com/android/internal/os/RuntimeInit.java55
-rw-r--r--core/java/com/android/internal/os/WebViewZygoteInit.java34
-rw-r--r--core/java/com/android/internal/os/WrapperInit.java58
-rw-r--r--core/java/com/android/internal/os/Zygote.java57
-rw-r--r--core/java/com/android/internal/os/ZygoteConnection.java280
-rw-r--r--core/java/com/android/internal/os/ZygoteInit.java56
-rw-r--r--core/java/com/android/internal/os/ZygoteServer.java70
-rw-r--r--core/jni/Android.bp1
-rw-r--r--core/jni/android_os_GraphicsEnvironment.cpp2
-rw-r--r--core/jni/android_os_SystemProperties.cpp269
-rw-r--r--core/jni/android_os_VintfObject.cpp8
-rw-r--r--core/jni/android_os_VintfRuntimeInfo.cpp4
-rw-r--r--core/jni/com_android_internal_os_Zygote.cpp12
-rw-r--r--core/res/res/values-mcc505-mnc01/config.xml9
-rw-r--r--core/res/res/values/config.xml19
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--core/res/res/xml/sms_short_codes.xml2
-rw-r--r--core/tests/coretests/src/android/net/LinkPropertiesTest.java108
-rwxr-xr-xcore/tests/systemproperties/run_core_systemproperties_test.sh2
-rw-r--r--core/tests/systemproperties/src/android/os/SystemPropertiesTest.java76
-rw-r--r--core/tests/utiltests/jni/Android.bp1
-rw-r--r--libs/androidfw/ResourceTypes.cpp7
-rw-r--r--libs/androidfw/include/androidfw/ResourceTypes.h1
-rw-r--r--libs/androidfw/include/androidfw/StringPiece.h1
-rw-r--r--libs/usb/Android.bp1
-rw-r--r--libs/usb/tests/AccessoryChat/Android.bp1
-rw-r--r--libs/usb/tests/AccessoryChat/accessorychat/Android.bp30
-rw-r--r--libs/usb/tests/AccessoryChat/accessorychat/Android.mk35
-rw-r--r--libs/usb/tests/accessorytest/Android.bp28
-rw-r--r--libs/usb/tests/accessorytest/Android.mk25
-rw-r--r--libs/usb/tests/accessorytest/accessory.h2
-rw-r--r--libs/usb/tests/accessorytest/hid.c2
-rw-r--r--libs/usb/tests/accessorytest/usb.c2
-rw-r--r--packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java7
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java16
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFooter.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java1
-rw-r--r--proto/src/ipconnectivity.proto223
-rw-r--r--services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java2
-rw-r--r--services/core/Android.mk5
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java50
-rw-r--r--services/core/java/com/android/server/NativeDaemonConnector.java8
-rw-r--r--services/core/java/com/android/server/NetworkManagementService.java41
-rw-r--r--services/core/java/com/android/server/RecoverySystemService.java5
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java28
-rw-r--r--services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java59
-rw-r--r--services/core/java/com/android/server/connectivity/Nat464Xlat.java131
-rw-r--r--services/core/java/com/android/server/connectivity/NetdEventListenerService.java98
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkAgentInfo.java28
-rw-r--r--services/core/java/com/android/server/connectivity/NetworkMonitor.java116
-rw-r--r--services/core/java/com/android/server/connectivity/tethering/OffloadController.java214
-rw-r--r--services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java19
-rw-r--r--services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java27
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java2
-rw-r--r--services/core/java/com/android/server/om/IdmapManager.java16
-rw-r--r--services/core/java/com/android/server/timezone/IntentHelperImpl.java26
-rw-r--r--services/core/java/com/android/server/timezone/RulesManagerService.java22
-rw-r--r--services/core/jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp4
-rw-r--r--services/net/java/android/net/apf/ApfFilter.java53
-rw-r--r--services/net/java/android/net/ip/InterfaceController.java162
-rw-r--r--services/net/java/android/net/ip/IpManager.java151
-rw-r--r--services/net/java/android/net/ip/IpReachabilityMonitor.java42
-rw-r--r--services/net/java/android/net/netlink/ConntrackMessage.java117
-rw-r--r--services/net/java/android/net/netlink/NetlinkSocket.java41
-rw-r--r--services/net/java/android/net/netlink/RtNetlinkNeighborMessage.java2
-rw-r--r--services/net/java/android/net/netlink/StructNfGenMsg.java51
-rw-r--r--services/net/java/android/net/netlink/StructNlAttr.java100
-rw-r--r--services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java42
-rw-r--r--telecomm/java/android/telecom/TelecomManager.java3
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java12
-rw-r--r--telephony/java/android/telephony/MbmsDownloadManager.java586
-rw-r--r--telephony/java/android/telephony/MbmsDownloadSession.java773
-rw-r--r--telephony/java/android/telephony/MbmsStreamingSession.java (renamed from telephony/java/android/telephony/MbmsStreamingManager.java)201
-rw-r--r--telephony/java/android/telephony/PhoneNumberUtils.java185
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java52
-rw-r--r--telephony/java/android/telephony/mbms/DownloadRequest.java92
-rw-r--r--telephony/java/android/telephony/mbms/DownloadStateCallback.java (renamed from telephony/java/android/telephony/mbms/DownloadProgressListener.java)36
-rw-r--r--telephony/java/android/telephony/mbms/FileInfo.java23
-rw-r--r--telephony/java/android/telephony/mbms/FileServiceInfo.java18
-rwxr-xr-xtelephony/java/android/telephony/mbms/IDownloadStateCallback.aidl (renamed from telephony/java/android/telephony/mbms/IDownloadProgressListener.aidl)9
-rwxr-xr-xtelephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl (renamed from telephony/java/android/telephony/mbms/IMbmsDownloadManagerCallback.aidl)8
-rwxr-xr-xtelephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl (renamed from telephony/java/android/telephony/mbms/IMbmsStreamingManagerCallback.aidl)8
-rwxr-xr-xtelephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl10
-rw-r--r--telephony/java/android/telephony/mbms/InternalDownloadSessionCallback.java86
-rw-r--r--telephony/java/android/telephony/mbms/InternalDownloadStateCallback.java70
-rw-r--r--telephony/java/android/telephony/mbms/InternalStreamingServiceCallback.java35
-rw-r--r--telephony/java/android/telephony/mbms/InternalStreamingSessionCallback.java (renamed from telephony/java/android/telephony/mbms/InternalStreamingManagerCallback.java)32
-rw-r--r--telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java64
-rw-r--r--telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java233
-rw-r--r--telephony/java/android/telephony/mbms/MbmsDownloadSessionCallback.java66
-rw-r--r--telephony/java/android/telephony/mbms/MbmsErrors.java (renamed from telephony/java/android/telephony/mbms/MbmsException.java)32
-rw-r--r--telephony/java/android/telephony/mbms/MbmsStreamingSessionCallback.java (renamed from telephony/java/android/telephony/mbms/MbmsStreamingManagerCallback.java)22
-rw-r--r--telephony/java/android/telephony/mbms/MbmsTempFileProvider.java8
-rw-r--r--telephony/java/android/telephony/mbms/MbmsUtils.java7
-rw-r--r--telephony/java/android/telephony/mbms/ServiceInfo.java29
-rw-r--r--telephony/java/android/telephony/mbms/StreamingService.java66
-rw-r--r--telephony/java/android/telephony/mbms/StreamingServiceCallback.java6
-rw-r--r--telephony/java/android/telephony/mbms/UriPathPair.java28
-rwxr-xr-xtelephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl15
-rwxr-xr-xtelephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl10
-rw-r--r--telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java235
-rw-r--r--telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java92
-rw-r--r--telephony/java/android/telephony/mbms/vendor/VendorUtils.java (renamed from telephony/java/android/telephony/mbms/vendor/VendorIntents.java)56
-rw-r--r--telephony/java/com/android/ims/ImsConfig.java1
-rw-r--r--telephony/java/com/android/internal/telephony/ExponentialBackoff.java84
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl9
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/GsmSmsAddress.java8
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/SmsMessage.java4
-rw-r--r--test-runner/Android.mk7
-rw-r--r--tests/net/Android.mk2
-rw-r--r--tests/net/java/android/net/apf/ApfTest.java97
-rw-r--r--tests/net/java/android/net/ip/IpManagerTest.java3
-rw-r--r--tests/net/java/android/net/netlink/ConntrackMessageTest.java131
-rw-r--r--tests/net/java/android/net/nsd/NsdManagerTest.java65
-rw-r--r--tests/net/java/com/android/internal/util/TestUtils.java3
-rw-r--r--tests/net/java/com/android/server/ConnectivityServiceTest.java76
-rw-r--r--tests/net/java/com/android/server/NsdServiceTest.java5
-rw-r--r--tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java91
-rw-r--r--tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java61
-rw-r--r--tests/net/java/com/android/server/connectivity/Nat464XlatTest.java226
-rw-r--r--tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java140
-rw-r--r--tests/net/java/com/android/server/connectivity/NetworkMonitorTest.java76
-rw-r--r--tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java84
-rw-r--r--tools/aapt/Android.bp115
-rw-r--r--tools/aapt/Android.mk90
-rw-r--r--tools/aapt/Command.cpp18
-rw-r--r--tools/aapt/Main.cpp17
-rw-r--r--tools/aapt2/Android.bp16
-rw-r--r--tools/aapt2/flatten/XmlFlattener.cpp6
-rw-r--r--tools/aapt2/flatten/XmlFlattener_test.cpp225
-rw-r--r--tools/aapt2/link/ManifestFixer.cpp1
-rw-r--r--tools/aapt2/test/Common.h6
-rw-r--r--tools/aapt2/util/Maybe.h32
-rw-r--r--tools/aapt2/util/Util.cpp15
-rw-r--r--tools/aapt2/util/Util.h3
-rw-r--r--tools/aapt2/util/Util_test.cpp88
-rw-r--r--tools/aapt2/xml/XmlDom_test.cpp37
-rw-r--r--tools/bit/Android.bp40
-rw-r--r--tools/bit/Android.mk44
-rw-r--r--tools/bit/adb.h2
-rw-r--r--tools/incident_report/Android.bp38
-rw-r--r--tools/incident_report/Android.mk44
-rw-r--r--tools/incident_section_gen/Android.bp33
-rw-r--r--tools/incident_section_gen/Android.mk37
-rw-r--r--tools/obbtool/Android.bp51
-rw-r--r--tools/obbtool/Android.mk47
-rw-r--r--tools/split-select/Android.bp108
-rw-r--r--tools/split-select/Android.mk119
-rw-r--r--tools/streaming_proto/Android.bp29
-rw-r--r--tools/streaming_proto/Android.mk13
-rw-r--r--tools/validatekeymaps/Android.bp30
-rw-r--r--tools/validatekeymaps/Android.mk33
199 files changed, 7374 insertions, 3919 deletions
diff --git a/Android.bp b/Android.bp
index 9e8c384399a6..6b61467b1a27 100644
--- a/Android.bp
+++ b/Android.bp
@@ -51,6 +51,7 @@ cc_library {
}
subdirs = [
+ "cmds/*",
"core/jni",
"libs/*",
"media/*",
diff --git a/Android.mk b/Android.mk
index 0e6642d70a28..b92f31e80f07 100644
--- a/Android.mk
+++ b/Android.mk
@@ -486,9 +486,9 @@ LOCAL_SRC_FILES += \
telecomm/java/com/android/internal/telecom/IInCallService.aidl \
telecomm/java/com/android/internal/telecom/ITelecomService.aidl \
telecomm/java/com/android/internal/telecom/RemoteServiceCallback.aidl \
- telephony/java/android/telephony/mbms/IMbmsDownloadManagerCallback.aidl \
- telephony/java/android/telephony/mbms/IMbmsStreamingManagerCallback.aidl \
- telephony/java/android/telephony/mbms/IDownloadProgressListener.aidl \
+ telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl \
+ telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl \
+ telephony/java/android/telephony/mbms/IDownloadStateCallback.aidl \
telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl \
telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl \
telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl \
diff --git a/api/current.txt b/api/current.txt
index b2181657da7d..5d7dcb11b245 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -39347,6 +39347,7 @@ package android.telecom {
method public boolean isInManagedCall();
method public boolean isIncomingCallPermitted(android.telecom.PhoneAccountHandle);
method public boolean isOutgoingCallPermitted(android.telecom.PhoneAccountHandle);
+ method public boolean isTtySupported();
method public boolean isVoiceMailNumber(android.telecom.PhoneAccountHandle, java.lang.String);
method public void placeCall(android.net.Uri, android.os.Bundle);
method public void registerPhoneAccount(android.telecom.PhoneAccount);
@@ -39749,13 +39750,42 @@ package android.telephony {
field public static final int STATUS_UNKNOWN_ERROR = 4; // 0x4
}
- public class MbmsStreamingManager {
- method public static android.telephony.MbmsStreamingManager create(android.content.Context, android.telephony.mbms.MbmsStreamingManagerCallback, int, android.os.Handler) throws android.telephony.mbms.MbmsException;
- method public static android.telephony.MbmsStreamingManager create(android.content.Context, android.telephony.mbms.MbmsStreamingManagerCallback, android.os.Handler) throws android.telephony.mbms.MbmsException;
- method public static android.telephony.MbmsStreamingManager create(android.content.Context, android.telephony.mbms.MbmsStreamingManagerCallback) throws android.telephony.mbms.MbmsException;
- method public void dispose();
- method public void getStreamingServices(java.util.List<java.lang.String>) throws android.telephony.mbms.MbmsException;
- method public android.telephony.mbms.StreamingService startStreaming(android.telephony.mbms.StreamingServiceInfo, android.telephony.mbms.StreamingServiceCallback, android.os.Handler) throws android.telephony.mbms.MbmsException;
+ public class MbmsDownloadSession implements java.lang.AutoCloseable {
+ method public void cancelDownload(android.telephony.mbms.DownloadRequest);
+ method public void close();
+ method public static android.telephony.MbmsDownloadSession create(android.content.Context, android.telephony.mbms.MbmsDownloadSessionCallback, android.os.Handler);
+ method public static android.telephony.MbmsDownloadSession create(android.content.Context, android.telephony.mbms.MbmsDownloadSessionCallback, int, android.os.Handler);
+ method public void download(android.telephony.mbms.DownloadRequest);
+ method public int getDownloadStatus(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo);
+ method public java.io.File getTempFileRootDirectory();
+ method public java.util.List<android.telephony.mbms.DownloadRequest> listPendingDownloads();
+ method public void registerStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback, android.os.Handler);
+ method public void requestUpdateFileServices(java.util.List<java.lang.String>);
+ method public void resetDownloadKnowledge(android.telephony.mbms.DownloadRequest);
+ method public void setTempFileRootDirectory(java.io.File);
+ method public void unregisterStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback);
+ field public static final java.lang.String DEFAULT_TOP_LEVEL_TEMP_DIRECTORY = "androidMbmsTempFileRoot";
+ field public static final java.lang.String EXTRA_MBMS_COMPLETED_FILE_URI = "android.telephony.extra.MBMS_COMPLETED_FILE_URI";
+ field public static final java.lang.String EXTRA_MBMS_DOWNLOAD_REQUEST = "android.telephony.extra.MBMS_DOWNLOAD_REQUEST";
+ field public static final java.lang.String EXTRA_MBMS_DOWNLOAD_RESULT = "android.telephony.extra.MBMS_DOWNLOAD_RESULT";
+ field public static final java.lang.String EXTRA_MBMS_FILE_INFO = "android.telephony.extra.MBMS_FILE_INFO";
+ field public static final int RESULT_CANCELLED = 2; // 0x2
+ field public static final int RESULT_EXPIRED = 3; // 0x3
+ field public static final int RESULT_IO_ERROR = 4; // 0x4
+ field public static final int RESULT_SUCCESSFUL = 1; // 0x1
+ field public static final int STATUS_ACTIVELY_DOWNLOADING = 1; // 0x1
+ field public static final int STATUS_PENDING_DOWNLOAD = 2; // 0x2
+ field public static final int STATUS_PENDING_DOWNLOAD_WINDOW = 4; // 0x4
+ field public static final int STATUS_PENDING_REPAIR = 3; // 0x3
+ field public static final int STATUS_UNKNOWN = 0; // 0x0
+ }
+
+ public class MbmsStreamingSession implements java.lang.AutoCloseable {
+ method public void close();
+ method public static android.telephony.MbmsStreamingSession create(android.content.Context, android.telephony.mbms.MbmsStreamingSessionCallback, int, android.os.Handler);
+ method public static android.telephony.MbmsStreamingSession create(android.content.Context, android.telephony.mbms.MbmsStreamingSessionCallback, android.os.Handler);
+ method public void requestUpdateStreamingServices(java.util.List<java.lang.String>);
+ method public android.telephony.mbms.StreamingService startStreaming(android.telephony.mbms.StreamingServiceInfo, android.telephony.mbms.StreamingServiceCallback, android.os.Handler);
}
public class NeighboringCellInfo implements android.os.Parcelable {
@@ -39788,8 +39818,10 @@ package android.telephony {
public class PhoneNumberUtils {
ctor public PhoneNumberUtils();
method public static void addTtsSpan(android.text.Spannable, int, int);
- method public static java.lang.String calledPartyBCDFragmentToString(byte[], int, int);
- method public static java.lang.String calledPartyBCDToString(byte[], int, int);
+ method public static deprecated java.lang.String calledPartyBCDFragmentToString(byte[], int, int);
+ method public static java.lang.String calledPartyBCDFragmentToString(byte[], int, int, int);
+ method public static deprecated java.lang.String calledPartyBCDToString(byte[], int, int);
+ method public static java.lang.String calledPartyBCDToString(byte[], int, int, int);
method public static boolean compare(java.lang.String, java.lang.String);
method public static boolean compare(android.content.Context, java.lang.String, java.lang.String);
method public static java.lang.String convertKeypadLettersToDigits(java.lang.String);
@@ -39822,12 +39854,15 @@ package android.telephony {
method public static byte[] networkPortionToCalledPartyBCD(java.lang.String);
method public static byte[] networkPortionToCalledPartyBCDWithLength(java.lang.String);
method public static java.lang.String normalizeNumber(java.lang.String);
- method public static byte[] numberToCalledPartyBCD(java.lang.String);
+ method public static deprecated byte[] numberToCalledPartyBCD(java.lang.String);
+ method public static byte[] numberToCalledPartyBCD(java.lang.String, int);
method public static java.lang.String replaceUnicodeDigits(java.lang.String);
method public static java.lang.String stringFromStringAndTOA(java.lang.String, int);
method public static java.lang.String stripSeparators(java.lang.String);
method public static java.lang.String toCallerIDMinMatch(java.lang.String);
method public static int toaFromString(java.lang.String);
+ field public static final int BCD_EXTENDED_TYPE_CALLED_PARTY = 2; // 0x2
+ field public static final int BCD_EXTENDED_TYPE_EF_ADN = 1; // 0x1
field public static final int FORMAT_JAPAN = 2; // 0x2
field public static final int FORMAT_NANP = 1; // 0x1
field public static final int FORMAT_UNKNOWN = 0; // 0x0
@@ -40110,6 +40145,7 @@ package android.telephony {
method public int getPhoneCount();
method public int getPhoneType();
method public android.telephony.ServiceState getServiceState();
+ method public android.telephony.SignalStrength getSignalStrength();
method public java.lang.String getSimCountryIso();
method public java.lang.String getSimOperator();
method public java.lang.String getSimOperatorName();
@@ -40135,7 +40171,7 @@ package android.telephony {
method public boolean isHearingAidCompatibilitySupported();
method public boolean isNetworkRoaming();
method public boolean isSmsCapable();
- method public boolean isTtyModeSupported();
+ method public deprecated boolean isTtyModeSupported();
method public boolean isVoiceCapable();
method public boolean isVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle);
method public boolean isWorldPhone();
@@ -40388,15 +40424,73 @@ package android.telephony.gsm {
package android.telephony.mbms {
- public class MbmsException extends java.lang.Exception {
- method public int getErrorCode();
+ public final class DownloadRequest implements android.os.Parcelable {
+ method public static android.telephony.mbms.DownloadRequest copy(android.telephony.mbms.DownloadRequest);
+ method public int describeContents();
+ method public java.lang.String getFileServiceId();
+ method public static int getMaxAppIntentSize();
+ method public static int getMaxDestinationUriSize();
+ method public android.net.Uri getSourceUri();
+ method public int getSubscriptionId();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.mbms.DownloadRequest> CREATOR;
+ }
+
+ public static class DownloadRequest.Builder {
+ ctor public DownloadRequest.Builder();
+ method public android.telephony.mbms.DownloadRequest build();
+ method public android.telephony.mbms.DownloadRequest.Builder setAppIntent(android.content.Intent);
+ method public android.telephony.mbms.DownloadRequest.Builder setServiceInfo(android.telephony.mbms.FileServiceInfo);
+ method public android.telephony.mbms.DownloadRequest.Builder setSource(android.net.Uri);
+ method public android.telephony.mbms.DownloadRequest.Builder setSubscriptionId(int);
+ }
+
+ public class DownloadStateCallback {
+ ctor public DownloadStateCallback();
+ method public void onProgressUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int, int, int, int);
+ method public void onStateUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int);
+ }
+
+ public final class FileInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public java.lang.String getMimeType();
+ method public android.net.Uri getUri();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.mbms.FileInfo> CREATOR;
+ }
+
+ public final class FileServiceInfo extends android.telephony.mbms.ServiceInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public java.util.List<android.telephony.mbms.FileInfo> getFiles();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.mbms.FileServiceInfo> CREATOR;
+ }
+
+ public class MbmsDownloadReceiver extends android.content.BroadcastReceiver {
+ ctor public MbmsDownloadReceiver();
+ method public void onReceive(android.content.Context, android.content.Intent);
+ }
+
+ public class MbmsDownloadSessionCallback {
+ ctor public MbmsDownloadSessionCallback();
+ method public void onError(int, java.lang.String);
+ method public void onFileServicesUpdated(java.util.List<android.telephony.mbms.FileServiceInfo>);
+ method public void onMiddlewareReady();
+ }
+
+ public class MbmsErrors {
field public static final int ERROR_MIDDLEWARE_LOST = 3; // 0x3
field public static final int ERROR_MIDDLEWARE_NOT_BOUND = 2; // 0x2
field public static final int ERROR_NO_UNIQUE_MIDDLEWARE = 1; // 0x1
field public static final int SUCCESS = 0; // 0x0
}
- public static class MbmsException.GeneralErrors {
+ public static class MbmsErrors.DownloadErrors {
+ field public static final int ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT = 401; // 0x191
+ field public static final int ERROR_UNKNOWN_DOWNLOAD_REQUEST = 402; // 0x192
+ }
+
+ public static class MbmsErrors.GeneralErrors {
field public static final int ERROR_CARRIER_CHANGE_NOT_ALLOWED = 207; // 0xcf
field public static final int ERROR_IN_E911 = 204; // 0xcc
field public static final int ERROR_MIDDLEWARE_NOT_YET_READY = 201; // 0xc9
@@ -40406,39 +40500,38 @@ package android.telephony.mbms {
field public static final int ERROR_UNABLE_TO_READ_SIM = 206; // 0xce
}
- public static class MbmsException.InitializationErrors {
+ public static class MbmsErrors.InitializationErrors {
field public static final int ERROR_APP_PERMISSIONS_NOT_GRANTED = 102; // 0x66
field public static final int ERROR_DUPLICATE_INITIALIZE = 101; // 0x65
field public static final int ERROR_UNABLE_TO_INITIALIZE = 103; // 0x67
}
- public static class MbmsException.StreamingErrors {
+ public static class MbmsErrors.StreamingErrors {
field public static final int ERROR_CONCURRENT_SERVICE_LIMIT_REACHED = 301; // 0x12d
field public static final int ERROR_DUPLICATE_START_STREAM = 303; // 0x12f
field public static final int ERROR_UNABLE_TO_START_SERVICE = 302; // 0x12e
}
- public class MbmsStreamingManagerCallback {
- ctor public MbmsStreamingManagerCallback();
+ public class MbmsStreamingSessionCallback {
+ ctor public MbmsStreamingSessionCallback();
method public void onError(int, java.lang.String);
method public void onMiddlewareReady();
method public void onStreamingServicesUpdated(java.util.List<android.telephony.mbms.StreamingServiceInfo>);
}
public class ServiceInfo {
- method public java.lang.String getClassName();
method public java.util.List<java.util.Locale> getLocales();
- method public java.util.Map<java.util.Locale, java.lang.String> getNames();
+ method public java.lang.CharSequence getNameForLocale(java.util.Locale);
+ method public java.lang.String getServiceClassName();
method public java.lang.String getServiceId();
method public java.util.Date getSessionEndTime();
method public java.util.Date getSessionStartTime();
}
public class StreamingService {
- method public void dispose() throws android.telephony.mbms.MbmsException;
method public android.telephony.mbms.StreamingServiceInfo getInfo();
- method public android.net.Uri getPlaybackUri() throws android.telephony.mbms.MbmsException;
- method public void stopStreaming() throws android.telephony.mbms.MbmsException;
+ method public android.net.Uri getPlaybackUri();
+ method public void stopStreaming();
field public static final int BROADCAST_METHOD = 1; // 0x1
field public static final int REASON_BY_USER_REQUEST = 1; // 0x1
field public static final int REASON_END_OF_SESSION = 2; // 0x2
diff --git a/api/system-current.txt b/api/system-current.txt
index 928346b0ac07..d5baf1cc6c55 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -43179,13 +43179,43 @@ package android.telephony {
field public static final int STATUS_UNKNOWN_ERROR = 4; // 0x4
}
- public class MbmsStreamingManager {
- method public static android.telephony.MbmsStreamingManager create(android.content.Context, android.telephony.mbms.MbmsStreamingManagerCallback, int, android.os.Handler) throws android.telephony.mbms.MbmsException;
- method public static android.telephony.MbmsStreamingManager create(android.content.Context, android.telephony.mbms.MbmsStreamingManagerCallback, android.os.Handler) throws android.telephony.mbms.MbmsException;
- method public static android.telephony.MbmsStreamingManager create(android.content.Context, android.telephony.mbms.MbmsStreamingManagerCallback) throws android.telephony.mbms.MbmsException;
- method public void dispose();
- method public void getStreamingServices(java.util.List<java.lang.String>) throws android.telephony.mbms.MbmsException;
- method public android.telephony.mbms.StreamingService startStreaming(android.telephony.mbms.StreamingServiceInfo, android.telephony.mbms.StreamingServiceCallback, android.os.Handler) throws android.telephony.mbms.MbmsException;
+ public class MbmsDownloadSession implements java.lang.AutoCloseable {
+ method public void cancelDownload(android.telephony.mbms.DownloadRequest);
+ method public void close();
+ method public static android.telephony.MbmsDownloadSession create(android.content.Context, android.telephony.mbms.MbmsDownloadSessionCallback, android.os.Handler);
+ method public static android.telephony.MbmsDownloadSession create(android.content.Context, android.telephony.mbms.MbmsDownloadSessionCallback, int, android.os.Handler);
+ method public void download(android.telephony.mbms.DownloadRequest);
+ method public int getDownloadStatus(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo);
+ method public java.io.File getTempFileRootDirectory();
+ method public java.util.List<android.telephony.mbms.DownloadRequest> listPendingDownloads();
+ method public void registerStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback, android.os.Handler);
+ method public void requestUpdateFileServices(java.util.List<java.lang.String>);
+ method public void resetDownloadKnowledge(android.telephony.mbms.DownloadRequest);
+ method public void setTempFileRootDirectory(java.io.File);
+ method public void unregisterStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback);
+ field public static final java.lang.String DEFAULT_TOP_LEVEL_TEMP_DIRECTORY = "androidMbmsTempFileRoot";
+ field public static final java.lang.String EXTRA_MBMS_COMPLETED_FILE_URI = "android.telephony.extra.MBMS_COMPLETED_FILE_URI";
+ field public static final java.lang.String EXTRA_MBMS_DOWNLOAD_REQUEST = "android.telephony.extra.MBMS_DOWNLOAD_REQUEST";
+ field public static final java.lang.String EXTRA_MBMS_DOWNLOAD_RESULT = "android.telephony.extra.MBMS_DOWNLOAD_RESULT";
+ field public static final java.lang.String EXTRA_MBMS_FILE_INFO = "android.telephony.extra.MBMS_FILE_INFO";
+ field public static final java.lang.String MBMS_DOWNLOAD_SERVICE_ACTION = "android.telephony.action.EmbmsDownload";
+ field public static final int RESULT_CANCELLED = 2; // 0x2
+ field public static final int RESULT_EXPIRED = 3; // 0x3
+ field public static final int RESULT_IO_ERROR = 4; // 0x4
+ field public static final int RESULT_SUCCESSFUL = 1; // 0x1
+ field public static final int STATUS_ACTIVELY_DOWNLOADING = 1; // 0x1
+ field public static final int STATUS_PENDING_DOWNLOAD = 2; // 0x2
+ field public static final int STATUS_PENDING_DOWNLOAD_WINDOW = 4; // 0x4
+ field public static final int STATUS_PENDING_REPAIR = 3; // 0x3
+ field public static final int STATUS_UNKNOWN = 0; // 0x0
+ }
+
+ public class MbmsStreamingSession implements java.lang.AutoCloseable {
+ method public void close();
+ method public static android.telephony.MbmsStreamingSession create(android.content.Context, android.telephony.mbms.MbmsStreamingSessionCallback, int, android.os.Handler);
+ method public static android.telephony.MbmsStreamingSession create(android.content.Context, android.telephony.mbms.MbmsStreamingSessionCallback, android.os.Handler);
+ method public void requestUpdateStreamingServices(java.util.List<java.lang.String>);
+ method public android.telephony.mbms.StreamingService startStreaming(android.telephony.mbms.StreamingServiceInfo, android.telephony.mbms.StreamingServiceCallback, android.os.Handler);
field public static final java.lang.String MBMS_STREAMING_SERVICE_ACTION = "android.telephony.action.EmbmsStreaming";
}
@@ -43219,8 +43249,10 @@ package android.telephony {
public class PhoneNumberUtils {
ctor public PhoneNumberUtils();
method public static void addTtsSpan(android.text.Spannable, int, int);
- method public static java.lang.String calledPartyBCDFragmentToString(byte[], int, int);
- method public static java.lang.String calledPartyBCDToString(byte[], int, int);
+ method public static deprecated java.lang.String calledPartyBCDFragmentToString(byte[], int, int);
+ method public static java.lang.String calledPartyBCDFragmentToString(byte[], int, int, int);
+ method public static deprecated java.lang.String calledPartyBCDToString(byte[], int, int);
+ method public static java.lang.String calledPartyBCDToString(byte[], int, int, int);
method public static boolean compare(java.lang.String, java.lang.String);
method public static boolean compare(android.content.Context, java.lang.String, java.lang.String);
method public static java.lang.String convertKeypadLettersToDigits(java.lang.String);
@@ -43253,12 +43285,15 @@ package android.telephony {
method public static byte[] networkPortionToCalledPartyBCD(java.lang.String);
method public static byte[] networkPortionToCalledPartyBCDWithLength(java.lang.String);
method public static java.lang.String normalizeNumber(java.lang.String);
- method public static byte[] numberToCalledPartyBCD(java.lang.String);
+ method public static deprecated byte[] numberToCalledPartyBCD(java.lang.String);
+ method public static byte[] numberToCalledPartyBCD(java.lang.String, int);
method public static java.lang.String replaceUnicodeDigits(java.lang.String);
method public static java.lang.String stringFromStringAndTOA(java.lang.String, int);
method public static java.lang.String stripSeparators(java.lang.String);
method public static java.lang.String toCallerIDMinMatch(java.lang.String);
method public static int toaFromString(java.lang.String);
+ field public static final int BCD_EXTENDED_TYPE_CALLED_PARTY = 2; // 0x2
+ field public static final int BCD_EXTENDED_TYPE_EF_ADN = 1; // 0x1
field public static final int FORMAT_JAPAN = 2; // 0x2
field public static final int FORMAT_NANP = 1; // 0x1
field public static final int FORMAT_UNKNOWN = 0; // 0x0
@@ -43531,8 +43566,8 @@ package android.telephony {
}
public class TelephonyManager {
- method public void answerRingingCall();
- method public void call(java.lang.String, java.lang.String);
+ method public deprecated void answerRingingCall();
+ method public deprecated void call(java.lang.String, java.lang.String);
method public boolean canChangeDtmfToneLength();
method public int checkCarrierPrivilegesForPackage(java.lang.String);
method public int checkCarrierPrivilegesForPackageAnyPhone(java.lang.String);
@@ -43542,7 +43577,7 @@ package android.telephony {
method public boolean disableDataConnectivity();
method public boolean enableDataConnectivity();
method public void enableVideoCalling(boolean);
- method public boolean endCall();
+ method public deprecated boolean endCall();
method public java.util.List<android.telephony.CellInfo> getAllCellInfo();
method public java.util.List<android.service.carrier.CarrierIdentifier> getAllowedCarriers(int);
method public int getCallState();
@@ -43583,6 +43618,7 @@ package android.telephony {
method public int getPhoneCount();
method public int getPhoneType();
method public android.telephony.ServiceState getServiceState();
+ method public android.telephony.SignalStrength getSignalStrength();
method public java.lang.String getSimCountryIso();
method public java.lang.String getSimOperator();
method public java.lang.String getSimOperatorName();
@@ -43617,7 +43653,7 @@ package android.telephony {
method public boolean isRadioOn();
method public boolean isRinging();
method public boolean isSmsCapable();
- method public boolean isTtyModeSupported();
+ method public deprecated boolean isTtyModeSupported();
method public boolean isVideoCallingEnabled();
method public deprecated boolean isVisualVoicemailEnabled(android.telecom.PhoneAccountHandle);
method public boolean isVoiceCapable();
@@ -43642,7 +43678,7 @@ package android.telephony {
method public boolean setVoiceMailNumber(java.lang.String, java.lang.String);
method public void setVoicemailRingtoneUri(android.telecom.PhoneAccountHandle, android.net.Uri);
method public void setVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle, boolean);
- method public void silenceRinger();
+ method public deprecated void silenceRinger();
method public boolean supplyPin(java.lang.String);
method public int[] supplyPinReportResult(java.lang.String);
method public boolean supplyPuk(java.lang.String, java.lang.String);
@@ -43902,15 +43938,85 @@ package android.telephony.ims {
package android.telephony.mbms {
- public class MbmsException extends java.lang.Exception {
- method public int getErrorCode();
+ public final class DownloadRequest implements android.os.Parcelable {
+ method public static android.telephony.mbms.DownloadRequest copy(android.telephony.mbms.DownloadRequest);
+ method public int describeContents();
+ method public java.lang.String getFileServiceId();
+ method public static int getMaxAppIntentSize();
+ method public static int getMaxDestinationUriSize();
+ method public byte[] getOpaqueData();
+ method public android.net.Uri getSourceUri();
+ method public int getSubscriptionId();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.mbms.DownloadRequest> CREATOR;
+ }
+
+ public static class DownloadRequest.Builder {
+ ctor public DownloadRequest.Builder();
+ method public android.telephony.mbms.DownloadRequest build();
+ method public android.telephony.mbms.DownloadRequest.Builder setAppIntent(android.content.Intent);
+ method public android.telephony.mbms.DownloadRequest.Builder setOpaqueData(byte[]);
+ method public android.telephony.mbms.DownloadRequest.Builder setServiceId(java.lang.String);
+ method public android.telephony.mbms.DownloadRequest.Builder setServiceInfo(android.telephony.mbms.FileServiceInfo);
+ method public android.telephony.mbms.DownloadRequest.Builder setSource(android.net.Uri);
+ method public android.telephony.mbms.DownloadRequest.Builder setSubscriptionId(int);
+ }
+
+ public class DownloadStateCallback {
+ ctor public DownloadStateCallback();
+ method public void onProgressUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int, int, int, int);
+ method public void onStateUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int);
+ }
+
+ public final class FileInfo implements android.os.Parcelable {
+ ctor public FileInfo(android.net.Uri, java.lang.String);
+ method public int describeContents();
+ method public java.lang.String getMimeType();
+ method public android.net.Uri getUri();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.mbms.FileInfo> CREATOR;
+ }
+
+ public final class FileServiceInfo extends android.telephony.mbms.ServiceInfo implements android.os.Parcelable {
+ ctor public FileServiceInfo(java.util.Map<java.util.Locale, java.lang.String>, java.lang.String, java.util.List<java.util.Locale>, java.lang.String, java.util.Date, java.util.Date, java.util.List<android.telephony.mbms.FileInfo>);
+ method public int describeContents();
+ method public java.util.List<android.telephony.mbms.FileInfo> getFiles();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.mbms.FileServiceInfo> CREATOR;
+ }
+
+ public class MbmsDownloadReceiver extends android.content.BroadcastReceiver {
+ ctor public MbmsDownloadReceiver();
+ method public void onReceive(android.content.Context, android.content.Intent);
+ field public static final int RESULT_APP_NOTIFICATION_ERROR = 6; // 0x6
+ field public static final int RESULT_BAD_TEMP_FILE_ROOT = 3; // 0x3
+ field public static final int RESULT_DOWNLOAD_FINALIZATION_ERROR = 4; // 0x4
+ field public static final int RESULT_INVALID_ACTION = 1; // 0x1
+ field public static final int RESULT_MALFORMED_INTENT = 2; // 0x2
+ field public static final int RESULT_OK = 0; // 0x0
+ field public static final int RESULT_TEMP_FILE_GENERATION_ERROR = 5; // 0x5
+ }
+
+ public class MbmsDownloadSessionCallback {
+ ctor public MbmsDownloadSessionCallback();
+ method public void onError(int, java.lang.String);
+ method public void onFileServicesUpdated(java.util.List<android.telephony.mbms.FileServiceInfo>);
+ method public void onMiddlewareReady();
+ }
+
+ public class MbmsErrors {
field public static final int ERROR_MIDDLEWARE_LOST = 3; // 0x3
field public static final int ERROR_MIDDLEWARE_NOT_BOUND = 2; // 0x2
field public static final int ERROR_NO_UNIQUE_MIDDLEWARE = 1; // 0x1
field public static final int SUCCESS = 0; // 0x0
}
- public static class MbmsException.GeneralErrors {
+ public static class MbmsErrors.DownloadErrors {
+ field public static final int ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT = 401; // 0x191
+ field public static final int ERROR_UNKNOWN_DOWNLOAD_REQUEST = 402; // 0x192
+ }
+
+ public static class MbmsErrors.GeneralErrors {
field public static final int ERROR_CARRIER_CHANGE_NOT_ALLOWED = 207; // 0xcf
field public static final int ERROR_IN_E911 = 204; // 0xcc
field public static final int ERROR_MIDDLEWARE_NOT_YET_READY = 201; // 0xc9
@@ -43920,39 +44026,38 @@ package android.telephony.mbms {
field public static final int ERROR_UNABLE_TO_READ_SIM = 206; // 0xce
}
- public static class MbmsException.InitializationErrors {
+ public static class MbmsErrors.InitializationErrors {
field public static final int ERROR_APP_PERMISSIONS_NOT_GRANTED = 102; // 0x66
field public static final int ERROR_DUPLICATE_INITIALIZE = 101; // 0x65
field public static final int ERROR_UNABLE_TO_INITIALIZE = 103; // 0x67
}
- public static class MbmsException.StreamingErrors {
+ public static class MbmsErrors.StreamingErrors {
field public static final int ERROR_CONCURRENT_SERVICE_LIMIT_REACHED = 301; // 0x12d
field public static final int ERROR_DUPLICATE_START_STREAM = 303; // 0x12f
field public static final int ERROR_UNABLE_TO_START_SERVICE = 302; // 0x12e
}
- public class MbmsStreamingManagerCallback {
- ctor public MbmsStreamingManagerCallback();
+ public class MbmsStreamingSessionCallback {
+ ctor public MbmsStreamingSessionCallback();
method public void onError(int, java.lang.String);
method public void onMiddlewareReady();
method public void onStreamingServicesUpdated(java.util.List<android.telephony.mbms.StreamingServiceInfo>);
}
public class ServiceInfo {
- method public java.lang.String getClassName();
method public java.util.List<java.util.Locale> getLocales();
- method public java.util.Map<java.util.Locale, java.lang.String> getNames();
+ method public java.lang.CharSequence getNameForLocale(java.util.Locale);
+ method public java.lang.String getServiceClassName();
method public java.lang.String getServiceId();
method public java.util.Date getSessionEndTime();
method public java.util.Date getSessionStartTime();
}
public class StreamingService {
- method public void dispose() throws android.telephony.mbms.MbmsException;
method public android.telephony.mbms.StreamingServiceInfo getInfo();
- method public android.net.Uri getPlaybackUri() throws android.telephony.mbms.MbmsException;
- method public void stopStreaming() throws android.telephony.mbms.MbmsException;
+ method public android.net.Uri getPlaybackUri();
+ method public void stopStreaming();
field public static final int BROADCAST_METHOD = 1; // 0x1
field public static final int REASON_BY_USER_REQUEST = 1; // 0x1
field public static final int REASON_END_OF_SESSION = 2; // 0x2
@@ -43984,22 +44089,62 @@ package android.telephony.mbms {
field public static final android.os.Parcelable.Creator<android.telephony.mbms.StreamingServiceInfo> CREATOR;
}
+ public final class UriPathPair implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.net.Uri getContentUri();
+ method public android.net.Uri getFilePathUri();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.mbms.UriPathPair> CREATOR;
+ }
+
}
package android.telephony.mbms.vendor {
+ public class MbmsDownloadServiceBase extends android.os.Binder {
+ ctor public MbmsDownloadServiceBase();
+ method public int cancelDownload(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
+ method public void dispose(int) throws android.os.RemoteException;
+ method public int download(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
+ method public int getDownloadStatus(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo) throws android.os.RemoteException;
+ method public int initialize(int, android.telephony.mbms.MbmsDownloadSessionCallback) throws android.os.RemoteException;
+ method public java.util.List<android.telephony.mbms.DownloadRequest> listPendingDownloads(int) throws android.os.RemoteException;
+ method public void onAppCallbackDied(int, int);
+ method public int registerStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback) throws android.os.RemoteException;
+ method public int requestUpdateFileServices(int, java.util.List<java.lang.String>) throws android.os.RemoteException;
+ method public int resetDownloadKnowledge(android.telephony.mbms.DownloadRequest) throws android.os.RemoteException;
+ method public int setTempFileRootDirectory(int, java.lang.String) throws android.os.RemoteException;
+ method public int unregisterStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback) throws android.os.RemoteException;
+ }
+
public class MbmsStreamingServiceBase extends android.os.Binder {
ctor public MbmsStreamingServiceBase();
method public void dispose(int) throws android.os.RemoteException;
- method public void disposeStream(int, java.lang.String) throws android.os.RemoteException;
method public android.net.Uri getPlaybackUri(int, java.lang.String) throws android.os.RemoteException;
- method public int getStreamingServices(int, java.util.List<java.lang.String>) throws android.os.RemoteException;
- method public int initialize(android.telephony.mbms.MbmsStreamingManagerCallback, int) throws android.os.RemoteException;
+ method public int initialize(android.telephony.mbms.MbmsStreamingSessionCallback, int) throws android.os.RemoteException;
method public void onAppCallbackDied(int, int);
+ method public int requestUpdateStreamingServices(int, java.util.List<java.lang.String>) throws android.os.RemoteException;
method public int startStreaming(int, java.lang.String, android.telephony.mbms.StreamingServiceCallback) throws android.os.RemoteException;
method public void stopStreaming(int, java.lang.String) throws android.os.RemoteException;
}
+ public class VendorUtils {
+ ctor public VendorUtils();
+ method public static android.content.ComponentName getAppReceiverFromPackageName(android.content.Context, java.lang.String);
+ field public static final java.lang.String ACTION_CLEANUP = "android.telephony.mbms.action.CLEANUP";
+ field public static final java.lang.String ACTION_DOWNLOAD_RESULT_INTERNAL = "android.telephony.mbms.action.DOWNLOAD_RESULT_INTERNAL";
+ field public static final java.lang.String ACTION_FILE_DESCRIPTOR_REQUEST = "android.telephony.mbms.action.FILE_DESCRIPTOR_REQUEST";
+ field public static final java.lang.String EXTRA_FD_COUNT = "android.telephony.mbms.extra.FD_COUNT";
+ field public static final java.lang.String EXTRA_FINAL_URI = "android.telephony.mbms.extra.FINAL_URI";
+ field public static final java.lang.String EXTRA_FREE_URI_LIST = "android.telephony.mbms.extra.FREE_URI_LIST";
+ field public static final java.lang.String EXTRA_PAUSED_LIST = "android.telephony.mbms.extra.PAUSED_LIST";
+ field public static final java.lang.String EXTRA_PAUSED_URI_LIST = "android.telephony.mbms.extra.PAUSED_URI_LIST";
+ field public static final java.lang.String EXTRA_SERVICE_ID = "android.telephony.mbms.extra.SERVICE_ID";
+ field public static final java.lang.String EXTRA_TEMP_FILES_IN_USE = "android.telephony.mbms.extra.TEMP_FILES_IN_USE";
+ field public static final java.lang.String EXTRA_TEMP_FILE_ROOT = "android.telephony.mbms.extra.TEMP_FILE_ROOT";
+ field public static final java.lang.String EXTRA_TEMP_LIST = "android.telephony.mbms.extra.TEMP_LIST";
+ }
+
}
package android.test {
diff --git a/api/test-current.txt b/api/test-current.txt
index 1dfca8ee14f3..5296a653da19 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -39569,6 +39569,7 @@ package android.telecom {
method public boolean isInManagedCall();
method public boolean isIncomingCallPermitted(android.telecom.PhoneAccountHandle);
method public boolean isOutgoingCallPermitted(android.telecom.PhoneAccountHandle);
+ method public boolean isTtySupported();
method public boolean isVoiceMailNumber(android.telecom.PhoneAccountHandle, java.lang.String);
method public void placeCall(android.net.Uri, android.os.Bundle);
method public void registerPhoneAccount(android.telecom.PhoneAccount);
@@ -39971,13 +39972,42 @@ package android.telephony {
field public static final int STATUS_UNKNOWN_ERROR = 4; // 0x4
}
- public class MbmsStreamingManager {
- method public static android.telephony.MbmsStreamingManager create(android.content.Context, android.telephony.mbms.MbmsStreamingManagerCallback, int, android.os.Handler) throws android.telephony.mbms.MbmsException;
- method public static android.telephony.MbmsStreamingManager create(android.content.Context, android.telephony.mbms.MbmsStreamingManagerCallback, android.os.Handler) throws android.telephony.mbms.MbmsException;
- method public static android.telephony.MbmsStreamingManager create(android.content.Context, android.telephony.mbms.MbmsStreamingManagerCallback) throws android.telephony.mbms.MbmsException;
- method public void dispose();
- method public void getStreamingServices(java.util.List<java.lang.String>) throws android.telephony.mbms.MbmsException;
- method public android.telephony.mbms.StreamingService startStreaming(android.telephony.mbms.StreamingServiceInfo, android.telephony.mbms.StreamingServiceCallback, android.os.Handler) throws android.telephony.mbms.MbmsException;
+ public class MbmsDownloadSession implements java.lang.AutoCloseable {
+ method public void cancelDownload(android.telephony.mbms.DownloadRequest);
+ method public void close();
+ method public static android.telephony.MbmsDownloadSession create(android.content.Context, android.telephony.mbms.MbmsDownloadSessionCallback, android.os.Handler);
+ method public static android.telephony.MbmsDownloadSession create(android.content.Context, android.telephony.mbms.MbmsDownloadSessionCallback, int, android.os.Handler);
+ method public void download(android.telephony.mbms.DownloadRequest);
+ method public int getDownloadStatus(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo);
+ method public java.io.File getTempFileRootDirectory();
+ method public java.util.List<android.telephony.mbms.DownloadRequest> listPendingDownloads();
+ method public void registerStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback, android.os.Handler);
+ method public void requestUpdateFileServices(java.util.List<java.lang.String>);
+ method public void resetDownloadKnowledge(android.telephony.mbms.DownloadRequest);
+ method public void setTempFileRootDirectory(java.io.File);
+ method public void unregisterStateCallback(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStateCallback);
+ field public static final java.lang.String DEFAULT_TOP_LEVEL_TEMP_DIRECTORY = "androidMbmsTempFileRoot";
+ field public static final java.lang.String EXTRA_MBMS_COMPLETED_FILE_URI = "android.telephony.extra.MBMS_COMPLETED_FILE_URI";
+ field public static final java.lang.String EXTRA_MBMS_DOWNLOAD_REQUEST = "android.telephony.extra.MBMS_DOWNLOAD_REQUEST";
+ field public static final java.lang.String EXTRA_MBMS_DOWNLOAD_RESULT = "android.telephony.extra.MBMS_DOWNLOAD_RESULT";
+ field public static final java.lang.String EXTRA_MBMS_FILE_INFO = "android.telephony.extra.MBMS_FILE_INFO";
+ field public static final int RESULT_CANCELLED = 2; // 0x2
+ field public static final int RESULT_EXPIRED = 3; // 0x3
+ field public static final int RESULT_IO_ERROR = 4; // 0x4
+ field public static final int RESULT_SUCCESSFUL = 1; // 0x1
+ field public static final int STATUS_ACTIVELY_DOWNLOADING = 1; // 0x1
+ field public static final int STATUS_PENDING_DOWNLOAD = 2; // 0x2
+ field public static final int STATUS_PENDING_DOWNLOAD_WINDOW = 4; // 0x4
+ field public static final int STATUS_PENDING_REPAIR = 3; // 0x3
+ field public static final int STATUS_UNKNOWN = 0; // 0x0
+ }
+
+ public class MbmsStreamingSession implements java.lang.AutoCloseable {
+ method public void close();
+ method public static android.telephony.MbmsStreamingSession create(android.content.Context, android.telephony.mbms.MbmsStreamingSessionCallback, int, android.os.Handler);
+ method public static android.telephony.MbmsStreamingSession create(android.content.Context, android.telephony.mbms.MbmsStreamingSessionCallback, android.os.Handler);
+ method public void requestUpdateStreamingServices(java.util.List<java.lang.String>);
+ method public android.telephony.mbms.StreamingService startStreaming(android.telephony.mbms.StreamingServiceInfo, android.telephony.mbms.StreamingServiceCallback, android.os.Handler);
}
public class NeighboringCellInfo implements android.os.Parcelable {
@@ -40010,8 +40040,10 @@ package android.telephony {
public class PhoneNumberUtils {
ctor public PhoneNumberUtils();
method public static void addTtsSpan(android.text.Spannable, int, int);
- method public static java.lang.String calledPartyBCDFragmentToString(byte[], int, int);
- method public static java.lang.String calledPartyBCDToString(byte[], int, int);
+ method public static deprecated java.lang.String calledPartyBCDFragmentToString(byte[], int, int);
+ method public static java.lang.String calledPartyBCDFragmentToString(byte[], int, int, int);
+ method public static deprecated java.lang.String calledPartyBCDToString(byte[], int, int);
+ method public static java.lang.String calledPartyBCDToString(byte[], int, int, int);
method public static boolean compare(java.lang.String, java.lang.String);
method public static boolean compare(android.content.Context, java.lang.String, java.lang.String);
method public static java.lang.String convertKeypadLettersToDigits(java.lang.String);
@@ -40044,12 +40076,15 @@ package android.telephony {
method public static byte[] networkPortionToCalledPartyBCD(java.lang.String);
method public static byte[] networkPortionToCalledPartyBCDWithLength(java.lang.String);
method public static java.lang.String normalizeNumber(java.lang.String);
- method public static byte[] numberToCalledPartyBCD(java.lang.String);
+ method public static deprecated byte[] numberToCalledPartyBCD(java.lang.String);
+ method public static byte[] numberToCalledPartyBCD(java.lang.String, int);
method public static java.lang.String replaceUnicodeDigits(java.lang.String);
method public static java.lang.String stringFromStringAndTOA(java.lang.String, int);
method public static java.lang.String stripSeparators(java.lang.String);
method public static java.lang.String toCallerIDMinMatch(java.lang.String);
method public static int toaFromString(java.lang.String);
+ field public static final int BCD_EXTENDED_TYPE_CALLED_PARTY = 2; // 0x2
+ field public static final int BCD_EXTENDED_TYPE_EF_ADN = 1; // 0x1
field public static final int FORMAT_JAPAN = 2; // 0x2
field public static final int FORMAT_NANP = 1; // 0x1
field public static final int FORMAT_UNKNOWN = 0; // 0x0
@@ -40332,6 +40367,7 @@ package android.telephony {
method public int getPhoneCount();
method public int getPhoneType();
method public android.telephony.ServiceState getServiceState();
+ method public android.telephony.SignalStrength getSignalStrength();
method public java.lang.String getSimCountryIso();
method public java.lang.String getSimOperator();
method public java.lang.String getSimOperatorName();
@@ -40357,7 +40393,7 @@ package android.telephony {
method public boolean isHearingAidCompatibilitySupported();
method public boolean isNetworkRoaming();
method public boolean isSmsCapable();
- method public boolean isTtyModeSupported();
+ method public deprecated boolean isTtyModeSupported();
method public boolean isVoiceCapable();
method public boolean isVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle);
method public boolean isWorldPhone();
@@ -40610,15 +40646,73 @@ package android.telephony.gsm {
package android.telephony.mbms {
- public class MbmsException extends java.lang.Exception {
- method public int getErrorCode();
+ public final class DownloadRequest implements android.os.Parcelable {
+ method public static android.telephony.mbms.DownloadRequest copy(android.telephony.mbms.DownloadRequest);
+ method public int describeContents();
+ method public java.lang.String getFileServiceId();
+ method public static int getMaxAppIntentSize();
+ method public static int getMaxDestinationUriSize();
+ method public android.net.Uri getSourceUri();
+ method public int getSubscriptionId();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.mbms.DownloadRequest> CREATOR;
+ }
+
+ public static class DownloadRequest.Builder {
+ ctor public DownloadRequest.Builder();
+ method public android.telephony.mbms.DownloadRequest build();
+ method public android.telephony.mbms.DownloadRequest.Builder setAppIntent(android.content.Intent);
+ method public android.telephony.mbms.DownloadRequest.Builder setServiceInfo(android.telephony.mbms.FileServiceInfo);
+ method public android.telephony.mbms.DownloadRequest.Builder setSource(android.net.Uri);
+ method public android.telephony.mbms.DownloadRequest.Builder setSubscriptionId(int);
+ }
+
+ public class DownloadStateCallback {
+ ctor public DownloadStateCallback();
+ method public void onProgressUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int, int, int, int);
+ method public void onStateUpdated(android.telephony.mbms.DownloadRequest, android.telephony.mbms.FileInfo, int);
+ }
+
+ public final class FileInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public java.lang.String getMimeType();
+ method public android.net.Uri getUri();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.mbms.FileInfo> CREATOR;
+ }
+
+ public final class FileServiceInfo extends android.telephony.mbms.ServiceInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method public java.util.List<android.telephony.mbms.FileInfo> getFiles();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.mbms.FileServiceInfo> CREATOR;
+ }
+
+ public class MbmsDownloadReceiver extends android.content.BroadcastReceiver {
+ ctor public MbmsDownloadReceiver();
+ method public void onReceive(android.content.Context, android.content.Intent);
+ }
+
+ public class MbmsDownloadSessionCallback {
+ ctor public MbmsDownloadSessionCallback();
+ method public void onError(int, java.lang.String);
+ method public void onFileServicesUpdated(java.util.List<android.telephony.mbms.FileServiceInfo>);
+ method public void onMiddlewareReady();
+ }
+
+ public class MbmsErrors {
field public static final int ERROR_MIDDLEWARE_LOST = 3; // 0x3
field public static final int ERROR_MIDDLEWARE_NOT_BOUND = 2; // 0x2
field public static final int ERROR_NO_UNIQUE_MIDDLEWARE = 1; // 0x1
field public static final int SUCCESS = 0; // 0x0
}
- public static class MbmsException.GeneralErrors {
+ public static class MbmsErrors.DownloadErrors {
+ field public static final int ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT = 401; // 0x191
+ field public static final int ERROR_UNKNOWN_DOWNLOAD_REQUEST = 402; // 0x192
+ }
+
+ public static class MbmsErrors.GeneralErrors {
field public static final int ERROR_CARRIER_CHANGE_NOT_ALLOWED = 207; // 0xcf
field public static final int ERROR_IN_E911 = 204; // 0xcc
field public static final int ERROR_MIDDLEWARE_NOT_YET_READY = 201; // 0xc9
@@ -40628,39 +40722,38 @@ package android.telephony.mbms {
field public static final int ERROR_UNABLE_TO_READ_SIM = 206; // 0xce
}
- public static class MbmsException.InitializationErrors {
+ public static class MbmsErrors.InitializationErrors {
field public static final int ERROR_APP_PERMISSIONS_NOT_GRANTED = 102; // 0x66
field public static final int ERROR_DUPLICATE_INITIALIZE = 101; // 0x65
field public static final int ERROR_UNABLE_TO_INITIALIZE = 103; // 0x67
}
- public static class MbmsException.StreamingErrors {
+ public static class MbmsErrors.StreamingErrors {
field public static final int ERROR_CONCURRENT_SERVICE_LIMIT_REACHED = 301; // 0x12d
field public static final int ERROR_DUPLICATE_START_STREAM = 303; // 0x12f
field public static final int ERROR_UNABLE_TO_START_SERVICE = 302; // 0x12e
}
- public class MbmsStreamingManagerCallback {
- ctor public MbmsStreamingManagerCallback();
+ public class MbmsStreamingSessionCallback {
+ ctor public MbmsStreamingSessionCallback();
method public void onError(int, java.lang.String);
method public void onMiddlewareReady();
method public void onStreamingServicesUpdated(java.util.List<android.telephony.mbms.StreamingServiceInfo>);
}
public class ServiceInfo {
- method public java.lang.String getClassName();
method public java.util.List<java.util.Locale> getLocales();
- method public java.util.Map<java.util.Locale, java.lang.String> getNames();
+ method public java.lang.CharSequence getNameForLocale(java.util.Locale);
+ method public java.lang.String getServiceClassName();
method public java.lang.String getServiceId();
method public java.util.Date getSessionEndTime();
method public java.util.Date getSessionStartTime();
}
public class StreamingService {
- method public void dispose() throws android.telephony.mbms.MbmsException;
method public android.telephony.mbms.StreamingServiceInfo getInfo();
- method public android.net.Uri getPlaybackUri() throws android.telephony.mbms.MbmsException;
- method public void stopStreaming() throws android.telephony.mbms.MbmsException;
+ method public android.net.Uri getPlaybackUri();
+ method public void stopStreaming();
field public static final int BROADCAST_METHOD = 1; // 0x1
field public static final int REASON_BY_USER_REQUEST = 1; // 0x1
field public static final int REASON_END_OF_SESSION = 2; // 0x2
diff --git a/cmds/am/Android.bp b/cmds/am/Android.bp
new file mode 100644
index 000000000000..7eb4edfecbf9
--- /dev/null
+++ b/cmds/am/Android.bp
@@ -0,0 +1,11 @@
+// Copyright 2008 The Android Open Source Project
+//
+
+cc_library_host_static {
+ name: "libinstrumentation",
+ srcs: ["**/*.proto"],
+ proto: {
+ type: "full",
+ export_proto_headers: true,
+ },
+}
diff --git a/cmds/am/Android.mk b/cmds/am/Android.mk
index 5586dd4e5b18..9411c3203ab8 100644
--- a/cmds/am/Android.mk
+++ b/cmds/am/Android.mk
@@ -16,14 +16,3 @@ LOCAL_SRC_FILES := am
LOCAL_MODULE_CLASS := EXECUTABLES
LOCAL_MODULE_TAGS := optional
include $(BUILD_PREBUILT)
-
-
-include $(CLEAR_VARS)
-LOCAL_SRC_FILES := \
- $(call all-proto-files-under, proto)
-LOCAL_MODULE := libinstrumentation
-LOCAL_PROTOC_OPTIMIZE_TYPE := full
-LOCAL_EXPORT_C_INCLUDE_DIRS := \
- $(call intermediates-dir-for,STATIC_LIBRARIES,libinstrumentation,HOST,,,)/proto/$(LOCAL_PATH)/proto
-include $(BUILD_HOST_STATIC_LIBRARY)
-
diff --git a/cmds/hid/Android.bp b/cmds/hid/Android.bp
new file mode 100644
index 000000000000..2b7963aa9425
--- /dev/null
+++ b/cmds/hid/Android.bp
@@ -0,0 +1 @@
+subdirs = ["jni"]
diff --git a/cmds/hid/jni/Android.bp b/cmds/hid/jni/Android.bp
new file mode 100644
index 000000000000..05c3099b27ff
--- /dev/null
+++ b/cmds/hid/jni/Android.bp
@@ -0,0 +1,18 @@
+cc_library_shared {
+ name: "libhidcommand_jni",
+
+ srcs: ["com_android_commands_hid_Device.cpp"],
+
+ shared_libs: [
+ "libandroid_runtime",
+ "liblog",
+ "libnativehelper",
+ "libutils",
+ ],
+
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+}
diff --git a/cmds/hid/jni/Android.mk b/cmds/hid/jni/Android.mk
deleted file mode 100644
index d41d39d27f5b..000000000000
--- a/cmds/hid/jni/Android.mk
+++ /dev/null
@@ -1,23 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
- com_android_commands_hid_Device.cpp
-
-LOCAL_C_INCLUDES := \
- $(JNI_H_INCLUDE) \
- frameworks/base/core/jni
-
-LOCAL_SHARED_LIBRARIES := \
- libandroid_runtime \
- liblog \
- libnativehelper \
- libutils
-
-LOCAL_MODULE := libhidcommand_jni
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_CFLAGS += -Wall -Wextra -Werror
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/cmds/idmap/create.cpp b/cmds/idmap/create.cpp
index 524db14f7aab..f415f8f5dd75 100644
--- a/cmds/idmap/create.cpp
+++ b/cmds/idmap/create.cpp
@@ -104,13 +104,17 @@ fail:
}
}
- uint32_t cached_target_crc, cached_overlay_crc;
+ uint32_t version, cached_target_crc, cached_overlay_crc;
String8 cached_target_path, cached_overlay_path;
- if (!ResTable::getIdmapInfo(buf, N, NULL, &cached_target_crc, &cached_overlay_crc,
+ if (!ResTable::getIdmapInfo(buf, N, &version, &cached_target_crc, &cached_overlay_crc,
&cached_target_path, &cached_overlay_path)) {
return true;
}
+ if (version != ResTable::IDMAP_CURRENT_VERSION) {
+ return true;
+ }
+
if (cached_target_path != target_apk_path) {
return true;
}
diff --git a/cmds/idmap/inspect.cpp b/cmds/idmap/inspect.cpp
index 154cb25a02a1..20005e2766d8 100644
--- a/cmds/idmap/inspect.cpp
+++ b/cmds/idmap/inspect.cpp
@@ -284,7 +284,9 @@ namespace {
if (err != NO_ERROR) {
return err;
}
- print("", "entry", data32, "%s/%s", type.string(), name.string());
+ if (data32 != ResTable_type::NO_ENTRY) {
+ print("", "entry", data32, "%s/%s", type.string(), name.string());
+ }
}
}
diff --git a/cmds/interrupter/Android.bp b/cmds/interrupter/Android.bp
new file mode 100644
index 000000000000..d68e7fe37535
--- /dev/null
+++ b/cmds/interrupter/Android.bp
@@ -0,0 +1,11 @@
+cc_library_shared {
+ name: "interrupter",
+ host_supported: true,
+ srcs: ["interrupter.c"],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wunused",
+ "-Wunreachable-code",
+ ],
+}
diff --git a/cmds/interrupter/Android.mk b/cmds/interrupter/Android.mk
deleted file mode 100644
index 97a96bfc8e25..000000000000
--- a/cmds/interrupter/Android.mk
+++ /dev/null
@@ -1,23 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
- interrupter.c
-LOCAL_MODULE := interrupter
-LOCAL_MODULE_TAGS := eng tests
-LOCAL_LDFLAGS := -ldl
-LOCAL_CFLAGS := -Wall -Werror -Wunused -Wunreachable-code
-
-include $(BUILD_SHARED_LIBRARY)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
- interrupter.c
-LOCAL_MODULE := interrupter
-LOCAL_MODULE_TAGS := eng tests
-LOCAL_LDFLAGS := -ldl
-LOCAL_CFLAGS := -Wall -Werror -Wunused -Wunreachable-code
-
-include $(BUILD_HOST_SHARED_LIBRARY)
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 6f326de76150..80554fff75ac 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -961,7 +961,7 @@ public class ResourcesManager {
// TODO(adamlesinski): Make this accept more than just overlay directories.
final void applyNewResourceDirsLocked(@NonNull final String baseCodePath,
- @NonNull final String[] newResourceDirs) {
+ @Nullable final String[] newResourceDirs) {
try {
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,
"ResourcesManager#applyNewResourceDirsLocked");
diff --git a/core/java/android/bluetooth/BluetoothA2dpSink.java b/core/java/android/bluetooth/BluetoothA2dpSink.java
index 611531c4f7c4..faab000a8997 100755
--- a/core/java/android/bluetooth/BluetoothA2dpSink.java
+++ b/core/java/android/bluetooth/BluetoothA2dpSink.java
@@ -125,7 +125,7 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
private Context mContext;
private ServiceListener mServiceListener;
- private IBluetoothA2dpSink mService;
+ private volatile IBluetoothA2dpSink mService;
private BluetoothAdapter mAdapter;
private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
@@ -240,15 +240,16 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
*/
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothA2dpSink service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.connect(device);
+ return service.connect(device);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -279,15 +280,16 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
*/
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothA2dpSink service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.disconnect(device);
+ return service.disconnect(device);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -297,15 +299,16 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
@Override
public List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
- if (mService != null && isEnabled()) {
+ final IBluetoothA2dpSink service = mService;
+ if (service != null && isEnabled()) {
try {
- return mService.getConnectedDevices();
+ return service.getConnectedDevices();
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return new ArrayList<BluetoothDevice>();
}
@@ -315,15 +318,16 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
@Override
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
- if (mService != null && isEnabled()) {
+ final IBluetoothA2dpSink service = mService;
+ if (service != null && isEnabled()) {
try {
- return mService.getDevicesMatchingConnectionStates(states);
+ return service.getDevicesMatchingConnectionStates(states);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return new ArrayList<BluetoothDevice>();
}
@@ -333,16 +337,16 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
@Override
public int getConnectionState(BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
- if (mService != null && isEnabled()
- && isValidDevice(device)) {
+ final IBluetoothA2dpSink service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.getConnectionState(device);
+ return service.getConnectionState(device);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return BluetoothProfile.STATE_DISCONNECTED;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return BluetoothProfile.STATE_DISCONNECTED;
}
@@ -359,16 +363,16 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
*/
public BluetoothAudioConfig getAudioConfig(BluetoothDevice device) {
if (VDBG) log("getAudioConfig(" + device + ")");
- if (mService != null && isEnabled()
- && isValidDevice(device)) {
+ final IBluetoothA2dpSink service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.getAudioConfig(device);
+ return service.getAudioConfig(device);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return null;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return null;
}
@@ -389,20 +393,20 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
*/
public boolean setPriority(BluetoothDevice device, int priority) {
if (DBG) log("setPriority(" + device + ", " + priority + ")");
- if (mService != null && isEnabled()
- && isValidDevice(device)) {
+ final IBluetoothA2dpSink service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
if (priority != BluetoothProfile.PRIORITY_OFF
&& priority != BluetoothProfile.PRIORITY_ON) {
return false;
}
try {
- return mService.setPriority(device, priority);
+ return service.setPriority(device, priority);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -421,16 +425,16 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
*/
public int getPriority(BluetoothDevice device) {
if (VDBG) log("getPriority(" + device + ")");
- if (mService != null && isEnabled()
- && isValidDevice(device)) {
+ final IBluetoothA2dpSink service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.getPriority(device);
+ return service.getPriority(device);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return BluetoothProfile.PRIORITY_OFF;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return BluetoothProfile.PRIORITY_OFF;
}
@@ -442,16 +446,16 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
* @param device BluetoothDevice device
*/
public boolean isA2dpPlaying(BluetoothDevice device) {
- if (mService != null && isEnabled()
- && isValidDevice(device)) {
+ final IBluetoothA2dpSink service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.isA2dpPlaying(device);
+ return service.isA2dpPlaying(device);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -485,7 +489,6 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
public void onServiceConnected(ComponentName className, IBinder service) {
if (DBG) Log.d(TAG, "Proxy object connected");
mService = IBluetoothA2dpSink.Stub.asInterface(Binder.allowBlocking(service));
-
if (mServiceListener != null) {
mServiceListener.onServiceConnected(BluetoothProfile.A2DP_SINK,
BluetoothA2dpSink.this);
@@ -502,15 +505,11 @@ public final class BluetoothA2dpSink implements BluetoothProfile {
};
private boolean isEnabled() {
- if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
- return false;
+ return mAdapter.getState() == BluetoothAdapter.STATE_ON;
}
- private boolean isValidDevice(BluetoothDevice device) {
- if (device == null) return false;
-
- if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
- return false;
+ private static boolean isValidDevice(BluetoothDevice device) {
+ return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
}
private static void log(String msg) {
diff --git a/core/java/android/bluetooth/BluetoothAvrcpController.java b/core/java/android/bluetooth/BluetoothAvrcpController.java
index 7528aa972109..5f0e5d97447e 100644
--- a/core/java/android/bluetooth/BluetoothAvrcpController.java
+++ b/core/java/android/bluetooth/BluetoothAvrcpController.java
@@ -81,7 +81,7 @@ public final class BluetoothAvrcpController implements BluetoothProfile {
private Context mContext;
private ServiceListener mServiceListener;
- private IBluetoothAvrcpController mService;
+ private volatile IBluetoothAvrcpController mService;
private BluetoothAdapter mAdapter;
private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
@@ -179,15 +179,16 @@ public final class BluetoothAvrcpController implements BluetoothProfile {
@Override
public List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
- if (mService != null && isEnabled()) {
+ final IBluetoothAvrcpController service = mService;
+ if (service != null && isEnabled()) {
try {
- return mService.getConnectedDevices();
+ return service.getConnectedDevices();
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return new ArrayList<BluetoothDevice>();
}
@@ -197,15 +198,16 @@ public final class BluetoothAvrcpController implements BluetoothProfile {
@Override
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
- if (mService != null && isEnabled()) {
+ final IBluetoothAvrcpController service = mService;
+ if (service != null && isEnabled()) {
try {
- return mService.getDevicesMatchingConnectionStates(states);
+ return service.getDevicesMatchingConnectionStates(states);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return new ArrayList<BluetoothDevice>();
}
@@ -215,16 +217,16 @@ public final class BluetoothAvrcpController implements BluetoothProfile {
@Override
public int getConnectionState(BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
- if (mService != null && isEnabled()
- && isValidDevice(device)) {
+ final IBluetoothAvrcpController service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.getConnectionState(device);
+ return service.getConnectionState(device);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return BluetoothProfile.STATE_DISCONNECTED;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return BluetoothProfile.STATE_DISCONNECTED;
}
@@ -236,9 +238,10 @@ public final class BluetoothAvrcpController implements BluetoothProfile {
public BluetoothAvrcpPlayerSettings getPlayerSettings(BluetoothDevice device) {
if (DBG) Log.d(TAG, "getPlayerSettings");
BluetoothAvrcpPlayerSettings settings = null;
- if (mService != null && isEnabled()) {
+ final IBluetoothAvrcpController service = mService;
+ if (service != null && isEnabled()) {
try {
- settings = mService.getPlayerSettings(device);
+ settings = service.getPlayerSettings(device);
} catch (RemoteException e) {
Log.e(TAG, "Error talking to BT service in getMetadata() " + e);
return null;
@@ -253,15 +256,16 @@ public final class BluetoothAvrcpController implements BluetoothProfile {
*/
public boolean setPlayerApplicationSetting(BluetoothAvrcpPlayerSettings plAppSetting) {
if (DBG) Log.d(TAG, "setPlayerApplicationSetting");
- if (mService != null && isEnabled()) {
+ final IBluetoothAvrcpController service = mService;
+ if (service != null && isEnabled()) {
try {
- return mService.setPlayerApplicationSetting(plAppSetting);
+ return service.setPlayerApplicationSetting(plAppSetting);
} catch (RemoteException e) {
Log.e(TAG, "Error talking to BT service in setPlayerApplicationSetting() " + e);
return false;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -272,23 +276,23 @@ public final class BluetoothAvrcpController implements BluetoothProfile {
public void sendGroupNavigationCmd(BluetoothDevice device, int keyCode, int keyState) {
Log.d(TAG, "sendGroupNavigationCmd dev = " + device + " key " + keyCode + " State = "
+ keyState);
- if (mService != null && isEnabled()) {
+ final IBluetoothAvrcpController service = mService;
+ if (service != null && isEnabled()) {
try {
- mService.sendGroupNavigationCmd(device, keyCode, keyState);
+ service.sendGroupNavigationCmd(device, keyCode, keyState);
return;
} catch (RemoteException e) {
Log.e(TAG, "Error talking to BT service in sendGroupNavigationCmd()", e);
return;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
}
private final ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
if (DBG) Log.d(TAG, "Proxy object connected");
mService = IBluetoothAvrcpController.Stub.asInterface(Binder.allowBlocking(service));
-
if (mServiceListener != null) {
mServiceListener.onServiceConnected(BluetoothProfile.AVRCP_CONTROLLER,
BluetoothAvrcpController.this);
@@ -305,15 +309,11 @@ public final class BluetoothAvrcpController implements BluetoothProfile {
};
private boolean isEnabled() {
- if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
- return false;
+ return mAdapter.getState() == BluetoothAdapter.STATE_ON;
}
- private boolean isValidDevice(BluetoothDevice device) {
- if (device == null) return false;
-
- if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
- return false;
+ private static boolean isValidDevice(BluetoothDevice device) {
+ return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
}
private static void log(String msg) {
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 3ab2c4a8f44d..d982bb7ffb43 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -712,7 +712,7 @@ public final class BluetoothDevice implements Parcelable {
* getService() called.
* TODO: Unify implementation of sService amongst BluetoothFoo API's
*/
- private static IBluetooth sService;
+ private static volatile IBluetooth sService;
private final String mAddress;
@@ -839,12 +839,13 @@ public final class BluetoothDevice implements Parcelable {
*/
@RequiresPermission(Manifest.permission.BLUETOOTH)
public String getName() {
- if (sService == null) {
+ final IBluetooth service = sService;
+ if (service == null) {
Log.e(TAG, "BT not enabled. Cannot get Remote Device name");
return null;
}
try {
- return sService.getRemoteName(this);
+ return service.getRemoteName(this);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -859,12 +860,13 @@ public final class BluetoothDevice implements Parcelable {
*/
@RequiresPermission(Manifest.permission.BLUETOOTH)
public int getType() {
- if (sService == null) {
+ final IBluetooth service = sService;
+ if (service == null) {
Log.e(TAG, "BT not enabled. Cannot get Remote Device type");
return DEVICE_TYPE_UNKNOWN;
}
try {
- return sService.getRemoteType(this);
+ return service.getRemoteType(this);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -879,12 +881,13 @@ public final class BluetoothDevice implements Parcelable {
* @hide
*/
public String getAlias() {
- if (sService == null) {
+ final IBluetooth service = sService;
+ if (service == null) {
Log.e(TAG, "BT not enabled. Cannot get Remote Device Alias");
return null;
}
try {
- return sService.getRemoteAlias(this);
+ return service.getRemoteAlias(this);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -902,12 +905,13 @@ public final class BluetoothDevice implements Parcelable {
* @hide
*/
public boolean setAlias(String alias) {
- if (sService == null) {
+ final IBluetooth service = sService;
+ if (service == null) {
Log.e(TAG, "BT not enabled. Cannot set Remote Device name");
return false;
}
try {
- return sService.setRemoteAlias(this, alias);
+ return service.setRemoteAlias(this, alias);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -942,12 +946,13 @@ public final class BluetoothDevice implements Parcelable {
*/
@RequiresPermission(Manifest.permission.BLUETOOTH)
public int getBatteryLevel() {
- if (sService == null) {
+ final IBluetooth service = sService;
+ if (service == null) {
Log.e(TAG, "Bluetooth disabled. Cannot get remote device battery level");
return BATTERY_LEVEL_UNKNOWN;
}
try {
- return sService.getBatteryLevel(this);
+ return service.getBatteryLevel(this);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -966,7 +971,8 @@ public final class BluetoothDevice implements Parcelable {
*/
@RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
public boolean createBond() {
- if (sService == null) {
+ final IBluetooth service = sService;
+ if (service == null) {
Log.e(TAG, "BT not enabled. Cannot create bond to Remote Device");
return false;
}
@@ -974,7 +980,7 @@ public final class BluetoothDevice implements Parcelable {
Log.i(TAG, "createBond() for device " + getAddress()
+ " called by pid: " + Process.myPid()
+ " tid: " + Process.myTid());
- return sService.createBond(this, TRANSPORT_AUTO);
+ return service.createBond(this, TRANSPORT_AUTO);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -998,7 +1004,8 @@ public final class BluetoothDevice implements Parcelable {
* @hide
*/
public boolean createBond(int transport) {
- if (sService == null) {
+ final IBluetooth service = sService;
+ if (service == null) {
Log.e(TAG, "BT not enabled. Cannot create bond to Remote Device");
return false;
}
@@ -1009,7 +1016,7 @@ public final class BluetoothDevice implements Parcelable {
Log.i(TAG, "createBond() for device " + getAddress()
+ " called by pid: " + Process.myPid()
+ " tid: " + Process.myTid());
- return sService.createBond(this, transport);
+ return service.createBond(this, transport);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1035,8 +1042,13 @@ public final class BluetoothDevice implements Parcelable {
* @hide
*/
public boolean createBondOutOfBand(int transport, OobData oobData) {
+ final IBluetooth service = sService;
+ if (service == null) {
+ Log.w(TAG, "BT not enabled, createBondOutOfBand failed");
+ return false;
+ }
try {
- return sService.createBondOutOfBand(this, transport, oobData);
+ return service.createBondOutOfBand(this, transport, oobData);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1045,8 +1057,13 @@ public final class BluetoothDevice implements Parcelable {
/** @hide */
public boolean isBondingInitiatedLocally() {
+ final IBluetooth service = sService;
+ if (service == null) {
+ Log.w(TAG, "BT not enabled, isBondingInitiatedLocally failed");
+ return false;
+ }
try {
- return sService.isBondingInitiatedLocally(this);
+ return service.isBondingInitiatedLocally(this);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1082,7 +1099,8 @@ public final class BluetoothDevice implements Parcelable {
* @hide
*/
public boolean cancelBondProcess() {
- if (sService == null) {
+ final IBluetooth service = sService;
+ if (service == null) {
Log.e(TAG, "BT not enabled. Cannot cancel Remote Device bond");
return false;
}
@@ -1090,7 +1108,7 @@ public final class BluetoothDevice implements Parcelable {
Log.i(TAG, "cancelBondProcess() for device " + getAddress()
+ " called by pid: " + Process.myPid()
+ " tid: " + Process.myTid());
- return sService.cancelBondProcess(this);
+ return service.cancelBondProcess(this);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1108,7 +1126,8 @@ public final class BluetoothDevice implements Parcelable {
* @hide
*/
public boolean removeBond() {
- if (sService == null) {
+ final IBluetooth service = sService;
+ if (service == null) {
Log.e(TAG, "BT not enabled. Cannot remove Remote Device bond");
return false;
}
@@ -1116,7 +1135,7 @@ public final class BluetoothDevice implements Parcelable {
Log.i(TAG, "removeBond() for device " + getAddress()
+ " called by pid: " + Process.myPid()
+ " tid: " + Process.myTid());
- return sService.removeBond(this);
+ return service.removeBond(this);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1134,19 +1153,15 @@ public final class BluetoothDevice implements Parcelable {
*/
@RequiresPermission(Manifest.permission.BLUETOOTH)
public int getBondState() {
- if (sService == null) {
+ final IBluetooth service = sService;
+ if (service == null) {
Log.e(TAG, "BT not enabled. Cannot get bond state");
return BOND_NONE;
}
try {
- return sService.getBondState(this);
+ return service.getBondState(this);
} catch (RemoteException e) {
Log.e(TAG, "", e);
- } catch (NullPointerException npe) {
- // Handle case where bluetooth service proxy
- // is already null.
- Log.e(TAG, "NullPointerException for getBondState() of device ("
- + getAddress() + ")", npe);
}
return BOND_NONE;
}
@@ -1160,12 +1175,13 @@ public final class BluetoothDevice implements Parcelable {
*/
@SystemApi
public boolean isConnected() {
- if (sService == null) {
+ final IBluetooth service = sService;
+ if (service == null) {
// BT is not enabled, we cannot be connected.
return false;
}
try {
- return sService.getConnectionState(this) != CONNECTION_STATE_DISCONNECTED;
+ return service.getConnectionState(this) != CONNECTION_STATE_DISCONNECTED;
} catch (RemoteException e) {
Log.e(TAG, "", e);
return false;
@@ -1182,12 +1198,13 @@ public final class BluetoothDevice implements Parcelable {
*/
@SystemApi
public boolean isEncrypted() {
- if (sService == null) {
+ final IBluetooth service = sService;
+ if (service == null) {
// BT is not enabled, we cannot be connected.
return false;
}
try {
- return sService.getConnectionState(this) > CONNECTION_STATE_CONNECTED;
+ return service.getConnectionState(this) > CONNECTION_STATE_CONNECTED;
} catch (RemoteException e) {
Log.e(TAG, "", e);
return false;
@@ -1201,12 +1218,13 @@ public final class BluetoothDevice implements Parcelable {
*/
@RequiresPermission(Manifest.permission.BLUETOOTH)
public BluetoothClass getBluetoothClass() {
- if (sService == null) {
+ final IBluetooth service = sService;
+ if (service == null) {
Log.e(TAG, "BT not enabled. Cannot get Bluetooth Class");
return null;
}
try {
- int classInt = sService.getRemoteClass(this);
+ int classInt = service.getRemoteClass(this);
if (classInt == BluetoothClass.ERROR) return null;
return new BluetoothClass(classInt);
} catch (RemoteException e) {
@@ -1227,12 +1245,13 @@ public final class BluetoothDevice implements Parcelable {
*/
@RequiresPermission(Manifest.permission.BLUETOOTH)
public ParcelUuid[] getUuids() {
- if (sService == null || !isBluetoothEnabled()) {
+ final IBluetooth service = sService;
+ if (service == null || !isBluetoothEnabled()) {
Log.e(TAG, "BT not enabled. Cannot get remote device Uuids");
return null;
}
try {
- return sService.getRemoteUuids(this);
+ return service.getRemoteUuids(this);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1254,7 +1273,7 @@ public final class BluetoothDevice implements Parcelable {
*/
@RequiresPermission(Manifest.permission.BLUETOOTH)
public boolean fetchUuidsWithSdp() {
- IBluetooth service = sService;
+ final IBluetooth service = sService;
if (service == null || !isBluetoothEnabled()) {
Log.e(TAG, "BT not enabled. Cannot fetchUuidsWithSdp");
return false;
@@ -1289,12 +1308,13 @@ public final class BluetoothDevice implements Parcelable {
*/
/** @hide */
public boolean sdpSearch(ParcelUuid uuid) {
- if (sService == null) {
+ final IBluetooth service = sService;
+ if (service == null) {
Log.e(TAG, "BT not enabled. Cannot query remote device sdp records");
return false;
}
try {
- return sService.sdpSearch(this, uuid);
+ return service.sdpSearch(this, uuid);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1308,12 +1328,13 @@ public final class BluetoothDevice implements Parcelable {
* @return true pin has been set false for error
*/
public boolean setPin(byte[] pin) {
- if (sService == null) {
+ final IBluetooth service = sService;
+ if (service == null) {
Log.e(TAG, "BT not enabled. Cannot set Remote Device pin");
return false;
}
try {
- return sService.setPin(this, true, pin.length, pin);
+ return service.setPin(this, true, pin.length, pin);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1337,12 +1358,13 @@ public final class BluetoothDevice implements Parcelable {
*/
@RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
public boolean setPairingConfirmation(boolean confirm) {
- if (sService == null) {
+ final IBluetooth service = sService;
+ if (service == null) {
Log.e(TAG, "BT not enabled. Cannot set pairing confirmation");
return false;
}
try {
- return sService.setPairingConfirmation(this, confirm);
+ return service.setPairingConfirmation(this, confirm);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1361,12 +1383,13 @@ public final class BluetoothDevice implements Parcelable {
/** @hide */
public boolean cancelPairingUserInput() {
- if (sService == null) {
+ final IBluetooth service = sService;
+ if (service == null) {
Log.e(TAG, "BT not enabled. Cannot create pairing user input");
return false;
}
try {
- return sService.cancelBondProcess(this);
+ return service.cancelBondProcess(this);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1400,11 +1423,12 @@ public final class BluetoothDevice implements Parcelable {
* @hide
*/
public int getPhonebookAccessPermission() {
- if (sService == null) {
+ final IBluetooth service = sService;
+ if (service == null) {
return ACCESS_UNKNOWN;
}
try {
- return sService.getPhonebookAccessPermission(this);
+ return service.getPhonebookAccessPermission(this);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1421,11 +1445,12 @@ public final class BluetoothDevice implements Parcelable {
* @hide
*/
public boolean setPhonebookAccessPermission(int value) {
- if (sService == null) {
+ final IBluetooth service = sService;
+ if (service == null) {
return false;
}
try {
- return sService.setPhonebookAccessPermission(this, value);
+ return service.setPhonebookAccessPermission(this, value);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1440,11 +1465,12 @@ public final class BluetoothDevice implements Parcelable {
* @hide
*/
public int getMessageAccessPermission() {
- if (sService == null) {
+ final IBluetooth service = sService;
+ if (service == null) {
return ACCESS_UNKNOWN;
}
try {
- return sService.getMessageAccessPermission(this);
+ return service.getMessageAccessPermission(this);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1461,11 +1487,12 @@ public final class BluetoothDevice implements Parcelable {
* @hide
*/
public boolean setMessageAccessPermission(int value) {
- if (sService == null) {
+ final IBluetooth service = sService;
+ if (service == null) {
return false;
}
try {
- return sService.setMessageAccessPermission(this, value);
+ return service.setMessageAccessPermission(this, value);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1480,11 +1507,12 @@ public final class BluetoothDevice implements Parcelable {
* @hide
*/
public int getSimAccessPermission() {
- if (sService == null) {
+ final IBluetooth service = sService;
+ if (service == null) {
return ACCESS_UNKNOWN;
}
try {
- return sService.getSimAccessPermission(this);
+ return service.getSimAccessPermission(this);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
@@ -1501,11 +1529,12 @@ public final class BluetoothDevice implements Parcelable {
* @hide
*/
public boolean setSimAccessPermission(int value) {
- if (sService == null) {
+ final IBluetooth service = sService;
+ if (service == null) {
return false;
}
try {
- return sService.setSimAccessPermission(this, value);
+ return service.setSimAccessPermission(this, value);
} catch (RemoteException e) {
Log.e(TAG, "", e);
}
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index 759d772920ba..a2af3422eaec 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -42,7 +42,7 @@ public final class BluetoothGatt implements BluetoothProfile {
private static final boolean VDBG = false;
private IBluetoothGatt mService;
- private BluetoothGattCallback mCallback;
+ private volatile BluetoothGattCallback mCallback;
private Handler mHandler;
private int mClientIf;
private BluetoothDevice mDevice;
@@ -164,8 +164,9 @@ public final class BluetoothGatt implements BluetoothProfile {
runOrQueueCallback(new Runnable() {
@Override
public void run() {
- if (mCallback != null) {
- mCallback.onConnectionStateChange(BluetoothGatt.this,
+ final BluetoothGattCallback callback = mCallback;
+ if (callback != null) {
+ callback.onConnectionStateChange(BluetoothGatt.this,
GATT_FAILURE,
BluetoothProfile.STATE_DISCONNECTED);
}
@@ -203,8 +204,9 @@ public final class BluetoothGatt implements BluetoothProfile {
runOrQueueCallback(new Runnable() {
@Override
public void run() {
- if (mCallback != null) {
- mCallback.onPhyUpdate(BluetoothGatt.this, txPhy, rxPhy, status);
+ final BluetoothGattCallback callback = mCallback;
+ if (callback != null) {
+ callback.onPhyUpdate(BluetoothGatt.this, txPhy, rxPhy, status);
}
}
});
@@ -227,8 +229,9 @@ public final class BluetoothGatt implements BluetoothProfile {
runOrQueueCallback(new Runnable() {
@Override
public void run() {
- if (mCallback != null) {
- mCallback.onPhyRead(BluetoothGatt.this, txPhy, rxPhy, status);
+ final BluetoothGattCallback callback = mCallback;
+ if (callback != null) {
+ callback.onPhyRead(BluetoothGatt.this, txPhy, rxPhy, status);
}
}
});
@@ -254,8 +257,9 @@ public final class BluetoothGatt implements BluetoothProfile {
runOrQueueCallback(new Runnable() {
@Override
public void run() {
- if (mCallback != null) {
- mCallback.onConnectionStateChange(BluetoothGatt.this, status,
+ final BluetoothGattCallback callback = mCallback;
+ if (callback != null) {
+ callback.onConnectionStateChange(BluetoothGatt.this, status,
profileState);
}
}
@@ -307,8 +311,7 @@ public final class BluetoothGatt implements BluetoothProfile {
for (BluetoothGattService brokenRef : includedServices) {
BluetoothGattService includedService = getService(mDevice,
- brokenRef.getUuid(), brokenRef.getInstanceId(),
- brokenRef.getType());
+ brokenRef.getUuid(), brokenRef.getInstanceId());
if (includedService != null) {
fixedService.addIncludedService(includedService);
} else {
@@ -320,8 +323,9 @@ public final class BluetoothGatt implements BluetoothProfile {
runOrQueueCallback(new Runnable() {
@Override
public void run() {
- if (mCallback != null) {
- mCallback.onServicesDiscovered(BluetoothGatt.this, status);
+ final BluetoothGattCallback callback = mCallback;
+ if (callback != null) {
+ callback.onServicesDiscovered(BluetoothGatt.this, status);
}
}
});
@@ -371,13 +375,13 @@ public final class BluetoothGatt implements BluetoothProfile {
return;
}
- if (status == 0) characteristic.setValue(value);
-
runOrQueueCallback(new Runnable() {
@Override
public void run() {
- if (mCallback != null) {
- mCallback.onCharacteristicRead(BluetoothGatt.this, characteristic,
+ final BluetoothGattCallback callback = mCallback;
+ if (callback != null) {
+ if (status == 0) characteristic.setValue(value);
+ callback.onCharacteristicRead(BluetoothGatt.this, characteristic,
status);
}
}
@@ -429,8 +433,9 @@ public final class BluetoothGatt implements BluetoothProfile {
runOrQueueCallback(new Runnable() {
@Override
public void run() {
- if (mCallback != null) {
- mCallback.onCharacteristicWrite(BluetoothGatt.this, characteristic,
+ final BluetoothGattCallback callback = mCallback;
+ if (callback != null) {
+ callback.onCharacteristicWrite(BluetoothGatt.this, characteristic,
status);
}
}
@@ -454,13 +459,13 @@ public final class BluetoothGatt implements BluetoothProfile {
handle);
if (characteristic == null) return;
- characteristic.setValue(value);
-
runOrQueueCallback(new Runnable() {
@Override
public void run() {
- if (mCallback != null) {
- mCallback.onCharacteristicChanged(BluetoothGatt.this,
+ final BluetoothGattCallback callback = mCallback;
+ if (callback != null) {
+ characteristic.setValue(value);
+ callback.onCharacteristicChanged(BluetoothGatt.this,
characteristic);
}
}
@@ -489,7 +494,6 @@ public final class BluetoothGatt implements BluetoothProfile {
BluetoothGattDescriptor descriptor = getDescriptorById(mDevice, handle);
if (descriptor == null) return;
- if (status == 0) descriptor.setValue(value);
if ((status == GATT_INSUFFICIENT_AUTHENTICATION
|| status == GATT_INSUFFICIENT_ENCRYPTION)
@@ -510,8 +514,10 @@ public final class BluetoothGatt implements BluetoothProfile {
runOrQueueCallback(new Runnable() {
@Override
public void run() {
- if (mCallback != null) {
- mCallback.onDescriptorRead(BluetoothGatt.this, descriptor, status);
+ final BluetoothGattCallback callback = mCallback;
+ if (callback != null) {
+ if (status == 0) descriptor.setValue(value);
+ callback.onDescriptorRead(BluetoothGatt.this, descriptor, status);
}
}
});
@@ -559,8 +565,9 @@ public final class BluetoothGatt implements BluetoothProfile {
runOrQueueCallback(new Runnable() {
@Override
public void run() {
- if (mCallback != null) {
- mCallback.onDescriptorWrite(BluetoothGatt.this, descriptor, status);
+ final BluetoothGattCallback callback = mCallback;
+ if (callback != null) {
+ callback.onDescriptorWrite(BluetoothGatt.this, descriptor, status);
}
}
});
@@ -587,8 +594,9 @@ public final class BluetoothGatt implements BluetoothProfile {
runOrQueueCallback(new Runnable() {
@Override
public void run() {
- if (mCallback != null) {
- mCallback.onReliableWriteCompleted(BluetoothGatt.this, status);
+ final BluetoothGattCallback callback = mCallback;
+ if (callback != null) {
+ callback.onReliableWriteCompleted(BluetoothGatt.this, status);
}
}
});
@@ -610,8 +618,9 @@ public final class BluetoothGatt implements BluetoothProfile {
runOrQueueCallback(new Runnable() {
@Override
public void run() {
- if (mCallback != null) {
- mCallback.onReadRemoteRssi(BluetoothGatt.this, rssi, status);
+ final BluetoothGattCallback callback = mCallback;
+ if (callback != null) {
+ callback.onReadRemoteRssi(BluetoothGatt.this, rssi, status);
}
}
});
@@ -634,8 +643,9 @@ public final class BluetoothGatt implements BluetoothProfile {
runOrQueueCallback(new Runnable() {
@Override
public void run() {
- if (mCallback != null) {
- mCallback.onMtuChanged(BluetoothGatt.this, mtu, status);
+ final BluetoothGattCallback callback = mCallback;
+ if (callback != null) {
+ callback.onMtuChanged(BluetoothGatt.this, mtu, status);
}
}
});
@@ -660,8 +670,9 @@ public final class BluetoothGatt implements BluetoothProfile {
runOrQueueCallback(new Runnable() {
@Override
public void run() {
- if (mCallback != null) {
- mCallback.onConnectionUpdated(BluetoothGatt.this, interval, latency,
+ final BluetoothGattCallback callback = mCallback;
+ if (callback != null) {
+ callback.onConnectionUpdated(BluetoothGatt.this, interval, latency,
timeout, status);
}
}
@@ -702,10 +713,9 @@ public final class BluetoothGatt implements BluetoothProfile {
* @hide
*/
/*package*/ BluetoothGattService getService(BluetoothDevice device, UUID uuid,
- int instanceId, int type) {
+ int instanceId) {
for (BluetoothGattService svc : mServices) {
if (svc.getDevice().equals(device)
- && svc.getType() == type
&& svc.getInstanceId() == instanceId
&& svc.getUuid().equals(uuid)) {
return svc;
@@ -901,7 +911,7 @@ public final class BluetoothGatt implements BluetoothProfile {
/**
* Set the preferred connection PHY for this app. Please note that this is just a
- * recommendation, whether the PHY change will happen depends on other applications peferences,
+ * recommendation, whether the PHY change will happen depends on other applications preferences,
* local and remote controller capabilities. Controller can override these settings.
* <p>
* {@link BluetoothGattCallback#onPhyUpdate} will be triggered as a result of this call, even
diff --git a/core/java/android/bluetooth/BluetoothGattServerCallback.java b/core/java/android/bluetooth/BluetoothGattServerCallback.java
index 22eba351b361..2c8114be3fe3 100644
--- a/core/java/android/bluetooth/BluetoothGattServerCallback.java
+++ b/core/java/android/bluetooth/BluetoothGattServerCallback.java
@@ -184,7 +184,7 @@ public abstract class BluetoothGattServerCallback {
/**
* Callback indicating the connection parameters were updated.
*
- * @param gatt The remote device involved
+ * @param device The remote device involved
* @param interval Connection interval used on this connection, 1.25ms unit. Valid range is from
* 6 (7.5ms) to 3200 (4000ms).
* @param latency Slave latency for the connection in number of connection events. Valid range
@@ -195,7 +195,7 @@ public abstract class BluetoothGattServerCallback {
* successfully
* @hide
*/
- public void onConnectionUpdated(BluetoothDevice gatt, int interval, int latency, int timeout,
+ public void onConnectionUpdated(BluetoothDevice device, int interval, int latency, int timeout,
int status) {
}
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index be1ce63cadc2..85550c7720a6 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -306,7 +306,7 @@ public final class BluetoothHeadset implements BluetoothProfile {
private Context mContext;
private ServiceListener mServiceListener;
- private IBluetoothHeadset mService;
+ private volatile IBluetoothHeadset mService;
private BluetoothAdapter mAdapter;
private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
@@ -418,15 +418,16 @@ public final class BluetoothHeadset implements BluetoothProfile {
*/
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadset service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.connect(device);
+ return service.connect(device);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return false;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -457,15 +458,16 @@ public final class BluetoothHeadset implements BluetoothProfile {
*/
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadset service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.disconnect(device);
+ return service.disconnect(device);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return false;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -475,15 +477,16 @@ public final class BluetoothHeadset implements BluetoothProfile {
@Override
public List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
- if (mService != null && isEnabled()) {
+ final IBluetoothHeadset service = mService;
+ if (service != null && isEnabled()) {
try {
- return mService.getConnectedDevices();
+ return service.getConnectedDevices();
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return new ArrayList<BluetoothDevice>();
}
@@ -493,15 +496,16 @@ public final class BluetoothHeadset implements BluetoothProfile {
@Override
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
- if (mService != null && isEnabled()) {
+ final IBluetoothHeadset service = mService;
+ if (service != null && isEnabled()) {
try {
- return mService.getDevicesMatchingConnectionStates(states);
+ return service.getDevicesMatchingConnectionStates(states);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return new ArrayList<BluetoothDevice>();
}
@@ -511,15 +515,16 @@ public final class BluetoothHeadset implements BluetoothProfile {
@Override
public int getConnectionState(BluetoothDevice device) {
if (VDBG) log("getConnectionState(" + device + ")");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadset service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.getConnectionState(device);
+ return service.getConnectionState(device);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return BluetoothProfile.STATE_DISCONNECTED;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return BluetoothProfile.STATE_DISCONNECTED;
}
@@ -540,19 +545,20 @@ public final class BluetoothHeadset implements BluetoothProfile {
*/
public boolean setPriority(BluetoothDevice device, int priority) {
if (DBG) log("setPriority(" + device + ", " + priority + ")");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadset service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
if (priority != BluetoothProfile.PRIORITY_OFF
&& priority != BluetoothProfile.PRIORITY_ON) {
return false;
}
try {
- return mService.setPriority(device, priority);
+ return service.setPriority(device, priority);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return false;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -571,15 +577,16 @@ public final class BluetoothHeadset implements BluetoothProfile {
*/
public int getPriority(BluetoothDevice device) {
if (VDBG) log("getPriority(" + device + ")");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadset service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.getPriority(device);
+ return service.getPriority(device);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return PRIORITY_OFF;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return PRIORITY_OFF;
}
@@ -605,14 +612,15 @@ public final class BluetoothHeadset implements BluetoothProfile {
*/
public boolean startVoiceRecognition(BluetoothDevice device) {
if (DBG) log("startVoiceRecognition()");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadset service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.startVoiceRecognition(device);
+ return service.startVoiceRecognition(device);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -627,14 +635,15 @@ public final class BluetoothHeadset implements BluetoothProfile {
*/
public boolean stopVoiceRecognition(BluetoothDevice device) {
if (DBG) log("stopVoiceRecognition()");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadset service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.stopVoiceRecognition(device);
+ return service.stopVoiceRecognition(device);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -648,14 +657,15 @@ public final class BluetoothHeadset implements BluetoothProfile {
*/
public boolean isAudioConnected(BluetoothDevice device) {
if (VDBG) log("isAudioConnected()");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadset service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.isAudioConnected(device);
+ return service.isAudioConnected(device);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -674,14 +684,15 @@ public final class BluetoothHeadset implements BluetoothProfile {
*/
public int getBatteryUsageHint(BluetoothDevice device) {
if (VDBG) log("getBatteryUsageHint()");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadset service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.getBatteryUsageHint(device);
+ return service.getBatteryUsageHint(device);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return -1;
}
@@ -704,9 +715,10 @@ public final class BluetoothHeadset implements BluetoothProfile {
*/
public boolean acceptIncomingConnect(BluetoothDevice device) {
if (DBG) log("acceptIncomingConnect");
- if (mService != null && isEnabled()) {
+ final IBluetoothHeadset service = mService;
+ if (service != null && isEnabled()) {
try {
- return mService.acceptIncomingConnect(device);
+ return service.acceptIncomingConnect(device);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -724,9 +736,10 @@ public final class BluetoothHeadset implements BluetoothProfile {
*/
public boolean rejectIncomingConnect(BluetoothDevice device) {
if (DBG) log("rejectIncomingConnect");
- if (mService != null) {
+ final IBluetoothHeadset service = mService;
+ if (service != null) {
try {
- return mService.rejectIncomingConnect(device);
+ return service.rejectIncomingConnect(device);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -745,9 +758,10 @@ public final class BluetoothHeadset implements BluetoothProfile {
*/
public int getAudioState(BluetoothDevice device) {
if (VDBG) log("getAudioState");
- if (mService != null && !isDisabled()) {
+ final IBluetoothHeadset service = mService;
+ if (service != null && !isDisabled()) {
try {
- return mService.getAudioState(device);
+ return service.getAudioState(device);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -770,9 +784,10 @@ public final class BluetoothHeadset implements BluetoothProfile {
*/
public void setAudioRouteAllowed(boolean allowed) {
if (VDBG) log("setAudioRouteAllowed");
- if (mService != null && isEnabled()) {
+ final IBluetoothHeadset service = mService;
+ if (service != null && isEnabled()) {
try {
- mService.setAudioRouteAllowed(allowed);
+ service.setAudioRouteAllowed(allowed);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -790,9 +805,10 @@ public final class BluetoothHeadset implements BluetoothProfile {
*/
public boolean getAudioRouteAllowed() {
if (VDBG) log("getAudioRouteAllowed");
- if (mService != null && isEnabled()) {
+ final IBluetoothHeadset service = mService;
+ if (service != null && isEnabled()) {
try {
- return mService.getAudioRouteAllowed();
+ return service.getAudioRouteAllowed();
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -812,9 +828,10 @@ public final class BluetoothHeadset implements BluetoothProfile {
*/
public void setForceScoAudio(boolean forced) {
if (VDBG) log("setForceScoAudio " + String.valueOf(forced));
- if (mService != null && isEnabled()) {
+ final IBluetoothHeadset service = mService;
+ if (service != null && isEnabled()) {
try {
- mService.setForceScoAudio(forced);
+ service.setForceScoAudio(forced);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -834,14 +851,15 @@ public final class BluetoothHeadset implements BluetoothProfile {
*/
public boolean isAudioOn() {
if (VDBG) log("isAudioOn()");
- if (mService != null && isEnabled()) {
+ final IBluetoothHeadset service = mService;
+ if (service != null && isEnabled()) {
try {
- return mService.isAudioOn();
+ return service.isAudioOn();
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -855,9 +873,10 @@ public final class BluetoothHeadset implements BluetoothProfile {
* @hide
*/
public boolean connectAudio() {
- if (mService != null && isEnabled()) {
+ final IBluetoothHeadset service = mService;
+ if (service != null && isEnabled()) {
try {
- return mService.connectAudio();
+ return service.connectAudio();
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -877,9 +896,10 @@ public final class BluetoothHeadset implements BluetoothProfile {
* @hide
*/
public boolean disconnectAudio() {
- if (mService != null && isEnabled()) {
+ final IBluetoothHeadset service = mService;
+ if (service != null && isEnabled()) {
try {
- return mService.disconnectAudio();
+ return service.disconnectAudio();
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -903,9 +923,10 @@ public final class BluetoothHeadset implements BluetoothProfile {
*/
public boolean startScoUsingVirtualVoiceCall(BluetoothDevice device) {
if (DBG) log("startScoUsingVirtualVoiceCall()");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadset service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.startScoUsingVirtualVoiceCall(device);
+ return service.startScoUsingVirtualVoiceCall(device);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -926,9 +947,10 @@ public final class BluetoothHeadset implements BluetoothProfile {
*/
public boolean stopScoUsingVirtualVoiceCall(BluetoothDevice device) {
if (DBG) log("stopScoUsingVirtualVoiceCall()");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadset service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.stopScoUsingVirtualVoiceCall(device);
+ return service.stopScoUsingVirtualVoiceCall(device);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -949,9 +971,10 @@ public final class BluetoothHeadset implements BluetoothProfile {
*/
public void phoneStateChanged(int numActive, int numHeld, int callState, String number,
int type) {
- if (mService != null && isEnabled()) {
+ final IBluetoothHeadset service = mService;
+ if (service != null && isEnabled()) {
try {
- mService.phoneStateChanged(numActive, numHeld, callState, number, type);
+ service.phoneStateChanged(numActive, numHeld, callState, number, type);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -968,9 +991,10 @@ public final class BluetoothHeadset implements BluetoothProfile {
*/
public void clccResponse(int index, int direction, int status, int mode, boolean mpty,
String number, int type) {
- if (mService != null && isEnabled()) {
+ final IBluetoothHeadset service = mService;
+ if (service != null && isEnabled()) {
try {
- mService.clccResponse(index, direction, status, mode, mpty, number, type);
+ service.clccResponse(index, direction, status, mode, mpty, number, type);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -1006,14 +1030,15 @@ public final class BluetoothHeadset implements BluetoothProfile {
if (command == null) {
throw new IllegalArgumentException("command is null");
}
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadset service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.sendVendorSpecificResultCode(device, command, arg);
+ return service.sendVendorSpecificResultCode(device, command, arg);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
}
- if (mService == null) {
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
}
return false;
@@ -1027,9 +1052,10 @@ public final class BluetoothHeadset implements BluetoothProfile {
* @hide
*/
public boolean enableWBS() {
- if (mService != null && isEnabled()) {
+ final IBluetoothHeadset service = mService;
+ if (service != null && isEnabled()) {
try {
- return mService.enableWBS();
+ return service.enableWBS();
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -1048,9 +1074,10 @@ public final class BluetoothHeadset implements BluetoothProfile {
* @hide
*/
public boolean disableWBS() {
- if (mService != null && isEnabled()) {
+ final IBluetoothHeadset service = mService;
+ if (service != null && isEnabled()) {
try {
- return mService.disableWBS();
+ return service.disableWBS();
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -1083,9 +1110,10 @@ public final class BluetoothHeadset implements BluetoothProfile {
* @hide
*/
public void bindResponse(int indId, boolean indStatus) {
- if (mService != null && isEnabled()) {
+ final IBluetoothHeadset service = mService;
+ if (service != null && isEnabled()) {
try {
- mService.bindResponse(indId, indStatus);
+ service.bindResponse(indId, indStatus);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -1115,20 +1143,15 @@ public final class BluetoothHeadset implements BluetoothProfile {
};
private boolean isEnabled() {
- if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
- return false;
+ return mAdapter.getState() == BluetoothAdapter.STATE_ON;
}
private boolean isDisabled() {
- if (mAdapter.getState() == BluetoothAdapter.STATE_OFF) return true;
- return false;
+ return mAdapter.getState() == BluetoothAdapter.STATE_OFF;
}
- private boolean isValidDevice(BluetoothDevice device) {
- if (device == null) return false;
-
- if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
- return false;
+ private static boolean isValidDevice(BluetoothDevice device) {
+ return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
}
private static void log(String msg) {
diff --git a/core/java/android/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java
index 7ed2d2e98f7f..031287f5ee12 100644
--- a/core/java/android/bluetooth/BluetoothHeadsetClient.java
+++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java
@@ -76,8 +76,8 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
* Intent sent whenever audio state changes.
*
* <p>It includes two mandatory extras:
- * {@link BluetoothProfile.EXTRA_STATE},
- * {@link BluetoothProfile.EXTRA_PREVIOUS_STATE},
+ * {@link BluetoothProfile#EXTRA_STATE},
+ * {@link BluetoothProfile#EXTRA_PREVIOUS_STATE},
* with possible values:
* {@link #STATE_AUDIO_CONNECTING},
* {@link #STATE_AUDIO_CONNECTED},
@@ -367,7 +367,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
private Context mContext;
private ServiceListener mServiceListener;
- private IBluetoothHeadsetClient mService;
+ private volatile IBluetoothHeadsetClient mService;
private BluetoothAdapter mAdapter;
private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
@@ -478,15 +478,16 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
*/
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.connect(device);
+ return service.connect(device);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return false;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -499,15 +500,16 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
*/
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.disconnect(device);
+ return service.disconnect(device);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return false;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -519,15 +521,16 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@Override
public List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
- if (mService != null && isEnabled()) {
+ final IBluetoothHeadsetClient service = mService;
+ if (service != null && isEnabled()) {
try {
- return mService.getConnectedDevices();
+ return service.getConnectedDevices();
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return new ArrayList<BluetoothDevice>();
}
@@ -541,15 +544,16 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@Override
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
- if (mService != null && isEnabled()) {
+ final IBluetoothHeadsetClient service = mService;
+ if (service != null && isEnabled()) {
try {
- return mService.getDevicesMatchingConnectionStates(states);
+ return service.getDevicesMatchingConnectionStates(states);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return new ArrayList<BluetoothDevice>();
}
@@ -562,15 +566,16 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
@Override
public int getConnectionState(BluetoothDevice device) {
if (VDBG) log("getConnectionState(" + device + ")");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.getConnectionState(device);
+ return service.getConnectionState(device);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return BluetoothProfile.STATE_DISCONNECTED;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return BluetoothProfile.STATE_DISCONNECTED;
}
@@ -581,19 +586,20 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
*/
public boolean setPriority(BluetoothDevice device, int priority) {
if (DBG) log("setPriority(" + device + ", " + priority + ")");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
if (priority != BluetoothProfile.PRIORITY_OFF
&& priority != BluetoothProfile.PRIORITY_ON) {
return false;
}
try {
- return mService.setPriority(device, priority);
+ return service.setPriority(device, priority);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return false;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -602,15 +608,16 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
*/
public int getPriority(BluetoothDevice device) {
if (VDBG) log("getPriority(" + device + ")");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.getPriority(device);
+ return service.getPriority(device);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return PRIORITY_OFF;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return PRIORITY_OFF;
}
@@ -627,14 +634,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
*/
public boolean startVoiceRecognition(BluetoothDevice device) {
if (DBG) log("startVoiceRecognition()");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.startVoiceRecognition(device);
+ return service.startVoiceRecognition(device);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -651,14 +659,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
*/
public boolean stopVoiceRecognition(BluetoothDevice device) {
if (DBG) log("stopVoiceRecognition()");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.stopVoiceRecognition(device);
+ return service.stopVoiceRecognition(device);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -670,14 +679,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
*/
public List<BluetoothHeadsetClientCall> getCurrentCalls(BluetoothDevice device) {
if (DBG) log("getCurrentCalls()");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.getCurrentCalls(device);
+ return service.getCurrentCalls(device);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return null;
}
@@ -689,14 +699,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
*/
public Bundle getCurrentAgEvents(BluetoothDevice device) {
if (DBG) log("getCurrentCalls()");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.getCurrentAgEvents(device);
+ return service.getCurrentAgEvents(device);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return null;
}
@@ -711,14 +722,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
*/
public boolean acceptCall(BluetoothDevice device, int flag) {
if (DBG) log("acceptCall()");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.acceptCall(device, flag);
+ return service.acceptCall(device, flag);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -731,14 +743,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
*/
public boolean holdCall(BluetoothDevice device) {
if (DBG) log("holdCall()");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.holdCall(device);
+ return service.holdCall(device);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -755,14 +768,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
*/
public boolean rejectCall(BluetoothDevice device) {
if (DBG) log("rejectCall()");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.rejectCall(device);
+ return service.rejectCall(device);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -784,14 +798,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
*/
public boolean terminateCall(BluetoothDevice device, BluetoothHeadsetClientCall call) {
if (DBG) log("terminateCall()");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.terminateCall(device, call);
+ return service.terminateCall(device, call);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -811,14 +826,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
*/
public boolean enterPrivateMode(BluetoothDevice device, int index) {
if (DBG) log("enterPrivateMode()");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.enterPrivateMode(device, index);
+ return service.enterPrivateMode(device, index);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -837,14 +853,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
*/
public boolean explicitCallTransfer(BluetoothDevice device) {
if (DBG) log("explicitCallTransfer()");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.explicitCallTransfer(device);
+ return service.explicitCallTransfer(device);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -859,14 +876,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
*/
public BluetoothHeadsetClientCall dial(BluetoothDevice device, String number) {
if (DBG) log("dial()");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.dial(device, number);
+ return service.dial(device, number);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return null;
}
@@ -882,14 +900,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
*/
public boolean sendDTMF(BluetoothDevice device, byte code) {
if (DBG) log("sendDTMF()");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.sendDTMF(device, code);
+ return service.sendDTMF(device, code);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -907,14 +926,15 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
*/
public boolean getLastVoiceTagNumber(BluetoothDevice device) {
if (DBG) log("getLastVoiceTagNumber()");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHeadsetClient service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.getLastVoiceTagNumber(device);
+ return service.getLastVoiceTagNumber(device);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -925,9 +945,10 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
*/
public int getAudioState(BluetoothDevice device) {
if (VDBG) log("getAudioState");
- if (mService != null && isEnabled()) {
+ final IBluetoothHeadsetClient service = mService;
+ if (service != null && isEnabled()) {
try {
- return mService.getAudioState(device);
+ return service.getAudioState(device);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -947,9 +968,10 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
*/
public void setAudioRouteAllowed(BluetoothDevice device, boolean allowed) {
if (VDBG) log("setAudioRouteAllowed");
- if (mService != null && isEnabled()) {
+ final IBluetoothHeadsetClient service = mService;
+ if (service != null && isEnabled()) {
try {
- mService.setAudioRouteAllowed(device, allowed);
+ service.setAudioRouteAllowed(device, allowed);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -968,9 +990,10 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
*/
public boolean getAudioRouteAllowed(BluetoothDevice device) {
if (VDBG) log("getAudioRouteAllowed");
- if (mService != null && isEnabled()) {
+ final IBluetoothHeadsetClient service = mService;
+ if (service != null && isEnabled()) {
try {
- return mService.getAudioRouteAllowed(device);
+ return service.getAudioRouteAllowed(device);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -991,9 +1014,10 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
* otherwise; upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} intent;
*/
public boolean connectAudio(BluetoothDevice device) {
- if (mService != null && isEnabled()) {
+ final IBluetoothHeadsetClient service = mService;
+ if (service != null && isEnabled()) {
try {
- return mService.connectAudio(device);
+ return service.connectAudio(device);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -1014,9 +1038,10 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
* otherwise; upon completion HFP sends {@link #ACTION_AUDIO_STATE_CHANGED} intent;
*/
public boolean disconnectAudio(BluetoothDevice device) {
- if (mService != null && isEnabled()) {
+ final IBluetoothHeadsetClient service = mService;
+ if (service != null && isEnabled()) {
try {
- return mService.disconnectAudio(device);
+ return service.disconnectAudio(device);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -1034,9 +1059,10 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
* @return bundle of AG features; null if no service or AG not connected
*/
public Bundle getCurrentAgFeatures(BluetoothDevice device) {
- if (mService != null && isEnabled()) {
+ final IBluetoothHeadsetClient service = mService;
+ if (service != null && isEnabled()) {
try {
- return mService.getCurrentAgFeatures(device);
+ return service.getCurrentAgFeatures(device);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -1048,7 +1074,7 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
}
- private ServiceConnection mConnection = new ServiceConnection() {
+ private final ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
if (DBG) Log.d(TAG, "Proxy object connected");
@@ -1071,15 +1097,11 @@ public final class BluetoothHeadsetClient implements BluetoothProfile {
};
private boolean isEnabled() {
- if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
- return false;
+ return mAdapter.getState() == BluetoothAdapter.STATE_ON;
}
- private boolean isValidDevice(BluetoothDevice device) {
- if (device == null) return false;
-
- if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
- return false;
+ private static boolean isValidDevice(BluetoothDevice device) {
+ return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
}
private static void log(String msg) {
diff --git a/core/java/android/bluetooth/BluetoothHealth.java b/core/java/android/bluetooth/BluetoothHealth.java
index dc5f38135aaf..57a019755f8f 100644
--- a/core/java/android/bluetooth/BluetoothHealth.java
+++ b/core/java/android/bluetooth/BluetoothHealth.java
@@ -176,9 +176,10 @@ public final class BluetoothHealth implements BluetoothProfile {
BluetoothHealthAppConfiguration config =
new BluetoothHealthAppConfiguration(name, dataType, role, channelType);
- if (mService != null) {
+ final IBluetoothHealth service = mService;
+ if (service != null) {
try {
- result = mService.registerAppConfiguration(config, wrapper);
+ result = service.registerAppConfiguration(config, wrapper);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -200,9 +201,10 @@ public final class BluetoothHealth implements BluetoothProfile {
*/
public boolean unregisterAppConfiguration(BluetoothHealthAppConfiguration config) {
boolean result = false;
- if (mService != null && isEnabled() && config != null) {
+ final IBluetoothHealth service = mService;
+ if (service != null && isEnabled() && config != null) {
try {
- result = mService.unregisterAppConfiguration(config);
+ result = service.unregisterAppConfiguration(config);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -228,9 +230,10 @@ public final class BluetoothHealth implements BluetoothProfile {
*/
public boolean connectChannelToSource(BluetoothDevice device,
BluetoothHealthAppConfiguration config) {
- if (mService != null && isEnabled() && isValidDevice(device) && config != null) {
+ final IBluetoothHealth service = mService;
+ if (service != null && isEnabled() && isValidDevice(device) && config != null) {
try {
- return mService.connectChannelToSource(device, config);
+ return service.connectChannelToSource(device, config);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -256,9 +259,10 @@ public final class BluetoothHealth implements BluetoothProfile {
*/
public boolean connectChannelToSink(BluetoothDevice device,
BluetoothHealthAppConfiguration config, int channelType) {
- if (mService != null && isEnabled() && isValidDevice(device) && config != null) {
+ final IBluetoothHealth service = mService;
+ if (service != null && isEnabled() && isValidDevice(device) && config != null) {
try {
- return mService.connectChannelToSink(device, config, channelType);
+ return service.connectChannelToSink(device, config, channelType);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -284,9 +288,10 @@ public final class BluetoothHealth implements BluetoothProfile {
*/
public boolean disconnectChannel(BluetoothDevice device,
BluetoothHealthAppConfiguration config, int channelId) {
- if (mService != null && isEnabled() && isValidDevice(device) && config != null) {
+ final IBluetoothHealth service = mService;
+ if (service != null && isEnabled() && isValidDevice(device) && config != null) {
try {
- return mService.disconnectChannel(device, config, channelId);
+ return service.disconnectChannel(device, config, channelId);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -312,9 +317,10 @@ public final class BluetoothHealth implements BluetoothProfile {
*/
public ParcelFileDescriptor getMainChannelFd(BluetoothDevice device,
BluetoothHealthAppConfiguration config) {
- if (mService != null && isEnabled() && isValidDevice(device) && config != null) {
+ final IBluetoothHealth service = mService;
+ if (service != null && isEnabled() && isValidDevice(device) && config != null) {
try {
- return mService.getMainChannelFd(device, config);
+ return service.getMainChannelFd(device, config);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -341,9 +347,10 @@ public final class BluetoothHealth implements BluetoothProfile {
*/
@Override
public int getConnectionState(BluetoothDevice device) {
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothHealth service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.getHealthDeviceConnectionState(device);
+ return service.getHealthDeviceConnectionState(device);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -370,15 +377,16 @@ public final class BluetoothHealth implements BluetoothProfile {
*/
@Override
public List<BluetoothDevice> getConnectedDevices() {
- if (mService != null && isEnabled()) {
+ final IBluetoothHealth service = mService;
+ if (service != null && isEnabled()) {
try {
- return mService.getConnectedHealthDevices();
+ return service.getConnectedHealthDevices();
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return new ArrayList<BluetoothDevice>();
}
@@ -401,15 +409,16 @@ public final class BluetoothHealth implements BluetoothProfile {
*/
@Override
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
- if (mService != null && isEnabled()) {
+ final IBluetoothHealth service = mService;
+ if (service != null && isEnabled()) {
try {
- return mService.getHealthDevicesMatchingConnectionStates(states);
+ return service.getHealthDevicesMatchingConnectionStates(states);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return new ArrayList<BluetoothDevice>();
}
@@ -455,7 +464,7 @@ public final class BluetoothHealth implements BluetoothProfile {
private Context mContext;
private ServiceListener mServiceListener;
- private IBluetoothHealth mService;
+ private volatile IBluetoothHealth mService;
BluetoothAdapter mAdapter;
/**
@@ -540,11 +549,8 @@ public final class BluetoothHealth implements BluetoothProfile {
return false;
}
- private boolean isValidDevice(BluetoothDevice device) {
- if (device == null) return false;
-
- if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
- return false;
+ private static boolean isValidDevice(BluetoothDevice device) {
+ return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
}
private boolean checkAppParam(String name, int role, int channelType,
diff --git a/core/java/android/bluetooth/BluetoothInputDevice.java b/core/java/android/bluetooth/BluetoothInputDevice.java
index a9a9010c7edb..32615761cf8c 100644
--- a/core/java/android/bluetooth/BluetoothInputDevice.java
+++ b/core/java/android/bluetooth/BluetoothInputDevice.java
@@ -222,7 +222,7 @@ public final class BluetoothInputDevice implements BluetoothProfile {
private Context mContext;
private ServiceListener mServiceListener;
private BluetoothAdapter mAdapter;
- private IBluetoothInputDevice mService;
+ private volatile IBluetoothInputDevice mService;
private final IBluetoothStateChangeCallback mBluetoothStateChangeCallback =
new IBluetoothStateChangeCallback.Stub() {
@@ -331,15 +331,16 @@ public final class BluetoothInputDevice implements BluetoothProfile {
*/
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothInputDevice service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.connect(device);
+ return service.connect(device);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -370,15 +371,16 @@ public final class BluetoothInputDevice implements BluetoothProfile {
*/
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothInputDevice service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.disconnect(device);
+ return service.disconnect(device);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -388,15 +390,16 @@ public final class BluetoothInputDevice implements BluetoothProfile {
@Override
public List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
- if (mService != null && isEnabled()) {
+ final IBluetoothInputDevice service = mService;
+ if (service != null && isEnabled()) {
try {
- return mService.getConnectedDevices();
+ return service.getConnectedDevices();
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return new ArrayList<BluetoothDevice>();
}
@@ -406,15 +409,16 @@ public final class BluetoothInputDevice implements BluetoothProfile {
@Override
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
- if (mService != null && isEnabled()) {
+ final IBluetoothInputDevice service = mService;
+ if (service != null && isEnabled()) {
try {
- return mService.getDevicesMatchingConnectionStates(states);
+ return service.getDevicesMatchingConnectionStates(states);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return new ArrayList<BluetoothDevice>();
}
@@ -424,15 +428,16 @@ public final class BluetoothInputDevice implements BluetoothProfile {
@Override
public int getConnectionState(BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothInputDevice service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.getConnectionState(device);
+ return service.getConnectionState(device);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return BluetoothProfile.STATE_DISCONNECTED;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return BluetoothProfile.STATE_DISCONNECTED;
}
@@ -453,19 +458,20 @@ public final class BluetoothInputDevice implements BluetoothProfile {
*/
public boolean setPriority(BluetoothDevice device, int priority) {
if (DBG) log("setPriority(" + device + ", " + priority + ")");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothInputDevice service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
if (priority != BluetoothProfile.PRIORITY_OFF
&& priority != BluetoothProfile.PRIORITY_ON) {
return false;
}
try {
- return mService.setPriority(device, priority);
+ return service.setPriority(device, priority);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -484,15 +490,16 @@ public final class BluetoothInputDevice implements BluetoothProfile {
*/
public int getPriority(BluetoothDevice device) {
if (VDBG) log("getPriority(" + device + ")");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothInputDevice service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.getPriority(device);
+ return service.getPriority(device);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return BluetoothProfile.PRIORITY_OFF;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return BluetoothProfile.PRIORITY_OFF;
}
@@ -517,18 +524,13 @@ public final class BluetoothInputDevice implements BluetoothProfile {
};
private boolean isEnabled() {
- if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
- return false;
+ return mAdapter.getState() == BluetoothAdapter.STATE_ON;
}
- private boolean isValidDevice(BluetoothDevice device) {
- if (device == null) return false;
-
- if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
- return false;
+ private static boolean isValidDevice(BluetoothDevice device) {
+ return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
}
-
/**
* Initiate virtual unplug for a HID input device.
*
@@ -540,16 +542,17 @@ public final class BluetoothInputDevice implements BluetoothProfile {
*/
public boolean virtualUnplug(BluetoothDevice device) {
if (DBG) log("virtualUnplug(" + device + ")");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothInputDevice service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.virtualUnplug(device);
+ return service.virtualUnplug(device);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -565,15 +568,16 @@ public final class BluetoothInputDevice implements BluetoothProfile {
*/
public boolean getProtocolMode(BluetoothDevice device) {
if (VDBG) log("getProtocolMode(" + device + ")");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothInputDevice service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.getProtocolMode(device);
+ return service.getProtocolMode(device);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -588,15 +592,16 @@ public final class BluetoothInputDevice implements BluetoothProfile {
*/
public boolean setProtocolMode(BluetoothDevice device, int protocolMode) {
if (DBG) log("setProtocolMode(" + device + ")");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothInputDevice service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.setProtocolMode(device, protocolMode);
+ return service.setProtocolMode(device, protocolMode);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -615,19 +620,19 @@ public final class BluetoothInputDevice implements BluetoothProfile {
public boolean getReport(BluetoothDevice device, byte reportType, byte reportId,
int bufferSize) {
if (VDBG) {
- log(
- "getReport(" + device + "), reportType=" + reportType + " reportId=" + reportId
- + "bufferSize=" + bufferSize);
+ log("getReport(" + device + "), reportType=" + reportType + " reportId=" + reportId
+ + "bufferSize=" + bufferSize);
}
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothInputDevice service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.getReport(device, reportType, reportId, bufferSize);
+ return service.getReport(device, reportType, reportId, bufferSize);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -644,15 +649,16 @@ public final class BluetoothInputDevice implements BluetoothProfile {
*/
public boolean setReport(BluetoothDevice device, byte reportType, String report) {
if (VDBG) log("setReport(" + device + "), reportType=" + reportType + " report=" + report);
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothInputDevice service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.setReport(device, reportType, report);
+ return service.setReport(device, reportType, report);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -668,15 +674,16 @@ public final class BluetoothInputDevice implements BluetoothProfile {
*/
public boolean sendData(BluetoothDevice device, String report) {
if (DBG) log("sendData(" + device + "), report=" + report);
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothInputDevice service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.sendData(device, report);
+ return service.sendData(device, report);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -691,15 +698,16 @@ public final class BluetoothInputDevice implements BluetoothProfile {
*/
public boolean getIdleTime(BluetoothDevice device) {
if (DBG) log("getIdletime(" + device + ")");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothInputDevice service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.getIdleTime(device);
+ return service.getIdleTime(device);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -715,15 +723,16 @@ public final class BluetoothInputDevice implements BluetoothProfile {
*/
public boolean setIdleTime(BluetoothDevice device, byte idleTime) {
if (DBG) log("setIdletime(" + device + "), idleTime=" + idleTime);
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothInputDevice service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.setIdleTime(device, idleTime);
+ return service.setIdleTime(device, idleTime);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
diff --git a/core/java/android/bluetooth/BluetoothInputHost.java b/core/java/android/bluetooth/BluetoothInputHost.java
index 15303dc79448..37f04278d461 100644
--- a/core/java/android/bluetooth/BluetoothInputHost.java
+++ b/core/java/android/bluetooth/BluetoothInputHost.java
@@ -113,7 +113,7 @@ public final class BluetoothInputHost implements BluetoothProfile {
private ServiceListener mServiceListener;
- private IBluetoothInputHost mService;
+ private volatile IBluetoothInputHost mService;
private BluetoothAdapter mAdapter;
@@ -202,24 +202,18 @@ public final class BluetoothInputHost implements BluetoothProfile {
}
};
- private ServiceConnection mConnection = new ServiceConnection() {
-
+ private final ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
Log.d(TAG, "onServiceConnected()");
-
mService = IBluetoothInputHost.Stub.asInterface(service);
-
if (mServiceListener != null) {
mServiceListener.onServiceConnected(BluetoothProfile.INPUT_HOST,
BluetoothInputHost.this);
}
}
-
public void onServiceDisconnected(ComponentName className) {
Log.d(TAG, "onServiceDisconnected()");
-
mService = null;
-
if (mServiceListener != null) {
mServiceListener.onServiceDisconnected(BluetoothProfile.INPUT_HOST);
}
@@ -291,9 +285,10 @@ public final class BluetoothInputHost implements BluetoothProfile {
public List<BluetoothDevice> getConnectedDevices() {
Log.v(TAG, "getConnectedDevices()");
- if (mService != null) {
+ final IBluetoothInputHost service = mService;
+ if (service != null) {
try {
- return mService.getConnectedDevices();
+ return service.getConnectedDevices();
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -311,9 +306,10 @@ public final class BluetoothInputHost implements BluetoothProfile {
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
Log.v(TAG, "getDevicesMatchingConnectionStates(): states=" + Arrays.toString(states));
- if (mService != null) {
+ final IBluetoothInputHost service = mService;
+ if (service != null) {
try {
- return mService.getDevicesMatchingConnectionStates(states);
+ return service.getDevicesMatchingConnectionStates(states);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -331,9 +327,10 @@ public final class BluetoothInputHost implements BluetoothProfile {
public int getConnectionState(BluetoothDevice device) {
Log.v(TAG, "getConnectionState(): device=" + device);
- if (mService != null) {
+ final IBluetoothInputHost service = mService;
+ if (service != null) {
try {
- return mService.getConnectionState(device);
+ return service.getConnectionState(device);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -370,13 +367,14 @@ public final class BluetoothInputHost implements BluetoothProfile {
return false;
}
- if (mService != null) {
+ final IBluetoothInputHost service = mService;
+ if (service != null) {
try {
BluetoothHidDeviceAppConfiguration config =
new BluetoothHidDeviceAppConfiguration();
BluetoothHidDeviceCallbackWrapper cbw =
new BluetoothHidDeviceCallbackWrapper(callback);
- result = mService.registerApp(config, sdp, inQos, outQos, cbw);
+ result = service.registerApp(config, sdp, inQos, outQos, cbw);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -403,9 +401,10 @@ public final class BluetoothInputHost implements BluetoothProfile {
boolean result = false;
- if (mService != null) {
+ final IBluetoothInputHost service = mService;
+ if (service != null) {
try {
- result = mService.unregisterApp(config);
+ result = service.unregisterApp(config);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -427,9 +426,10 @@ public final class BluetoothInputHost implements BluetoothProfile {
public boolean sendReport(BluetoothDevice device, int id, byte[] data) {
boolean result = false;
- if (mService != null) {
+ final IBluetoothInputHost service = mService;
+ if (service != null) {
try {
- result = mService.sendReport(device, id, data);
+ result = service.sendReport(device, id, data);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -454,9 +454,10 @@ public final class BluetoothInputHost implements BluetoothProfile {
boolean result = false;
- if (mService != null) {
+ final IBluetoothInputHost service = mService;
+ if (service != null) {
try {
- result = mService.replyReport(device, type, id, data);
+ result = service.replyReport(device, type, id, data);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -479,9 +480,10 @@ public final class BluetoothInputHost implements BluetoothProfile {
boolean result = false;
- if (mService != null) {
+ final IBluetoothInputHost service = mService;
+ if (service != null) {
try {
- result = mService.reportError(device, error);
+ result = service.reportError(device, error);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -502,9 +504,10 @@ public final class BluetoothInputHost implements BluetoothProfile {
boolean result = false;
- if (mService != null) {
+ final IBluetoothInputHost service = mService;
+ if (service != null) {
try {
- result = mService.unplug(device);
+ result = service.unplug(device);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -526,9 +529,10 @@ public final class BluetoothInputHost implements BluetoothProfile {
boolean result = false;
- if (mService != null) {
+ final IBluetoothInputHost service = mService;
+ if (service != null) {
try {
- result = mService.connect(device);
+ result = service.connect(device);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -549,9 +553,10 @@ public final class BluetoothInputHost implements BluetoothProfile {
boolean result = false;
- if (mService != null) {
+ final IBluetoothInputHost service = mService;
+ if (service != null) {
try {
- result = mService.disconnect(device);
+ result = service.disconnect(device);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
diff --git a/core/java/android/bluetooth/BluetoothMap.java b/core/java/android/bluetooth/BluetoothMap.java
index 26a9106f49f5..5b55b23680c3 100644
--- a/core/java/android/bluetooth/BluetoothMap.java
+++ b/core/java/android/bluetooth/BluetoothMap.java
@@ -43,7 +43,7 @@ public final class BluetoothMap implements BluetoothProfile {
public static final String ACTION_CONNECTION_STATE_CHANGED =
"android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED";
- private IBluetoothMap mService;
+ private volatile IBluetoothMap mService;
private final Context mContext;
private ServiceListener mServiceListener;
private BluetoothAdapter mAdapter;
@@ -161,9 +161,10 @@ public final class BluetoothMap implements BluetoothProfile {
*/
public int getState() {
if (VDBG) log("getState()");
- if (mService != null) {
+ final IBluetoothMap service = mService;
+ if (service != null) {
try {
- return mService.getState();
+ return service.getState();
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -182,9 +183,10 @@ public final class BluetoothMap implements BluetoothProfile {
*/
public BluetoothDevice getClient() {
if (VDBG) log("getClient()");
- if (mService != null) {
+ final IBluetoothMap service = mService;
+ if (service != null) {
try {
- return mService.getClient();
+ return service.getClient();
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -202,9 +204,10 @@ public final class BluetoothMap implements BluetoothProfile {
*/
public boolean isConnected(BluetoothDevice device) {
if (VDBG) log("isConnected(" + device + ")");
- if (mService != null) {
+ final IBluetoothMap service = mService;
+ if (service != null) {
try {
- return mService.isConnected(device);
+ return service.isConnected(device);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -232,15 +235,16 @@ public final class BluetoothMap implements BluetoothProfile {
*/
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothMap service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.disconnect(device);
+ return service.disconnect(device);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return false;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -272,15 +276,16 @@ public final class BluetoothMap implements BluetoothProfile {
*/
public List<BluetoothDevice> getConnectedDevices() {
if (DBG) log("getConnectedDevices()");
- if (mService != null && isEnabled()) {
+ final IBluetoothMap service = mService;
+ if (service != null && isEnabled()) {
try {
- return mService.getConnectedDevices();
+ return service.getConnectedDevices();
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return new ArrayList<BluetoothDevice>();
}
@@ -291,15 +296,16 @@ public final class BluetoothMap implements BluetoothProfile {
*/
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (DBG) log("getDevicesMatchingStates()");
- if (mService != null && isEnabled()) {
+ final IBluetoothMap service = mService;
+ if (service != null && isEnabled()) {
try {
- return mService.getDevicesMatchingConnectionStates(states);
+ return service.getDevicesMatchingConnectionStates(states);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return new ArrayList<BluetoothDevice>();
}
@@ -310,15 +316,16 @@ public final class BluetoothMap implements BluetoothProfile {
*/
public int getConnectionState(BluetoothDevice device) {
if (DBG) log("getConnectionState(" + device + ")");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothMap service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.getConnectionState(device);
+ return service.getConnectionState(device);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return BluetoothProfile.STATE_DISCONNECTED;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return BluetoothProfile.STATE_DISCONNECTED;
}
@@ -335,19 +342,20 @@ public final class BluetoothMap implements BluetoothProfile {
*/
public boolean setPriority(BluetoothDevice device, int priority) {
if (DBG) log("setPriority(" + device + ", " + priority + ")");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothMap service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
if (priority != BluetoothProfile.PRIORITY_OFF
&& priority != BluetoothProfile.PRIORITY_ON) {
return false;
}
try {
- return mService.setPriority(device, priority);
+ return service.setPriority(device, priority);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return false;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -363,15 +371,16 @@ public final class BluetoothMap implements BluetoothProfile {
*/
public int getPriority(BluetoothDevice device) {
if (VDBG) log("getPriority(" + device + ")");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothMap service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.getPriority(device);
+ return service.getPriority(device);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return PRIORITY_OFF;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return PRIORITY_OFF;
}
@@ -403,13 +412,8 @@ public final class BluetoothMap implements BluetoothProfile {
log("Bluetooth is Not enabled");
return false;
}
-
- private boolean isValidDevice(BluetoothDevice device) {
- if (device == null) return false;
-
- if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
- return false;
+ private static boolean isValidDevice(BluetoothDevice device) {
+ return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
}
-
}
diff --git a/core/java/android/bluetooth/BluetoothMapClient.java b/core/java/android/bluetooth/BluetoothMapClient.java
index 3e0c36548c41..af3b662d6a49 100644
--- a/core/java/android/bluetooth/BluetoothMapClient.java
+++ b/core/java/android/bluetooth/BluetoothMapClient.java
@@ -59,7 +59,7 @@ public final class BluetoothMapClient implements BluetoothProfile {
public static final String EXTRA_SENDER_CONTACT_NAME =
"android.bluetooth.mapmce.profile.extra.SENDER_CONTACT_NAME";
- private IBluetoothMapClient mService;
+ private volatile IBluetoothMapClient mService;
private final Context mContext;
private ServiceListener mServiceListener;
private BluetoothAdapter mAdapter;
@@ -176,9 +176,10 @@ public final class BluetoothMapClient implements BluetoothProfile {
*/
public boolean isConnected(BluetoothDevice device) {
if (VDBG) Log.d(TAG, "isConnected(" + device + ")");
- if (mService != null) {
+ final IBluetoothMapClient service = mService;
+ if (service != null) {
try {
- return mService.isConnected(device);
+ return service.isConnected(device);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -195,9 +196,10 @@ public final class BluetoothMapClient implements BluetoothProfile {
*/
public boolean connect(BluetoothDevice device) {
if (DBG) Log.d(TAG, "connect(" + device + ")" + "for MAPS MCE");
- if (mService != null) {
+ final IBluetoothMapClient service = mService;
+ if (service != null) {
try {
- return mService.connect(device);
+ return service.connect(device);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -216,14 +218,15 @@ public final class BluetoothMapClient implements BluetoothProfile {
*/
public boolean disconnect(BluetoothDevice device) {
if (DBG) Log.d(TAG, "disconnect(" + device + ")");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothMapClient service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.disconnect(device);
+ return service.disconnect(device);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -235,15 +238,16 @@ public final class BluetoothMapClient implements BluetoothProfile {
@Override
public List<BluetoothDevice> getConnectedDevices() {
if (DBG) Log.d(TAG, "getConnectedDevices()");
- if (mService != null && isEnabled()) {
+ final IBluetoothMapClient service = mService;
+ if (service != null && isEnabled()) {
try {
- return mService.getConnectedDevices();
+ return service.getConnectedDevices();
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return new ArrayList<>();
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return new ArrayList<>();
}
@@ -255,15 +259,16 @@ public final class BluetoothMapClient implements BluetoothProfile {
@Override
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (DBG) Log.d(TAG, "getDevicesMatchingStates()");
- if (mService != null && isEnabled()) {
+ final IBluetoothMapClient service = mService;
+ if (service != null && isEnabled()) {
try {
- return mService.getDevicesMatchingConnectionStates(states);
+ return service.getDevicesMatchingConnectionStates(states);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return new ArrayList<>();
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return new ArrayList<>();
}
@@ -275,15 +280,16 @@ public final class BluetoothMapClient implements BluetoothProfile {
@Override
public int getConnectionState(BluetoothDevice device) {
if (DBG) Log.d(TAG, "getConnectionState(" + device + ")");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothMapClient service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.getConnectionState(device);
+ return service.getConnectionState(device);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return BluetoothProfile.STATE_DISCONNECTED;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return BluetoothProfile.STATE_DISCONNECTED;
}
@@ -298,19 +304,20 @@ public final class BluetoothMapClient implements BluetoothProfile {
*/
public boolean setPriority(BluetoothDevice device, int priority) {
if (DBG) Log.d(TAG, "setPriority(" + device + ", " + priority + ")");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothMapClient service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
if (priority != BluetoothProfile.PRIORITY_OFF
&& priority != BluetoothProfile.PRIORITY_ON) {
return false;
}
try {
- return mService.setPriority(device, priority);
+ return service.setPriority(device, priority);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return false;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -326,15 +333,16 @@ public final class BluetoothMapClient implements BluetoothProfile {
*/
public int getPriority(BluetoothDevice device) {
if (VDBG) Log.d(TAG, "getPriority(" + device + ")");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothMapClient service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.getPriority(device);
+ return service.getPriority(device);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return PRIORITY_OFF;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return PRIORITY_OFF;
}
@@ -353,9 +361,10 @@ public final class BluetoothMapClient implements BluetoothProfile {
public boolean sendMessage(BluetoothDevice device, Uri[] contacts, String message,
PendingIntent sentIntent, PendingIntent deliveredIntent) {
if (DBG) Log.d(TAG, "sendMessage(" + device + ", " + contacts + ", " + message);
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothMapClient service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.sendMessage(device, contacts, message, sentIntent, deliveredIntent);
+ return service.sendMessage(device, contacts, message, sentIntent, deliveredIntent);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return false;
@@ -372,9 +381,10 @@ public final class BluetoothMapClient implements BluetoothProfile {
*/
public boolean getUnreadMessages(BluetoothDevice device) {
if (DBG) Log.d(TAG, "getUnreadMessages(" + device + ")");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothMapClient service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.getUnreadMessages(device);
+ return service.getUnreadMessages(device);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return false;
@@ -409,12 +419,8 @@ public final class BluetoothMapClient implements BluetoothProfile {
return false;
}
- private boolean isValidDevice(BluetoothDevice device) {
- if (device == null) return false;
-
- if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
- return false;
+ private static boolean isValidDevice(BluetoothDevice device) {
+ return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
}
-
}
diff --git a/core/java/android/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java
index 63e83d22178d..866b06308354 100644
--- a/core/java/android/bluetooth/BluetoothPan.java
+++ b/core/java/android/bluetooth/BluetoothPan.java
@@ -123,7 +123,7 @@ public final class BluetoothPan implements BluetoothProfile {
private Context mContext;
private ServiceListener mServiceListener;
private BluetoothAdapter mAdapter;
- private IBluetoothPan mPanService;
+ private volatile IBluetoothPan mPanService;
/**
* Create a BluetoothPan proxy object for interacting with the local
@@ -238,15 +238,16 @@ public final class BluetoothPan implements BluetoothProfile {
*/
public boolean connect(BluetoothDevice device) {
if (DBG) log("connect(" + device + ")");
- if (mPanService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothPan service = mPanService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mPanService.connect(device);
+ return service.connect(device);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
}
}
- if (mPanService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -277,15 +278,16 @@ public final class BluetoothPan implements BluetoothProfile {
*/
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
- if (mPanService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothPan service = mPanService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mPanService.disconnect(device);
+ return service.disconnect(device);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return false;
}
}
- if (mPanService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -295,15 +297,16 @@ public final class BluetoothPan implements BluetoothProfile {
@Override
public List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
- if (mPanService != null && isEnabled()) {
+ final IBluetoothPan service = mPanService;
+ if (service != null && isEnabled()) {
try {
- return mPanService.getConnectedDevices();
+ return service.getConnectedDevices();
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
}
}
- if (mPanService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return new ArrayList<BluetoothDevice>();
}
@@ -313,15 +316,16 @@ public final class BluetoothPan implements BluetoothProfile {
@Override
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (VDBG) log("getDevicesMatchingStates()");
- if (mPanService != null && isEnabled()) {
+ final IBluetoothPan service = mPanService;
+ if (service != null && isEnabled()) {
try {
- return mPanService.getDevicesMatchingConnectionStates(states);
+ return service.getDevicesMatchingConnectionStates(states);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
}
}
- if (mPanService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return new ArrayList<BluetoothDevice>();
}
@@ -331,25 +335,25 @@ public final class BluetoothPan implements BluetoothProfile {
@Override
public int getConnectionState(BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
- if (mPanService != null && isEnabled()
- && isValidDevice(device)) {
+ final IBluetoothPan service = mPanService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mPanService.getConnectionState(device);
+ return service.getConnectionState(device);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
return BluetoothProfile.STATE_DISCONNECTED;
}
}
- if (mPanService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return BluetoothProfile.STATE_DISCONNECTED;
}
public void setBluetoothTethering(boolean value) {
if (DBG) log("setBluetoothTethering(" + value + ")");
-
- if (mPanService != null && isEnabled()) {
+ final IBluetoothPan service = mPanService;
+ if (service != null && isEnabled()) {
try {
- mPanService.setBluetoothTethering(value);
+ service.setBluetoothTethering(value);
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
}
@@ -358,10 +362,10 @@ public final class BluetoothPan implements BluetoothProfile {
public boolean isTetheringOn() {
if (VDBG) log("isTetheringOn()");
-
- if (mPanService != null && isEnabled()) {
+ final IBluetoothPan service = mPanService;
+ if (service != null && isEnabled()) {
try {
- return mPanService.isTetheringOn();
+ return service.isTetheringOn();
} catch (RemoteException e) {
Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
}
@@ -373,7 +377,6 @@ public final class BluetoothPan implements BluetoothProfile {
public void onServiceConnected(ComponentName className, IBinder service) {
if (DBG) Log.d(TAG, "BluetoothPAN Proxy object connected");
mPanService = IBluetoothPan.Stub.asInterface(Binder.allowBlocking(service));
-
if (mServiceListener != null) {
mServiceListener.onServiceConnected(BluetoothProfile.PAN,
BluetoothPan.this);
@@ -390,15 +393,11 @@ public final class BluetoothPan implements BluetoothProfile {
};
private boolean isEnabled() {
- if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
- return false;
+ return mAdapter.getState() == BluetoothAdapter.STATE_ON;
}
- private boolean isValidDevice(BluetoothDevice device) {
- if (device == null) return false;
-
- if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
- return false;
+ private static boolean isValidDevice(BluetoothDevice device) {
+ return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
}
private static void log(String msg) {
diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java
index 78b7c7b7a70b..19f5198ca71a 100644
--- a/core/java/android/bluetooth/BluetoothPbap.java
+++ b/core/java/android/bluetooth/BluetoothPbap.java
@@ -68,7 +68,7 @@ public class BluetoothPbap {
public static final String PBAP_STATE_CHANGED_ACTION =
"android.bluetooth.pbap.intent.action.PBAP_STATE_CHANGED";
- private IBluetoothPbap mService;
+ private volatile IBluetoothPbap mService;
private final Context mContext;
private ServiceListener mServiceListener;
private BluetoothAdapter mAdapter;
@@ -214,9 +214,10 @@ public class BluetoothPbap {
*/
public int getState() {
if (VDBG) log("getState()");
- if (mService != null) {
+ final IBluetoothPbap service = mService;
+ if (service != null) {
try {
- return mService.getState();
+ return service.getState();
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -235,9 +236,10 @@ public class BluetoothPbap {
*/
public BluetoothDevice getClient() {
if (VDBG) log("getClient()");
- if (mService != null) {
+ final IBluetoothPbap service = mService;
+ if (service != null) {
try {
- return mService.getClient();
+ return service.getClient();
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -255,9 +257,10 @@ public class BluetoothPbap {
*/
public boolean isConnected(BluetoothDevice device) {
if (VDBG) log("isConnected(" + device + ")");
- if (mService != null) {
+ final IBluetoothPbap service = mService;
+ if (service != null) {
try {
- return mService.isConnected(device);
+ return service.isConnected(device);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -275,9 +278,10 @@ public class BluetoothPbap {
*/
public boolean disconnect() {
if (DBG) log("disconnect()");
- if (mService != null) {
+ final IBluetoothPbap service = mService;
+ if (service != null) {
try {
- mService.disconnect();
+ service.disconnect();
return true;
} catch (RemoteException e) {
Log.e(TAG, e.toString());
diff --git a/core/java/android/bluetooth/BluetoothPbapClient.java b/core/java/android/bluetooth/BluetoothPbapClient.java
index b9b372c84843..00a15f3f7087 100644
--- a/core/java/android/bluetooth/BluetoothPbapClient.java
+++ b/core/java/android/bluetooth/BluetoothPbapClient.java
@@ -42,7 +42,7 @@ public final class BluetoothPbapClient implements BluetoothProfile {
public static final String ACTION_CONNECTION_STATE_CHANGED =
"android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED";
- private IBluetoothPbapClient mService;
+ private volatile IBluetoothPbapClient mService;
private final Context mContext;
private ServiceListener mServiceListener;
private BluetoothAdapter mAdapter;
@@ -173,15 +173,16 @@ public final class BluetoothPbapClient implements BluetoothProfile {
if (DBG) {
log("connect(" + device + ") for PBAP Client.");
}
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothPbapClient service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.connect(device);
+ return service.connect(device);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return false;
}
}
- if (mService == null) {
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
}
return false;
@@ -197,16 +198,17 @@ public final class BluetoothPbapClient implements BluetoothProfile {
if (DBG) {
log("disconnect(" + device + ")" + new Exception());
}
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothPbapClient service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- mService.disconnect(device);
+ service.disconnect(device);
return true;
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return false;
}
}
- if (mService == null) {
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
}
return false;
@@ -223,15 +225,16 @@ public final class BluetoothPbapClient implements BluetoothProfile {
if (DBG) {
log("getConnectedDevices()");
}
- if (mService != null && isEnabled()) {
+ final IBluetoothPbapClient service = mService;
+ if (service != null && isEnabled()) {
try {
- return mService.getConnectedDevices();
+ return service.getConnectedDevices();
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
}
}
- if (mService == null) {
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
}
return new ArrayList<BluetoothDevice>();
@@ -247,15 +250,16 @@ public final class BluetoothPbapClient implements BluetoothProfile {
if (DBG) {
log("getDevicesMatchingStates()");
}
- if (mService != null && isEnabled()) {
+ final IBluetoothPbapClient service = mService;
+ if (service != null && isEnabled()) {
try {
- return mService.getDevicesMatchingConnectionStates(states);
+ return service.getDevicesMatchingConnectionStates(states);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
}
}
- if (mService == null) {
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
}
return new ArrayList<BluetoothDevice>();
@@ -271,15 +275,16 @@ public final class BluetoothPbapClient implements BluetoothProfile {
if (DBG) {
log("getConnectionState(" + device + ")");
}
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothPbapClient service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.getConnectionState(device);
+ return service.getConnectionState(device);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return BluetoothProfile.STATE_DISCONNECTED;
}
}
- if (mService == null) {
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
}
return BluetoothProfile.STATE_DISCONNECTED;
@@ -321,14 +326,8 @@ public final class BluetoothPbapClient implements BluetoothProfile {
return false;
}
- private boolean isValidDevice(BluetoothDevice device) {
- if (device == null) {
- return false;
- }
- if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
- return true;
- }
- return false;
+ private static boolean isValidDevice(BluetoothDevice device) {
+ return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
}
/**
@@ -339,26 +338,27 @@ public final class BluetoothPbapClient implements BluetoothProfile {
* {@link #PRIORITY_OFF},
*
* @param device Paired bluetooth device
- * @param priority
+ * @param priority Priority of this profile
* @return true if priority is set, false on error
*/
public boolean setPriority(BluetoothDevice device, int priority) {
if (DBG) {
log("setPriority(" + device + ", " + priority + ")");
}
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothPbapClient service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
if (priority != BluetoothProfile.PRIORITY_OFF
&& priority != BluetoothProfile.PRIORITY_ON) {
return false;
}
try {
- return mService.setPriority(device, priority);
+ return service.setPriority(device, priority);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return false;
}
}
- if (mService == null) {
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
}
return false;
@@ -378,15 +378,16 @@ public final class BluetoothPbapClient implements BluetoothProfile {
if (VDBG) {
log("getPriority(" + device + ")");
}
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothPbapClient service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.getPriority(device);
+ return service.getPriority(device);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return PRIORITY_OFF;
}
}
- if (mService == null) {
+ if (service == null) {
Log.w(TAG, "Proxy not attached to service");
}
return PRIORITY_OFF;
diff --git a/core/java/android/bluetooth/BluetoothSap.java b/core/java/android/bluetooth/BluetoothSap.java
index bcdf4938fe2f..48481620c97f 100644
--- a/core/java/android/bluetooth/BluetoothSap.java
+++ b/core/java/android/bluetooth/BluetoothSap.java
@@ -68,7 +68,7 @@ public final class BluetoothSap implements BluetoothProfile {
public static final String ACTION_CONNECTION_STATE_CHANGED =
"android.bluetooth.sap.profile.action.CONNECTION_STATE_CHANGED";
- private IBluetoothSap mService;
+ private volatile IBluetoothSap mService;
private final Context mContext;
private ServiceListener mServiceListener;
private BluetoothAdapter mAdapter;
@@ -202,9 +202,10 @@ public final class BluetoothSap implements BluetoothProfile {
*/
public int getState() {
if (VDBG) log("getState()");
- if (mService != null) {
+ final IBluetoothSap service = mService;
+ if (service != null) {
try {
- return mService.getState();
+ return service.getState();
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -224,9 +225,10 @@ public final class BluetoothSap implements BluetoothProfile {
*/
public BluetoothDevice getClient() {
if (VDBG) log("getClient()");
- if (mService != null) {
+ final IBluetoothSap service = mService;
+ if (service != null) {
try {
- return mService.getClient();
+ return service.getClient();
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -246,9 +248,10 @@ public final class BluetoothSap implements BluetoothProfile {
*/
public boolean isConnected(BluetoothDevice device) {
if (VDBG) log("isConnected(" + device + ")");
- if (mService != null) {
+ final IBluetoothSap service = mService;
+ if (service != null) {
try {
- return mService.isConnected(device);
+ return service.isConnected(device);
} catch (RemoteException e) {
Log.e(TAG, e.toString());
}
@@ -279,15 +282,16 @@ public final class BluetoothSap implements BluetoothProfile {
*/
public boolean disconnect(BluetoothDevice device) {
if (DBG) log("disconnect(" + device + ")");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothSap service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.disconnect(device);
+ return service.disconnect(device);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return false;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -299,15 +303,16 @@ public final class BluetoothSap implements BluetoothProfile {
*/
public List<BluetoothDevice> getConnectedDevices() {
if (DBG) log("getConnectedDevices()");
- if (mService != null && isEnabled()) {
+ final IBluetoothSap service = mService;
+ if (service != null && isEnabled()) {
try {
- return mService.getConnectedDevices();
+ return service.getConnectedDevices();
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return new ArrayList<BluetoothDevice>();
}
@@ -319,15 +324,16 @@ public final class BluetoothSap implements BluetoothProfile {
*/
public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
if (DBG) log("getDevicesMatchingStates()");
- if (mService != null && isEnabled()) {
+ final IBluetoothSap service = mService;
+ if (service != null && isEnabled()) {
try {
- return mService.getDevicesMatchingConnectionStates(states);
+ return service.getDevicesMatchingConnectionStates(states);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return new ArrayList<BluetoothDevice>();
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return new ArrayList<BluetoothDevice>();
}
@@ -339,15 +345,16 @@ public final class BluetoothSap implements BluetoothProfile {
*/
public int getConnectionState(BluetoothDevice device) {
if (DBG) log("getConnectionState(" + device + ")");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothSap service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.getConnectionState(device);
+ return service.getConnectionState(device);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return BluetoothProfile.STATE_DISCONNECTED;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return BluetoothProfile.STATE_DISCONNECTED;
}
@@ -363,19 +370,20 @@ public final class BluetoothSap implements BluetoothProfile {
*/
public boolean setPriority(BluetoothDevice device, int priority) {
if (DBG) log("setPriority(" + device + ", " + priority + ")");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothSap service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
if (priority != BluetoothProfile.PRIORITY_OFF
&& priority != BluetoothProfile.PRIORITY_ON) {
return false;
}
try {
- return mService.setPriority(device, priority);
+ return service.setPriority(device, priority);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return false;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return false;
}
@@ -388,19 +396,20 @@ public final class BluetoothSap implements BluetoothProfile {
*/
public int getPriority(BluetoothDevice device) {
if (VDBG) log("getPriority(" + device + ")");
- if (mService != null && isEnabled() && isValidDevice(device)) {
+ final IBluetoothSap service = mService;
+ if (service != null && isEnabled() && isValidDevice(device)) {
try {
- return mService.getPriority(device);
+ return service.getPriority(device);
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
return PRIORITY_OFF;
}
}
- if (mService == null) Log.w(TAG, "Proxy not attached to service");
+ if (service == null) Log.w(TAG, "Proxy not attached to service");
return PRIORITY_OFF;
}
- private ServiceConnection mConnection = new ServiceConnection() {
+ private final ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
if (DBG) log("Proxy object connected");
mService = IBluetoothSap.Stub.asInterface(Binder.allowBlocking(service));
@@ -432,15 +441,8 @@ public final class BluetoothSap implements BluetoothProfile {
return false;
}
- private boolean isValidDevice(BluetoothDevice device) {
- if (device == null) {
- return false;
- }
-
- if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
- return true;
- }
- return false;
+ private static boolean isValidDevice(BluetoothDevice device) {
+ return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress());
}
}
diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java
index 5bfc54d267ca..76cb3f5b548e 100644
--- a/core/java/android/bluetooth/BluetoothUuid.java
+++ b/core/java/android/bluetooth/BluetoothUuid.java
@@ -232,7 +232,7 @@ public final class BluetoothUuid {
*/
public static int getServiceIdentifierFromParcelUuid(ParcelUuid parcelUuid) {
UUID uuid = parcelUuid.getUuid();
- long value = (uuid.getMostSignificantBits() & 0x0000FFFF00000000L) >>> 32;
+ long value = (uuid.getMostSignificantBits() & 0xFFFFFFFF00000000L) >>> 32;
return (int) value;
}
diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java
index ad9e20b9292c..dd1a61c31644 100644
--- a/core/java/android/bluetooth/le/BluetoothLeScanner.java
+++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java
@@ -99,7 +99,9 @@ public final class BluetoothLeScanner {
/**
* Start Bluetooth LE scan with default parameters and no filters. The scan results will be
- * delivered through {@code callback}.
+ * delivered through {@code callback}. For unfiltered scans, scanning is stopped on screen
+ * off to save power. Scanning is resumed when screen is turned on again. To avoid this, use
+ * {@link #startScan(List, ScanSettings, ScanCallback)} with desired {@link ScanFilter}.
* <p>
* An app must hold
* {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or
@@ -116,6 +118,9 @@ public final class BluetoothLeScanner {
/**
* Start Bluetooth LE scan. The scan results will be delivered through {@code callback}.
+ * For unfiltered scans, scanning is stopped on screen off to save power. Scanning is
+ * resumed when screen is turned on again. To avoid this, do filetered scanning by
+ * using proper {@link ScanFilter}.
* <p>
* An app must hold
* {@link android.Manifest.permission#ACCESS_COARSE_LOCATION ACCESS_COARSE_LOCATION} or
diff --git a/core/java/android/bluetooth/le/ScanSettings.java b/core/java/android/bluetooth/le/ScanSettings.java
index 35ed424aa2db..8fdcba85d33a 100644
--- a/core/java/android/bluetooth/le/ScanSettings.java
+++ b/core/java/android/bluetooth/le/ScanSettings.java
@@ -35,7 +35,7 @@ public final class ScanSettings implements Parcelable {
/**
* Perform Bluetooth LE scan in low power mode. This is the default scan mode as it consumes the
- * least power.
+ * least power. This mode is enforced if the scanning application is not in foreground.
*/
public static final int SCAN_MODE_LOW_POWER = 0;
diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java
index 1bb0fbb74a53..2c9fb23e077a 100644
--- a/core/java/android/net/LinkProperties.java
+++ b/core/java/android/net/LinkProperties.java
@@ -18,14 +18,13 @@ package android.net;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.net.ProxyInfo;
-import android.os.Parcelable;
import android.os.Parcel;
+import android.os.Parcelable;
import android.text.TextUtils;
-import java.net.InetAddress;
import java.net.Inet4Address;
import java.net.Inet6Address;
+import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
@@ -71,8 +70,23 @@ public final class LinkProperties implements Parcelable {
* @hide
*/
public static class CompareResult<T> {
- public List<T> removed = new ArrayList<T>();
- public List<T> added = new ArrayList<T>();
+ public final List<T> removed = new ArrayList<T>();
+ public final List<T> added = new ArrayList<T>();
+
+ public CompareResult() {}
+
+ public CompareResult(Collection<T> oldItems, Collection<T> newItems) {
+ if (oldItems != null) {
+ removed.addAll(oldItems);
+ }
+ if (newItems != null) {
+ for (T newItem : newItems) {
+ if (!removed.remove(newItem)) {
+ added.add(newItem);
+ }
+ }
+ }
+ }
@Override
public String toString() {
@@ -504,11 +518,22 @@ public final class LinkProperties implements Parcelable {
}
/**
+ * Make sure this LinkProperties instance contains routes that cover the local subnet
+ * of its link addresses. Add any route that is missing.
+ * @hide
+ */
+ public void ensureDirectlyConnectedRoutes() {
+ for (LinkAddress addr: mLinkAddresses) {
+ addRoute(new RouteInfo(addr, null, mIfaceName));
+ }
+ }
+
+ /**
* Returns all the routes on this link and all the links stacked above it.
* @hide
*/
public List<RouteInfo> getAllRoutes() {
- List<RouteInfo> routes = new ArrayList();
+ List<RouteInfo> routes = new ArrayList<>();
routes.addAll(mRoutes);
for (LinkProperties stacked: mStackedLinks.values()) {
routes.addAll(stacked.getAllRoutes());
@@ -990,17 +1015,8 @@ public final class LinkProperties implements Parcelable {
* are in target but not in mLinkAddresses are placed in the
* addedAddresses.
*/
- CompareResult<LinkAddress> result = new CompareResult<LinkAddress>();
- result.removed = new ArrayList<LinkAddress>(mLinkAddresses);
- result.added.clear();
- if (target != null) {
- for (LinkAddress newAddress : target.getLinkAddresses()) {
- if (! result.removed.remove(newAddress)) {
- result.added.add(newAddress);
- }
- }
- }
- return result;
+ return new CompareResult<>(mLinkAddresses,
+ target != null ? target.getLinkAddresses() : null);
}
/**
@@ -1019,18 +1035,7 @@ public final class LinkProperties implements Parcelable {
* are in target but not in mDnses are placed in the
* addedAddresses.
*/
- CompareResult<InetAddress> result = new CompareResult<InetAddress>();
-
- result.removed = new ArrayList<InetAddress>(mDnses);
- result.added.clear();
- if (target != null) {
- for (InetAddress newAddress : target.getDnsServers()) {
- if (! result.removed.remove(newAddress)) {
- result.added.add(newAddress);
- }
- }
- }
- return result;
+ return new CompareResult<>(mDnses, target != null ? target.getDnsServers() : null);
}
/**
@@ -1048,18 +1053,7 @@ public final class LinkProperties implements Parcelable {
* leaving the routes that are different. And route address which
* are in target but not in mRoutes are placed in added.
*/
- CompareResult<RouteInfo> result = new CompareResult<RouteInfo>();
-
- result.removed = getAllRoutes();
- result.added.clear();
- if (target != null) {
- for (RouteInfo r : target.getAllRoutes()) {
- if (! result.removed.remove(r)) {
- result.added.add(r);
- }
- }
- }
- return result;
+ return new CompareResult<>(getAllRoutes(), target != null ? target.getAllRoutes() : null);
}
/**
@@ -1077,18 +1071,8 @@ public final class LinkProperties implements Parcelable {
* leaving the interface names that are different. And interface names which
* are in target but not in this are placed in added.
*/
- CompareResult<String> result = new CompareResult<String>();
-
- result.removed = getAllInterfaceNames();
- result.added.clear();
- if (target != null) {
- for (String r : target.getAllInterfaceNames()) {
- if (! result.removed.remove(r)) {
- result.added.add(r);
- }
- }
- }
- return result;
+ return new CompareResult<>(getAllInterfaceNames(),
+ target != null ? target.getAllInterfaceNames() : null);
}
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 0780af602be2..171adc054bbe 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -677,36 +677,33 @@ public class NetworkStats implements Parcelable {
entry.tag = left.tag[i];
entry.metered = left.metered[i];
entry.roaming = left.roaming[i];
+ entry.rxBytes = left.rxBytes[i];
+ entry.rxPackets = left.rxPackets[i];
+ entry.txBytes = left.txBytes[i];
+ entry.txPackets = left.txPackets[i];
+ entry.operations = left.operations[i];
// find remote row that matches, and subtract
final int j = right.findIndexHinted(entry.iface, entry.uid, entry.set, entry.tag,
entry.metered, entry.roaming, i);
- if (j == -1) {
- // newly appearing row, return entire value
- entry.rxBytes = left.rxBytes[i];
- entry.rxPackets = left.rxPackets[i];
- entry.txBytes = left.txBytes[i];
- entry.txPackets = left.txPackets[i];
- entry.operations = left.operations[i];
- } else {
- // existing row, subtract remote value
- entry.rxBytes = left.rxBytes[i] - right.rxBytes[j];
- entry.rxPackets = left.rxPackets[i] - right.rxPackets[j];
- entry.txBytes = left.txBytes[i] - right.txBytes[j];
- entry.txPackets = left.txPackets[i] - right.txPackets[j];
- entry.operations = left.operations[i] - right.operations[j];
-
- if (entry.rxBytes < 0 || entry.rxPackets < 0 || entry.txBytes < 0
- || entry.txPackets < 0 || entry.operations < 0) {
- if (observer != null) {
- observer.foundNonMonotonic(left, i, right, j, cookie);
- }
- entry.rxBytes = Math.max(entry.rxBytes, 0);
- entry.rxPackets = Math.max(entry.rxPackets, 0);
- entry.txBytes = Math.max(entry.txBytes, 0);
- entry.txPackets = Math.max(entry.txPackets, 0);
- entry.operations = Math.max(entry.operations, 0);
+ if (j != -1) {
+ // Found matching row, subtract remote value.
+ entry.rxBytes -= right.rxBytes[j];
+ entry.rxPackets -= right.rxPackets[j];
+ entry.txBytes -= right.txBytes[j];
+ entry.txPackets -= right.txPackets[j];
+ entry.operations -= right.operations[j];
+ }
+
+ if (entry.isNegative()) {
+ if (observer != null) {
+ observer.foundNonMonotonic(left, i, right, j, cookie);
}
+ entry.rxBytes = Math.max(entry.rxBytes, 0);
+ entry.rxPackets = Math.max(entry.rxPackets, 0);
+ entry.txBytes = Math.max(entry.txBytes, 0);
+ entry.txPackets = Math.max(entry.txPackets, 0);
+ entry.operations = Math.max(entry.operations, 0);
}
result.addValues(entry);
diff --git a/core/java/android/net/metrics/WakeupEvent.java b/core/java/android/net/metrics/WakeupEvent.java
new file mode 100644
index 000000000000..cbf3fc8c81da
--- /dev/null
+++ b/core/java/android/net/metrics/WakeupEvent.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.metrics;
+
+/**
+ * An event logged when NFLOG notifies userspace of a wakeup packet for
+ * watched interfaces.
+ * {@hide}
+ */
+public class WakeupEvent {
+ public String iface;
+ public long timestampMs;
+ public int uid;
+
+ @Override
+ public String toString() {
+ return String.format("WakeupEvent(%tT.%tL, %s, uid: %d)",
+ timestampMs, timestampMs, iface, uid);
+ }
+}
diff --git a/core/java/android/net/metrics/WakeupStats.java b/core/java/android/net/metrics/WakeupStats.java
new file mode 100644
index 000000000000..d520b9745918
--- /dev/null
+++ b/core/java/android/net/metrics/WakeupStats.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.metrics;
+
+import android.os.Process;
+import android.os.SystemClock;
+
+/**
+ * An event logged per interface and that aggregates WakeupEvents for that interface.
+ * {@hide}
+ */
+public class WakeupStats {
+
+ private static final int NO_UID = -1;
+
+ public final long creationTimeMs = SystemClock.elapsedRealtime();
+ public final String iface;
+
+ public long totalWakeups = 0;
+ public long rootWakeups = 0;
+ public long systemWakeups = 0;
+ public long nonApplicationWakeups = 0;
+ public long applicationWakeups = 0;
+ public long unroutedWakeups = 0;
+ public long durationSec = 0;
+
+ public WakeupStats(String iface) {
+ this.iface = iface;
+ }
+
+ /** Update durationSec with current time. */
+ public void updateDuration() {
+ durationSec = (SystemClock.elapsedRealtime() - creationTimeMs) / 1000;
+ }
+
+ /** Update wakeup counters for the given WakeupEvent. */
+ public void countEvent(WakeupEvent ev) {
+ totalWakeups++;
+ switch (ev.uid) {
+ case Process.ROOT_UID:
+ rootWakeups++;
+ break;
+ case Process.SYSTEM_UID:
+ systemWakeups++;
+ break;
+ case NO_UID:
+ unroutedWakeups++;
+ break;
+ default:
+ if (ev.uid >= Process.FIRST_APPLICATION_UID) {
+ applicationWakeups++;
+ } else {
+ nonApplicationWakeups++;
+ }
+ break;
+ }
+ }
+
+ @Override
+ public String toString() {
+ updateDuration();
+ return new StringBuilder()
+ .append("WakeupStats(").append(iface)
+ .append(", total: ").append(totalWakeups)
+ .append(", root: ").append(rootWakeups)
+ .append(", system: ").append(systemWakeups)
+ .append(", apps: ").append(applicationWakeups)
+ .append(", non-apps: ").append(nonApplicationWakeups)
+ .append(", unrouted: ").append(unroutedWakeups)
+ .append(", ").append(durationSec).append("s)")
+ .toString();
+ }
+}
diff --git a/core/java/android/net/nsd/NsdManager.java b/core/java/android/net/nsd/NsdManager.java
index 1e41eea925a5..535bf675cd0e 100644
--- a/core/java/android/net/nsd/NsdManager.java
+++ b/core/java/android/net/nsd/NsdManager.java
@@ -21,25 +21,24 @@ import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.internal.util.Preconditions.checkStringNotEmpty;
import android.annotation.SdkConstant;
-import android.annotation.SystemService;
import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SystemService;
import android.content.Context;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
-import android.os.RemoteException;
import android.os.Messenger;
-import android.text.TextUtils;
+import android.os.RemoteException;
import android.util.Log;
import android.util.SparseArray;
-import java.util.concurrent.CountDownLatch;
-
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.AsyncChannel;
import com.android.internal.util.Protocol;
+import java.util.concurrent.CountDownLatch;
+
/**
* The Network Service Discovery Manager class provides the API to discover services
* on a network. As an example, if device A and device B are connected over a Wi-Fi
@@ -244,7 +243,7 @@ public final class NsdManager {
return name;
}
- private static int FIRST_LISTENER_KEY = 1;
+ private static final int FIRST_LISTENER_KEY = 1;
private final INsdManager mService;
private final Context mContext;
@@ -278,6 +277,7 @@ public final class NsdManager {
@VisibleForTesting
public void disconnect() {
mAsyncChannel.disconnect();
+ mHandler.getLooper().quitSafely();
}
/**
@@ -650,7 +650,7 @@ public final class NsdManager {
private static void checkServiceInfo(NsdServiceInfo serviceInfo) {
checkNotNull(serviceInfo, "NsdServiceInfo cannot be null");
- checkStringNotEmpty(serviceInfo.getServiceName(),"Service name cannot be empty");
+ checkStringNotEmpty(serviceInfo.getServiceName(), "Service name cannot be empty");
checkStringNotEmpty(serviceInfo.getServiceType(), "Service type cannot be empty");
}
}
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index ae0b88573e8f..c45eb2e4ac39 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -19,8 +19,8 @@ package android.os;
import android.annotation.TestApi;
import android.system.Os;
import android.system.OsConstants;
-import android.util.Log;
import android.webkit.WebViewZygote;
+
import dalvik.system.VMRuntime;
/**
@@ -417,7 +417,7 @@ public class Process {
*
* When invokeWith is not null, the process will be started as a fresh app
* and not a zygote fork. Note that this is only allowed for uid 0 or when
- * debugFlags contains DEBUG_ENABLE_DEBUGGER.
+ * runtimeFlags contains DEBUG_ENABLE_DEBUGGER.
*
* @param processClass The class to use as the process's main entry
* point.
@@ -425,7 +425,7 @@ public class Process {
* @param uid The user-id under which the process will run.
* @param gid The group-id under which the process will run.
* @param gids Additional group-ids associated with the process.
- * @param debugFlags Additional flags.
+ * @param runtimeFlags Additional flags for the runtime.
* @param targetSdkVersion The target SDK version for the app.
* @param seInfo null-ok SELinux information for the new process.
* @param abi non-null the ABI this app should be started with.
@@ -442,7 +442,7 @@ public class Process {
public static final ProcessStartResult start(final String processClass,
final String niceName,
int uid, int gid, int[] gids,
- int debugFlags, int mountExternal,
+ int runtimeFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String abi,
@@ -451,7 +451,7 @@ public class Process {
String invokeWith,
String[] zygoteArgs) {
return zygoteProcess.start(processClass, niceName, uid, gid, gids,
- debugFlags, mountExternal, targetSdkVersion, seInfo,
+ runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, zygoteArgs);
}
@@ -459,7 +459,7 @@ public class Process {
public static final ProcessStartResult startWebView(final String processClass,
final String niceName,
int uid, int gid, int[] gids,
- int debugFlags, int mountExternal,
+ int runtimeFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String abi,
@@ -468,7 +468,7 @@ public class Process {
String invokeWith,
String[] zygoteArgs) {
return WebViewZygote.getProcess().start(processClass, niceName, uid, gid, gids,
- debugFlags, mountExternal, targetSdkVersion, seInfo,
+ runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, zygoteArgs);
}
diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java
index 8632194f2b02..560b4b31cdc6 100644
--- a/core/java/android/os/SystemProperties.java
+++ b/core/java/android/os/SystemProperties.java
@@ -16,6 +16,8 @@
package android.os;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.util.Log;
import android.util.MutableInt;
@@ -43,17 +45,12 @@ public class SystemProperties {
public static final int PROP_VALUE_MAX = 91;
+ @GuardedBy("sChangeCallbacks")
private static final ArrayList<Runnable> sChangeCallbacks = new ArrayList<Runnable>();
@GuardedBy("sRoReads")
- private static final HashMap<String, MutableInt> sRoReads;
- static {
- if (TRACK_KEY_ACCESS) {
- sRoReads = new HashMap<>();
- } else {
- sRoReads = null;
- }
- }
+ private static final HashMap<String, MutableInt> sRoReads =
+ TRACK_KEY_ACCESS ? new HashMap<>() : null;
private static void onKeyAccess(String key) {
if (!TRACK_KEY_ACCESS) return;
@@ -85,77 +82,102 @@ public class SystemProperties {
private static native void native_report_sysprop_change();
/**
- * Get the value for the given key.
- * @return an empty string if the key isn't found
+ * Get the String value for the given {@code key}.
+ *
+ * <b>WARNING:</b> Do not use this method if the value may not be a valid UTF string! This
+ * method will crash in native code.
+ *
+ * @param key the key to lookup
+ * @return an empty string if the {@code key} isn't found
*/
- public static String get(String key) {
+ @NonNull
+ public static String get(@NonNull String key) {
if (TRACK_KEY_ACCESS) onKeyAccess(key);
return native_get(key);
}
/**
- * Get the value for the given key.
- * @return if the key isn't found, return def if it isn't null, or an empty string otherwise
+ * Get the String value for the given {@code key}.
+ *
+ * <b>WARNING:</b> Do not use this method if the value may not be a valid UTF string! This
+ * method will crash in native code.
+ *
+ * @param key the key to lookup
+ * @param def the default value in case the property is not set or empty
+ * @return if the {@code key} isn't found, return {@code def} if it isn't null, or an empty
+ * string otherwise
*/
- public static String get(String key, String def) {
+ @NonNull
+ public static String get(@NonNull String key, @Nullable String def) {
if (TRACK_KEY_ACCESS) onKeyAccess(key);
return native_get(key, def);
}
/**
- * Get the value for the given key, and return as an integer.
+ * Get the value for the given {@code key}, and return as an integer.
+ *
* @param key the key to lookup
* @param def a default value to return
* @return the key parsed as an integer, or def if the key isn't found or
* cannot be parsed
*/
- public static int getInt(String key, int def) {
+ public static int getInt(@NonNull String key, int def) {
if (TRACK_KEY_ACCESS) onKeyAccess(key);
return native_get_int(key, def);
}
/**
- * Get the value for the given key, and return as a long.
+ * Get the value for the given {@code key}, and return as a long.
+ *
* @param key the key to lookup
* @param def a default value to return
* @return the key parsed as a long, or def if the key isn't found or
* cannot be parsed
*/
- public static long getLong(String key, long def) {
+ public static long getLong(@NonNull String key, long def) {
if (TRACK_KEY_ACCESS) onKeyAccess(key);
return native_get_long(key, def);
}
/**
- * Get the value for the given key, returned as a boolean.
+ * Get the value for the given {@code key}, returned as a boolean.
* Values 'n', 'no', '0', 'false' or 'off' are considered false.
* Values 'y', 'yes', '1', 'true' or 'on' are considered true.
* (case sensitive).
* If the key does not exist, or has any other value, then the default
* result is returned.
+ *
* @param key the key to lookup
* @param def a default value to return
* @return the key parsed as a boolean, or def if the key isn't found or is
* not able to be parsed as a boolean.
*/
- public static boolean getBoolean(String key, boolean def) {
+ public static boolean getBoolean(@NonNull String key, boolean def) {
if (TRACK_KEY_ACCESS) onKeyAccess(key);
return native_get_boolean(key, def);
}
/**
- * Set the value for the given key.
- * @throws IllegalArgumentException if the value exceeds 92 characters
+ * Set the value for the given {@code key} to {@code val}.
+ *
+ * @throws IllegalArgumentException if the {@code val} exceeds 91 characters
*/
- public static void set(String key, String val) {
+ public static void set(@NonNull String key, @Nullable String val) {
if (val != null && val.length() > PROP_VALUE_MAX) {
- throw newValueTooLargeException(key, val);
+ throw new IllegalArgumentException("value of system property '" + key
+ + "' is longer than " + PROP_VALUE_MAX + " characters: " + val);
}
if (TRACK_KEY_ACCESS) onKeyAccess(key);
native_set(key, val);
}
- public static void addChangeCallback(Runnable callback) {
+ /**
+ * Add a callback that will be run whenever any system property changes.
+ *
+ * @param callback The {@link Runnable} that should be executed when a system property
+ * changes.
+ */
+ public static void addChangeCallback(@NonNull Runnable callback) {
synchronized (sChangeCallbacks) {
if (sChangeCallbacks.size() == 0) {
native_add_change_callback();
@@ -164,7 +186,8 @@ public class SystemProperties {
}
}
- static void callChangeCallbacks() {
+ @SuppressWarnings("unused") // Called from native code.
+ private static void callChangeCallbacks() {
synchronized (sChangeCallbacks) {
//Log.i("foo", "Calling " + sChangeCallbacks.size() + " change callbacks!");
if (sChangeCallbacks.size() == 0) {
@@ -177,11 +200,6 @@ public class SystemProperties {
}
}
- private static IllegalArgumentException newValueTooLargeException(String key, String value) {
- return new IllegalArgumentException("value of system property '" + key + "' is longer than "
- + PROP_VALUE_MAX + " characters: " + value);
- }
-
/*
* Notifies listeners that a system property has changed
*/
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 8208438dc62a..93826d808c3a 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -19,9 +19,11 @@ package android.os;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.util.Log;
+
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.Zygote;
import com.android.internal.util.Preconditions;
+
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.IOException;
@@ -172,7 +174,7 @@ public class ZygoteProcess {
*
* When invokeWith is not null, the process will be started as a fresh app
* and not a zygote fork. Note that this is only allowed for uid 0 or when
- * debugFlags contains DEBUG_ENABLE_DEBUGGER.
+ * runtimeFlags contains DEBUG_ENABLE_DEBUGGER.
*
* @param processClass The class to use as the process's main entry
* point.
@@ -180,7 +182,7 @@ public class ZygoteProcess {
* @param uid The user-id under which the process will run.
* @param gid The group-id under which the process will run.
* @param gids Additional group-ids associated with the process.
- * @param debugFlags Additional flags.
+ * @param runtimeFlags Additional flags.
* @param targetSdkVersion The target SDK version for the app.
* @param seInfo null-ok SELinux information for the new process.
* @param abi non-null the ABI this app should be started with.
@@ -195,7 +197,7 @@ public class ZygoteProcess {
public final Process.ProcessStartResult start(final String processClass,
final String niceName,
int uid, int gid, int[] gids,
- int debugFlags, int mountExternal,
+ int runtimeFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String abi,
@@ -205,7 +207,7 @@ public class ZygoteProcess {
String[] zygoteArgs) {
try {
return startViaZygote(processClass, niceName, uid, gid, gids,
- debugFlags, mountExternal, targetSdkVersion, seInfo,
+ runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
Log.e(LOG_TAG,
@@ -316,7 +318,7 @@ public class ZygoteProcess {
* @param gid a POSIX gid that the new process shuold setgid() to
* @param gids null-ok; a list of supplementary group IDs that the
* new process should setgroup() to.
- * @param debugFlags Additional flags.
+ * @param runtimeFlags Additional flags for the runtime.
* @param targetSdkVersion The target SDK version for the app.
* @param seInfo null-ok SELinux information for the new process.
* @param abi the ABI the process should use.
@@ -330,7 +332,7 @@ public class ZygoteProcess {
final String niceName,
final int uid, final int gid,
final int[] gids,
- int debugFlags, int mountExternal,
+ int runtimeFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String abi,
@@ -346,33 +348,7 @@ public class ZygoteProcess {
argsForZygote.add("--runtime-args");
argsForZygote.add("--setuid=" + uid);
argsForZygote.add("--setgid=" + gid);
- if ((debugFlags & Zygote.DEBUG_ENABLE_JNI_LOGGING) != 0) {
- argsForZygote.add("--enable-jni-logging");
- }
- if ((debugFlags & Zygote.DEBUG_ENABLE_SAFEMODE) != 0) {
- argsForZygote.add("--enable-safemode");
- }
- if ((debugFlags & Zygote.DEBUG_ENABLE_JDWP) != 0) {
- argsForZygote.add("--enable-jdwp");
- }
- if ((debugFlags & Zygote.DEBUG_ENABLE_CHECKJNI) != 0) {
- argsForZygote.add("--enable-checkjni");
- }
- if ((debugFlags & Zygote.DEBUG_GENERATE_DEBUG_INFO) != 0) {
- argsForZygote.add("--generate-debug-info");
- }
- if ((debugFlags & Zygote.DEBUG_ALWAYS_JIT) != 0) {
- argsForZygote.add("--always-jit");
- }
- if ((debugFlags & Zygote.DEBUG_NATIVE_DEBUGGABLE) != 0) {
- argsForZygote.add("--native-debuggable");
- }
- if ((debugFlags & Zygote.DEBUG_JAVA_DEBUGGABLE) != 0) {
- argsForZygote.add("--java-debuggable");
- }
- if ((debugFlags & Zygote.DEBUG_ENABLE_ASSERT) != 0) {
- argsForZygote.add("--enable-assert");
- }
+ argsForZygote.add("--runtime-flags=" + runtimeFlags);
if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) {
argsForZygote.add("--mount-external-default");
} else if (mountExternal == Zygote.MOUNT_EXTERNAL_READ) {
@@ -487,8 +463,8 @@ public class ZygoteProcess {
* Instructs the zygote to pre-load the classes and native libraries at the given paths
* for the specified abi. Not all zygotes support this function.
*/
- public void preloadPackageForAbi(String packagePath, String libsPath, String cacheKey,
- String abi) throws ZygoteStartFailedEx, IOException {
+ public boolean preloadPackageForAbi(String packagePath, String libsPath, String cacheKey,
+ String abi) throws ZygoteStartFailedEx, IOException {
synchronized(mLock) {
ZygoteState state = openZygoteSocketIfNeeded(abi);
state.writer.write("4");
@@ -507,6 +483,8 @@ public class ZygoteProcess {
state.writer.newLine();
state.writer.flush();
+
+ return (state.inputStream.readInt() == 0);
}
}
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index f4be1289eb70..66475e445efa 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -31,6 +31,7 @@ import android.util.Slog;
import com.android.internal.logging.AndroidConfig;
import com.android.server.NetworkManagementSocketTagger;
import dalvik.system.VMRuntime;
+import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.TimeZone;
@@ -228,8 +229,8 @@ public class RuntimeInit {
* @param argv Argument vector for main()
* @param classLoader the classLoader to load {@className} with
*/
- private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader)
- throws Zygote.MethodAndArgsCaller {
+ private static Runnable findStaticMain(String className, String[] argv,
+ ClassLoader classLoader) {
Class<?> cl;
try {
@@ -263,7 +264,7 @@ public class RuntimeInit {
* clears up all the stack frames that were required in setting
* up the process.
*/
- throw new Zygote.MethodAndArgsCaller(m, argv);
+ return new MethodAndArgsCaller(m, argv);
}
public static final void main(String[] argv) {
@@ -286,8 +287,8 @@ public class RuntimeInit {
if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!");
}
- protected static void applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
- throws Zygote.MethodAndArgsCaller {
+ protected static Runnable applicationInit(int targetSdkVersion, String[] argv,
+ ClassLoader classLoader) {
// If the application calls System.exit(), terminate the process
// immediately without running any shutdown hooks. It is not possible to
// shutdown an Android application gracefully. Among other things, the
@@ -300,20 +301,13 @@ public class RuntimeInit {
VMRuntime.getRuntime().setTargetHeapUtilization(0.75f);
VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
- final Arguments args;
- try {
- args = new Arguments(argv);
- } catch (IllegalArgumentException ex) {
- Slog.e(TAG, ex.getMessage());
- // let the process exit
- return;
- }
+ final Arguments args = new Arguments(argv);
// The end of of the RuntimeInit event (see #zygoteInit).
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
// Remaining arguments are passed to the start class's static main
- invokeStaticMain(args.startClass, args.startArgs, classLoader);
+ return findStaticMain(args.startClass, args.startArgs, classLoader);
}
/**
@@ -422,4 +416,37 @@ public class RuntimeInit {
System.arraycopy(args, curArg, startArgs, 0, startArgs.length);
}
}
+
+ /**
+ * Helper class which holds a method and arguments and can call them. This is used as part of
+ * a trampoline to get rid of the initial process setup stack frames.
+ */
+ static class MethodAndArgsCaller implements Runnable {
+ /** method to call */
+ private final Method mMethod;
+
+ /** argument array */
+ private final String[] mArgs;
+
+ public MethodAndArgsCaller(Method method, String[] args) {
+ mMethod = method;
+ mArgs = args;
+ }
+
+ public void run() {
+ try {
+ mMethod.invoke(null, new Object[] { mArgs });
+ } catch (IllegalAccessException ex) {
+ throw new RuntimeException(ex);
+ } catch (InvocationTargetException ex) {
+ Throwable cause = ex.getCause();
+ if (cause instanceof RuntimeException) {
+ throw (RuntimeException) cause;
+ } else if (cause instanceof Error) {
+ throw (Error) cause;
+ }
+ throw new RuntimeException(ex);
+ }
+ }
+ }
}
diff --git a/core/java/com/android/internal/os/WebViewZygoteInit.java b/core/java/com/android/internal/os/WebViewZygoteInit.java
index e28079fd5cdd..7f46a0c1abde 100644
--- a/core/java/com/android/internal/os/WebViewZygoteInit.java
+++ b/core/java/com/android/internal/os/WebViewZygoteInit.java
@@ -26,6 +26,7 @@ import android.util.Log;
import android.webkit.WebViewFactory;
import android.webkit.WebViewFactoryProvider;
+import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
@@ -68,8 +69,7 @@ class WebViewZygoteInit {
}
@Override
- protected boolean handlePreloadPackage(String packagePath, String libsPath,
- String cacheKey) {
+ protected void handlePreloadPackage(String packagePath, String libsPath, String cacheKey) {
Log.i(TAG, "Beginning package preload");
// 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.
@@ -87,19 +87,28 @@ class WebViewZygoteInit {
// 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.
+ boolean preloadSucceeded = false;
try {
Class<WebViewFactoryProvider> providerClass =
WebViewFactory.getWebViewProviderClass(loader);
Object result = providerClass.getMethod("preloadInZygote").invoke(null);
- if (!((Boolean)result).booleanValue()) {
+ preloadSucceeded = ((Boolean) result).booleanValue();
+ if (!preloadSucceeded) {
Log.e(TAG, "preloadInZygote returned false");
}
} catch (ClassNotFoundException | NoSuchMethodException | SecurityException |
IllegalAccessException | InvocationTargetException e) {
Log.e(TAG, "Exception while preloading package", e);
}
+
+ try {
+ DataOutputStream socketOut = getSocketOutputStream();
+ socketOut.writeInt(preloadSucceeded ? 1 : 0);
+ } catch (IOException ioe) {
+ throw new IllegalStateException("Error writing to command socket", ioe);
+ }
+
Log.i(TAG, "Package preload done");
- return false;
}
}
@@ -113,16 +122,23 @@ class WebViewZygoteInit {
throw new RuntimeException("Failed to setpgid(0,0)", ex);
}
+ final Runnable caller;
try {
sServer.registerServerSocket("webview_zygote");
- sServer.runSelectLoop(TextUtils.join(",", Build.SUPPORTED_ABIS));
- sServer.closeServerSocket();
- } catch (Zygote.MethodAndArgsCaller caller) {
- caller.run();
+ // The select loop returns early in the child process after a fork and
+ // loops forever in the zygote.
+ caller = sServer.runSelectLoop(TextUtils.join(",", Build.SUPPORTED_ABIS));
} catch (RuntimeException e) {
Log.e(TAG, "Fatal exception:", e);
+ throw e;
+ } finally {
+ sServer.closeServerSocket();
}
- System.exit(0);
+ // We're in the child process and have exited the select loop. Proceed to execute the
+ // command.
+ if (caller != null) {
+ caller.run();
+ }
}
}
diff --git a/core/java/com/android/internal/os/WrapperInit.java b/core/java/com/android/internal/os/WrapperInit.java
index 608bc9ff33d3..89328b21ccf5 100644
--- a/core/java/com/android/internal/os/WrapperInit.java
+++ b/core/java/com/android/internal/os/WrapperInit.java
@@ -25,7 +25,6 @@ import android.system.StructCapUserData;
import android.system.StructCapUserHeader;
import android.util.BootTimingsTraceLog;
import android.util.Slog;
-import com.android.internal.os.Zygote.MethodAndArgsCaller;
import dalvik.system.VMRuntime;
import java.io.DataOutputStream;
import java.io.FileDescriptor;
@@ -61,37 +60,35 @@ public class WrapperInit {
* @param args The command-line arguments.
*/
public static void main(String[] args) {
- try {
- // Parse our mandatory arguments.
- int fdNum = Integer.parseInt(args[0], 10);
- int targetSdkVersion = Integer.parseInt(args[1], 10);
+ // Parse our mandatory arguments.
+ int fdNum = Integer.parseInt(args[0], 10);
+ int targetSdkVersion = Integer.parseInt(args[1], 10);
- // Tell the Zygote what our actual PID is (since it only knows about the
- // wrapper that it directly forked).
- if (fdNum != 0) {
- try {
- FileDescriptor fd = new FileDescriptor();
- fd.setInt$(fdNum);
- DataOutputStream os = new DataOutputStream(new FileOutputStream(fd));
- os.writeInt(Process.myPid());
- os.close();
- IoUtils.closeQuietly(fd);
- } catch (IOException ex) {
- Slog.d(TAG, "Could not write pid of wrapped process to Zygote pipe.", ex);
- }
+ // Tell the Zygote what our actual PID is (since it only knows about the
+ // wrapper that it directly forked).
+ if (fdNum != 0) {
+ try {
+ FileDescriptor fd = new FileDescriptor();
+ fd.setInt$(fdNum);
+ DataOutputStream os = new DataOutputStream(new FileOutputStream(fd));
+ os.writeInt(Process.myPid());
+ os.close();
+ IoUtils.closeQuietly(fd);
+ } catch (IOException ex) {
+ Slog.d(TAG, "Could not write pid of wrapped process to Zygote pipe.", ex);
}
+ }
- // Mimic system Zygote preloading.
- ZygoteInit.preload(new BootTimingsTraceLog("WrapperInitTiming",
- Trace.TRACE_TAG_DALVIK));
+ // Mimic system Zygote preloading.
+ ZygoteInit.preload(new BootTimingsTraceLog("WrapperInitTiming",
+ Trace.TRACE_TAG_DALVIK));
- // Launch the application.
- String[] runtimeArgs = new String[args.length - 2];
- System.arraycopy(args, 2, runtimeArgs, 0, runtimeArgs.length);
- WrapperInit.wrapperInit(targetSdkVersion, runtimeArgs);
- } catch (Zygote.MethodAndArgsCaller caller) {
- caller.run();
- }
+ // Launch the application.
+ String[] runtimeArgs = new String[args.length - 2];
+ System.arraycopy(args, 2, runtimeArgs, 0, runtimeArgs.length);
+ Runnable r = wrapperInit(targetSdkVersion, runtimeArgs);
+
+ r.run();
}
/**
@@ -142,8 +139,7 @@ public class WrapperInit {
* @param targetSdkVersion target SDK version
* @param argv arg strings
*/
- private static void wrapperInit(int targetSdkVersion, String[] argv)
- throws Zygote.MethodAndArgsCaller {
+ private static Runnable wrapperInit(int targetSdkVersion, String[] argv) {
if (RuntimeInit.DEBUG) {
Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from wrapper");
}
@@ -165,7 +161,7 @@ public class WrapperInit {
// Perform the same initialization that would happen after the Zygote forks.
Zygote.nativePreApplicationInit();
- RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
+ return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
}
/**
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 91d9d1e43cf7..c85f41774b04 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -28,7 +28,7 @@ import java.lang.reflect.Method;
/** @hide */
public final class Zygote {
/*
- * Bit values for "debugFlags" argument. The definitions are duplicated
+ * Bit values for "runtimeFlags" argument. The definitions are duplicated
* in the native code.
*/
@@ -75,7 +75,7 @@ public final class Zygote {
* fork()ing and and before spawning any threads.
* @param gids null-ok; a list of UNIX gids that the new process should
* setgroups() to after fork and before spawning any threads.
- * @param debugFlags bit flags that enable debugging features.
+ * @param runtimeFlags bit flags that enable ART features.
* @param rlimits null-ok an array of rlimit tuples, with the second
* dimension having a length of 3 and representing
* (resource, rlim_cur, rlim_max). These are set via the posix
@@ -96,14 +96,14 @@ public final class Zygote {
* @return 0 if this is the child, pid of the child
* if this is the parent, or -1 on error.
*/
- public static int forkAndSpecialize(int uid, int gid, int[] gids, int debugFlags,
+ public static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
int[] fdsToIgnore, String instructionSet, String appDataDir) {
VM_HOOKS.preFork();
// Resets nice priority for zygote process.
resetNicePriority();
int pid = nativeForkAndSpecialize(
- uid, gid, gids, debugFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
+ uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
fdsToIgnore, instructionSet, appDataDir);
// Enable tracing as soon as possible for the child process.
if (pid == 0) {
@@ -116,7 +116,7 @@ public final class Zygote {
return pid;
}
- native private static int nativeForkAndSpecialize(int uid, int gid, int[] gids,int debugFlags,
+ native private static int nativeForkAndSpecialize(int uid, int gid, int[] gids,int runtimeFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
int[] fdsToIgnore, String instructionSet, String appDataDir);
@@ -137,7 +137,7 @@ public final class Zygote {
* fork()ing and and before spawning any threads.
* @param gids null-ok; a list of UNIX gids that the new process should
* setgroups() to after fork and before spawning any threads.
- * @param debugFlags bit flags that enable debugging features.
+ * @param runtimeFlags bit flags that enable ART features.
* @param rlimits null-ok an array of rlimit tuples, with the second
* dimension having a length of 3 and representing
* (resource, rlim_cur, rlim_max). These are set via the posix
@@ -148,13 +148,13 @@ public final class Zygote {
* @return 0 if this is the child, pid of the child
* if this is the parent, or -1 on error.
*/
- public static int forkSystemServer(int uid, int gid, int[] gids, int debugFlags,
+ public static int forkSystemServer(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) {
VM_HOOKS.preFork();
// Resets nice priority for zygote process.
resetNicePriority();
int pid = nativeForkSystemServer(
- uid, gid, gids, debugFlags, rlimits, permittedCapabilities, effectiveCapabilities);
+ uid, gid, gids, runtimeFlags, rlimits, permittedCapabilities, effectiveCapabilities);
// Enable tracing as soon as we enter the system_server.
if (pid == 0) {
Trace.setTracingEnabled(true);
@@ -163,7 +163,7 @@ public final class Zygote {
return pid;
}
- native private static int nativeForkSystemServer(int uid, int gid, int[] gids, int debugFlags,
+ native private static int nativeForkSystemServer(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, long permittedCapabilities, long effectiveCapabilities);
/**
@@ -177,9 +177,9 @@ public final class Zygote {
*/
native protected static void nativeUnmountStorageOnInit();
- private static void callPostForkChildHooks(int debugFlags, boolean isSystemServer,
+ private static void callPostForkChildHooks(int runtimeFlags, boolean isSystemServer,
String instructionSet) {
- VM_HOOKS.postForkChild(debugFlags, isSystemServer, instructionSet);
+ VM_HOOKS.postForkChild(runtimeFlags, isSystemServer, instructionSet);
}
/**
@@ -221,39 +221,4 @@ public final class Zygote {
command.append(" '").append(arg.replace("'", "'\\''")).append("'");
}
}
-
- /**
- * Helper exception class which holds a method and arguments and
- * can call them. This is used as part of a trampoline to get rid of
- * the initial process setup stack frames.
- */
- public static class MethodAndArgsCaller extends Exception
- implements Runnable {
- /** method to call */
- private final Method mMethod;
-
- /** argument array */
- private final String[] mArgs;
-
- public MethodAndArgsCaller(Method method, String[] args) {
- mMethod = method;
- mArgs = args;
- }
-
- public void run() {
- try {
- mMethod.invoke(null, new Object[] { mArgs });
- } catch (IllegalAccessException ex) {
- throw new RuntimeException(ex);
- } catch (InvocationTargetException ex) {
- Throwable cause = ex.getCause();
- if (cause instanceof RuntimeException) {
- throw (RuntimeException) cause;
- } else if (cause instanceof Error) {
- throw (Error) cause;
- }
- throw new RuntimeException(ex);
- }
- }
- }
}
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 05f43e4abbf5..0bb7326a682b 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -30,7 +30,6 @@ import android.net.Credentials;
import android.net.LocalSocket;
import android.os.FactoryTest;
import android.os.Process;
-import android.os.SELinux;
import android.os.SystemProperties;
import android.os.Trace;
import android.system.ErrnoException;
@@ -42,14 +41,13 @@ import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
+import java.io.EOFException;
import java.io.FileDescriptor;
-import java.io.FileOutputStream;
+import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
-import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
-import java.util.Arrays;
import libcore.io.IoUtils;
/**
@@ -73,6 +71,7 @@ class ZygoteConnection {
private final BufferedReader mSocketReader;
private final Credentials peer;
private final String abiList;
+ private boolean isEof;
/**
* Constructs instance from connected socket.
@@ -99,6 +98,8 @@ class ZygoteConnection {
Log.e(TAG, "Cannot read peer credentials", ex);
throw ex;
}
+
+ isEof = false;
}
/**
@@ -111,21 +112,14 @@ class ZygoteConnection {
}
/**
- * Reads one start command from the command socket. If successful,
- * a child is forked and a {@link Zygote.MethodAndArgsCaller}
- * exception is thrown in that child while in the parent process,
- * the method returns normally. On failure, the child is not
- * spawned and messages are printed to the log and stderr. Returns
- * a boolean status value indicating whether an end-of-file on the command
- * socket has been encountered.
+ * Reads one start command from the command socket. If successful, a child is forked and a
+ * {@code Runnable} that calls the childs main method (or equivalent) is returned in the child
+ * process. {@code null} is always returned in the parent process (the zygote).
*
- * @return false if command socket should continue to be read from, or
- * true if an end-of-file has been encountered.
- * @throws Zygote.MethodAndArgsCaller trampoline to invoke main()
- * method in child process
+ * If the client closes the socket, an {@code EOF} condition is set, which callers can test
+ * for by calling {@code ZygoteConnection.isClosedByPeer}.
*/
- boolean runOnce(ZygoteServer zygoteServer) throws Zygote.MethodAndArgsCaller {
-
+ Runnable processOneCommand(ZygoteServer zygoteServer) {
String args[];
Arguments parsedArgs = null;
FileDescriptor[] descriptors;
@@ -134,130 +128,120 @@ class ZygoteConnection {
args = readArgumentList();
descriptors = mSocket.getAncillaryFileDescriptors();
} catch (IOException ex) {
- Log.w(TAG, "IOException on command socket " + ex.getMessage());
- closeSocket();
- return true;
+ throw new IllegalStateException("IOException on command socket", ex);
}
+ // readArgumentList returns null only when it has reached EOF with no available
+ // data to read. This will only happen when the remote socket has disconnected.
if (args == null) {
- // EOF reached.
- closeSocket();
- return true;
- }
-
- /** the stderr of the most recent request, if avail */
- PrintStream newStderr = null;
-
- if (descriptors != null && descriptors.length >= 3) {
- newStderr = new PrintStream(
- new FileOutputStream(descriptors[2]));
+ isEof = true;
+ return null;
}
int pid = -1;
FileDescriptor childPipeFd = null;
FileDescriptor serverPipeFd = null;
- try {
- parsedArgs = new Arguments(args);
+ parsedArgs = new Arguments(args);
- if (parsedArgs.abiListQuery) {
- return handleAbiListQuery();
- }
+ if (parsedArgs.abiListQuery) {
+ handleAbiListQuery();
+ return null;
+ }
- if (parsedArgs.preloadDefault) {
- return handlePreload();
- }
+ if (parsedArgs.preloadDefault) {
+ handlePreload();
+ return null;
+ }
- if (parsedArgs.preloadPackage != null) {
- return handlePreloadPackage(parsedArgs.preloadPackage,
- parsedArgs.preloadPackageLibs, parsedArgs.preloadPackageCacheKey);
- }
+ if (parsedArgs.preloadPackage != null) {
+ handlePreloadPackage(parsedArgs.preloadPackage, parsedArgs.preloadPackageLibs,
+ parsedArgs.preloadPackageCacheKey);
+ return null;
+ }
- if (parsedArgs.permittedCapabilities != 0 || parsedArgs.effectiveCapabilities != 0) {
- throw new ZygoteSecurityException("Client may not specify capabilities: " +
- "permitted=0x" + Long.toHexString(parsedArgs.permittedCapabilities) +
- ", effective=0x" + Long.toHexString(parsedArgs.effectiveCapabilities));
- }
+ if (parsedArgs.permittedCapabilities != 0 || parsedArgs.effectiveCapabilities != 0) {
+ throw new ZygoteSecurityException("Client may not specify capabilities: " +
+ "permitted=0x" + Long.toHexString(parsedArgs.permittedCapabilities) +
+ ", effective=0x" + Long.toHexString(parsedArgs.effectiveCapabilities));
+ }
- applyUidSecurityPolicy(parsedArgs, peer);
- applyInvokeWithSecurityPolicy(parsedArgs, peer);
+ applyUidSecurityPolicy(parsedArgs, peer);
+ applyInvokeWithSecurityPolicy(parsedArgs, peer);
- applyDebuggerSystemProperty(parsedArgs);
- applyInvokeWithSystemProperty(parsedArgs);
+ applyDebuggerSystemProperty(parsedArgs);
+ applyInvokeWithSystemProperty(parsedArgs);
- int[][] rlimits = null;
+ int[][] rlimits = null;
- if (parsedArgs.rlimits != null) {
- rlimits = parsedArgs.rlimits.toArray(intArray2d);
- }
+ if (parsedArgs.rlimits != null) {
+ rlimits = parsedArgs.rlimits.toArray(intArray2d);
+ }
- int[] fdsToIgnore = null;
+ int[] fdsToIgnore = null;
- if (parsedArgs.invokeWith != null) {
+ if (parsedArgs.invokeWith != null) {
+ try {
FileDescriptor[] pipeFds = Os.pipe2(O_CLOEXEC);
childPipeFd = pipeFds[1];
serverPipeFd = pipeFds[0];
Os.fcntlInt(childPipeFd, F_SETFD, 0);
- fdsToIgnore = new int[] { childPipeFd.getInt$(), serverPipeFd.getInt$() };
+ fdsToIgnore = new int[]{childPipeFd.getInt$(), serverPipeFd.getInt$()};
+ } catch (ErrnoException errnoEx) {
+ throw new IllegalStateException("Unable to set up pipe for invoke-with", errnoEx);
}
+ }
- /**
- * In order to avoid leaking descriptors to the Zygote child,
- * the native code must close the two Zygote socket descriptors
- * in the child process before it switches from Zygote-root to
- * the UID and privileges of the application being launched.
- *
- * In order to avoid "bad file descriptor" errors when the
- * two LocalSocket objects are closed, the Posix file
- * descriptors are released via a dup2() call which closes
- * the socket and substitutes an open descriptor to /dev/null.
- */
+ /**
+ * In order to avoid leaking descriptors to the Zygote child,
+ * the native code must close the two Zygote socket descriptors
+ * in the child process before it switches from Zygote-root to
+ * the UID and privileges of the application being launched.
+ *
+ * In order to avoid "bad file descriptor" errors when the
+ * two LocalSocket objects are closed, the Posix file
+ * descriptors are released via a dup2() call which closes
+ * the socket and substitutes an open descriptor to /dev/null.
+ */
- int [] fdsToClose = { -1, -1 };
+ int [] fdsToClose = { -1, -1 };
- FileDescriptor fd = mSocket.getFileDescriptor();
+ FileDescriptor fd = mSocket.getFileDescriptor();
- if (fd != null) {
- fdsToClose[0] = fd.getInt$();
- }
+ if (fd != null) {
+ fdsToClose[0] = fd.getInt$();
+ }
- fd = zygoteServer.getServerSocketFileDescriptor();
+ fd = zygoteServer.getServerSocketFileDescriptor();
- if (fd != null) {
- fdsToClose[1] = fd.getInt$();
- }
+ if (fd != null) {
+ fdsToClose[1] = fd.getInt$();
+ }
- fd = null;
+ fd = null;
- pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
- parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
- parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.instructionSet,
- parsedArgs.appDataDir);
- } catch (ErrnoException ex) {
- logAndPrintError(newStderr, "Exception creating pipe", ex);
- } catch (IllegalArgumentException ex) {
- logAndPrintError(newStderr, "Invalid zygote arguments", ex);
- } catch (ZygoteSecurityException ex) {
- logAndPrintError(newStderr,
- "Zygote security policy prevents request: ", ex);
- }
+ pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
+ parsedArgs.runtimeFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
+ parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.instructionSet,
+ parsedArgs.appDataDir);
try {
if (pid == 0) {
// in child
+ zygoteServer.setForkChild();
+
zygoteServer.closeServerSocket();
IoUtils.closeQuietly(serverPipeFd);
serverPipeFd = null;
- handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
- // should never get here, the child is expected to either
- // throw Zygote.MethodAndArgsCaller or exec().
- return true;
+ return handleChildProc(parsedArgs, descriptors, childPipeFd);
} else {
- // in parent...pid of < 0 means failure
+ // In the parent. A pid < 0 indicates a failure and will be handled in
+ // handleParentProc.
IoUtils.closeQuietly(childPipeFd);
childPipeFd = null;
- return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
+ handleParentProc(pid, descriptors, serverPipeFd);
+ return null;
}
} finally {
IoUtils.closeQuietly(childPipeFd);
@@ -265,15 +249,13 @@ class ZygoteConnection {
}
}
- private boolean handleAbiListQuery() {
+ private void handleAbiListQuery() {
try {
final byte[] abiListBytes = abiList.getBytes(StandardCharsets.US_ASCII);
mSocketOutStream.writeInt(abiListBytes.length);
mSocketOutStream.write(abiListBytes);
- return false;
} catch (IOException ioe) {
- Log.e(TAG, "Error writing to command socket", ioe);
- return true;
+ throw new IllegalStateException("Error writing to command socket", ioe);
}
}
@@ -283,7 +265,7 @@ class ZygoteConnection {
* if no preload was initiated. The latter implies that the zygote is not configured to load
* resources lazy or that the zygote has already handled a previous request to handlePreload.
*/
- private boolean handlePreload() {
+ private void handlePreload() {
try {
if (isPreloadComplete()) {
mSocketOutStream.writeInt(1);
@@ -291,11 +273,8 @@ class ZygoteConnection {
preload();
mSocketOutStream.writeInt(0);
}
-
- return false;
} catch (IOException ioe) {
- Log.e(TAG, "Error writing to command socket", ioe);
- return true;
+ throw new IllegalStateException("Error writing to command socket", ioe);
}
}
@@ -307,7 +286,11 @@ class ZygoteConnection {
return ZygoteInit.isPreloadComplete();
}
- protected boolean handlePreloadPackage(String packagePath, String libsPath, String cacheKey) {
+ protected DataOutputStream getSocketOutputStream() {
+ return mSocketOutStream;
+ }
+
+ protected void handlePreloadPackage(String packagePath, String libsPath, String cacheKey) {
throw new RuntimeException("Zyogte does not support package preloading");
}
@@ -323,6 +306,10 @@ class ZygoteConnection {
}
}
+ boolean isClosedByPeer() {
+ return isEof;
+ }
+
/**
* Handles argument parsing for args related to the zygote spawner.
*
@@ -363,11 +350,9 @@ class ZygoteConnection {
int[] gids;
/**
- * From --enable-jdwp, --enable-checkjni, --enable-assert,
- * --enable-safemode, --generate-debug-info, --enable-jni-logging,
- * --java-debuggable, and --native-debuggable.
+ * From --runtime-flags.
*/
- int debugFlags;
+ int runtimeFlags;
/** From --mount-external */
int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
@@ -483,26 +468,11 @@ class ZygoteConnection {
targetSdkVersionSpecified = true;
targetSdkVersion = Integer.parseInt(
arg.substring(arg.indexOf('=') + 1));
- } else if (arg.equals("--enable-jdwp")) {
- debugFlags |= Zygote.DEBUG_ENABLE_JDWP;
- } else if (arg.equals("--enable-safemode")) {
- debugFlags |= Zygote.DEBUG_ENABLE_SAFEMODE;
- } else if (arg.equals("--enable-checkjni")) {
- debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
- } else if (arg.equals("--generate-debug-info")) {
- debugFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO;
- } else if (arg.equals("--always-jit")) {
- debugFlags |= Zygote.DEBUG_ALWAYS_JIT;
- } else if (arg.equals("--native-debuggable")) {
- debugFlags |= Zygote.DEBUG_NATIVE_DEBUGGABLE;
- } else if (arg.equals("--java-debuggable")) {
- debugFlags |= Zygote.DEBUG_JAVA_DEBUGGABLE;
- } else if (arg.equals("--enable-jni-logging")) {
- debugFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING;
- } else if (arg.equals("--enable-assert")) {
- debugFlags |= Zygote.DEBUG_ENABLE_ASSERT;
} else if (arg.equals("--runtime-args")) {
seenRuntimeArgs = true;
+ } else if (arg.startsWith("--runtime-flags=")) {
+ runtimeFlags = Integer.parseInt(
+ arg.substring(arg.indexOf('=') + 1));
} else if (arg.startsWith("--seinfo=")) {
if (seInfoSpecified) {
throw new IllegalArgumentException(
@@ -718,7 +688,7 @@ class ZygoteConnection {
*/
public static void applyDebuggerSystemProperty(Arguments args) {
if (RoSystemProperties.DEBUGGABLE) {
- args.debugFlags |= Zygote.DEBUG_ENABLE_JDWP;
+ args.runtimeFlags |= Zygote.DEBUG_ENABLE_JDWP;
}
}
@@ -740,7 +710,7 @@ class ZygoteConnection {
int peerUid = peer.getUid();
if (args.invokeWith != null && peerUid != 0 &&
- (args.debugFlags & Zygote.DEBUG_ENABLE_JDWP) == 0) {
+ (args.runtimeFlags & Zygote.DEBUG_ENABLE_JDWP) == 0) {
throw new ZygoteSecurityException("Peer is permitted to specify an"
+ "explicit invoke-with wrapper command only for debuggable"
+ "applications.");
@@ -770,15 +740,9 @@ class ZygoteConnection {
* @param parsedArgs non-null; zygote args
* @param descriptors null-ok; new file descriptors for stdio if available.
* @param pipeFd null-ok; pipe for communication back to Zygote.
- * @param newStderr null-ok; stream to use for stderr until stdio
- * is reopened.
- *
- * @throws Zygote.MethodAndArgsCaller on success to
- * trampoline to code that invokes static main.
*/
- private void handleChildProc(Arguments parsedArgs,
- FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
- throws Zygote.MethodAndArgsCaller {
+ private Runnable handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors,
+ FileDescriptor pipeFd) {
/**
* By the time we get here, the native code has closed the two actual Zygote
* socket connections, and substituted /dev/null in their place. The LocalSocket
@@ -795,7 +759,6 @@ class ZygoteConnection {
for (FileDescriptor fd: descriptors) {
IoUtils.closeQuietly(fd);
}
- newStderr = System.err;
} catch (ErrnoException ex) {
Log.e(TAG, "Error reopening stdio", ex);
}
@@ -812,9 +775,12 @@ class ZygoteConnection {
parsedArgs.niceName, parsedArgs.targetSdkVersion,
VMRuntime.getCurrentInstructionSet(),
pipeFd, parsedArgs.remainingArgs);
+
+ // Should not get here.
+ throw new IllegalStateException("WrapperInit.execApplication unexpectedly returned");
} else {
- ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion,
- parsedArgs.remainingArgs, null /* classLoader */);
+ return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs,
+ null /* classLoader */);
}
}
@@ -826,13 +792,8 @@ class ZygoteConnection {
* @param descriptors null-ok; file descriptors for child's new stdio if
* specified.
* @param pipeFd null-ok; pipe for communication with child.
- * @param parsedArgs non-null; zygote args
- * @return true for "exit command loop" and false for "continue command
- * loop"
*/
- private boolean handleParentProc(int pid,
- FileDescriptor[] descriptors, FileDescriptor pipeFd, Arguments parsedArgs) {
-
+ private void handleParentProc(int pid, FileDescriptor[] descriptors, FileDescriptor pipeFd) {
if (pid > 0) {
setChildPgid(pid);
}
@@ -924,11 +885,8 @@ class ZygoteConnection {
mSocketOutStream.writeInt(pid);
mSocketOutStream.writeBoolean(usingWrapper);
} catch (IOException ex) {
- Log.e(TAG, "Error writing to command socket", ex);
- return true;
+ throw new IllegalStateException("Error writing to command socket", ex);
}
-
- return false;
}
private void setChildPgid(int pid) {
@@ -944,20 +902,4 @@ class ZygoteConnection {
+ "normal if peer is not in our session");
}
}
-
- /**
- * Logs an error message and prints it to the specified stream, if
- * provided
- *
- * @param newStderr null-ok; a standard error stream
- * @param message non-null; error message
- * @param ex null-ok an exception
- */
- private static void logAndPrintError (PrintStream newStderr,
- String message, Throwable ex) {
- Log.e(TAG, message, ex);
- if (newStderr != null) {
- newStderr.println(message + (ex == null ? "" : ex));
- }
- }
}
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index c8c7ed9761bb..ee19163f5bf9 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -24,7 +24,6 @@ import android.content.res.TypedArray;
import android.icu.impl.CacheValue;
import android.icu.text.DecimalFormatSymbols;
import android.icu.util.ULocale;
-import android.net.LocalServerSocket;
import android.opengl.EGL14;
import android.os.Build;
import android.os.IInstalld;
@@ -447,10 +446,7 @@ public class ZygoteInit {
/**
* Finish remaining work for the newly forked system server process.
*/
- private static void handleSystemServerProcess(
- ZygoteConnection.Arguments parsedArgs)
- throws Zygote.MethodAndArgsCaller {
-
+ private static Runnable handleSystemServerProcess(ZygoteConnection.Arguments parsedArgs) {
// set umask to 0077 so new files and directories will default to owner-only permissions.
Os.umask(S_IRWXG | S_IRWXO);
@@ -496,6 +492,8 @@ public class ZygoteInit {
WrapperInit.execApplication(parsedArgs.invokeWith,
parsedArgs.niceName, parsedArgs.targetSdkVersion,
VMRuntime.getCurrentInstructionSet(), null, args);
+
+ throw new IllegalStateException("Unexpected return from WrapperInit.execApplication");
} else {
ClassLoader cl = null;
if (systemServerClasspath != null) {
@@ -507,7 +505,7 @@ public class ZygoteInit {
/*
* Pass the remaining arguments to SystemServer.
*/
- ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
+ return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
}
/* should never reach here */
@@ -589,10 +587,13 @@ public class ZygoteInit {
}
/**
- * Prepare the arguments and fork for the system server process.
+ * Prepare the arguments and forks for the system server process.
+ *
+ * Returns an {@code Runnable} that provides an entrypoint into system_server code in the
+ * child process, and {@code null} in the parent.
*/
- private static boolean startSystemServer(String abiList, String socketName, ZygoteServer zygoteServer)
- throws Zygote.MethodAndArgsCaller, RuntimeException {
+ private static Runnable forkSystemServer(String abiList, String socketName,
+ ZygoteServer zygoteServer) {
long capabilities = posixCapabilitiesAsBits(
OsConstants.CAP_IPC_LOCK,
OsConstants.CAP_KILL,
@@ -642,7 +643,7 @@ public class ZygoteInit {
pid = Zygote.forkSystemServer(
parsedArgs.uid, parsedArgs.gid,
parsedArgs.gids,
- parsedArgs.debugFlags,
+ parsedArgs.runtimeFlags,
null,
parsedArgs.permittedCapabilities,
parsedArgs.effectiveCapabilities);
@@ -657,10 +658,10 @@ public class ZygoteInit {
}
zygoteServer.closeServerSocket();
- handleSystemServerProcess(parsedArgs);
+ return handleSystemServerProcess(parsedArgs);
}
- return true;
+ return null;
}
/**
@@ -691,6 +692,7 @@ public class ZygoteInit {
throw new RuntimeException("Failed to setpgid(0,0)", ex);
}
+ final Runnable caller;
try {
// Report Zygote start time to tron unless it is a runtime restart
if (!"1".equals(SystemProperties.get("sys.boot_completed"))) {
@@ -760,19 +762,32 @@ public class ZygoteInit {
ZygoteHooks.stopZygoteNoThreadCreation();
if (startSystemServer) {
- startSystemServer(abiList, socketName, zygoteServer);
+ Runnable r = forkSystemServer(abiList, socketName, zygoteServer);
+
+ // {@code r == null} in the parent (zygote) process, and {@code r != null} in the
+ // child (system_server) process.
+ if (r != null) {
+ r.run();
+ return;
+ }
}
Log.i(TAG, "Accepting command socket connections");
- zygoteServer.runSelectLoop(abiList);
- zygoteServer.closeServerSocket();
- } catch (Zygote.MethodAndArgsCaller caller) {
- caller.run();
+ // The select loop returns early in the child process after a fork and
+ // loops forever in the zygote.
+ caller = zygoteServer.runSelectLoop(abiList);
} catch (Throwable ex) {
Log.e(TAG, "System zygote died with exception", ex);
- zygoteServer.closeServerSocket();
throw ex;
+ } finally {
+ zygoteServer.closeServerSocket();
+ }
+
+ // We're in the child process and have exited the select loop. Proceed to execute the
+ // command.
+ if (caller != null) {
+ caller.run();
}
}
@@ -830,8 +845,7 @@ public class ZygoteInit {
* @param targetSdkVersion target SDK version
* @param argv arg strings
*/
- public static final void zygoteInit(int targetSdkVersion, String[] argv,
- ClassLoader classLoader) throws Zygote.MethodAndArgsCaller {
+ public static final Runnable zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) {
if (RuntimeInit.DEBUG) {
Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote");
}
@@ -841,7 +855,7 @@ public class ZygoteInit {
RuntimeInit.commonInit();
ZygoteInit.nativeZygoteInit();
- RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
+ return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
}
private static final native void nativeZygoteInit();
diff --git a/core/java/com/android/internal/os/ZygoteServer.java b/core/java/com/android/internal/os/ZygoteServer.java
index 126d9e7db2ff..8baa15a058de 100644
--- a/core/java/com/android/internal/os/ZygoteServer.java
+++ b/core/java/com/android/internal/os/ZygoteServer.java
@@ -25,6 +25,7 @@ import android.system.ErrnoException;
import android.system.StructPollfd;
import android.util.Log;
+import android.util.Slog;
import java.io.IOException;
import java.io.FileDescriptor;
import java.util.ArrayList;
@@ -45,9 +46,18 @@ class ZygoteServer {
private LocalServerSocket mServerSocket;
+ /**
+ * Set by the child process, immediately after a call to {@code Zygote.forkAndSpecialize}.
+ */
+ private boolean mIsForkChild;
+
ZygoteServer() {
}
+ void setForkChild() {
+ mIsForkChild = true;
+ }
+
/**
* Registers a server socket for zygote command connections
*
@@ -129,11 +139,8 @@ class ZygoteServer {
* Runs the zygote process's select loop. Accepts new connections as
* they happen, and reads commands from connections one spawn-request's
* worth at a time.
- *
- * @throws Zygote.MethodAndArgsCaller in a child process when a main()
- * should be executed.
*/
- void runSelectLoop(String abiList) throws Zygote.MethodAndArgsCaller {
+ Runnable runSelectLoop(String abiList) {
ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
@@ -156,15 +163,62 @@ class ZygoteServer {
if ((pollFds[i].revents & POLLIN) == 0) {
continue;
}
+
if (i == 0) {
ZygoteConnection newPeer = acceptCommandPeer(abiList);
peers.add(newPeer);
fds.add(newPeer.getFileDesciptor());
} else {
- boolean done = peers.get(i).runOnce(this);
- if (done) {
- peers.remove(i);
- fds.remove(i);
+ try {
+ ZygoteConnection connection = peers.get(i);
+ final Runnable command = connection.processOneCommand(this);
+
+ if (mIsForkChild) {
+ // We're in the child. We should always have a command to run at this
+ // stage if processOneCommand hasn't called "exec".
+ if (command == null) {
+ throw new IllegalStateException("command == null");
+ }
+
+ return command;
+ } else {
+ // We're in the server - we should never have any commands to run.
+ if (command != null) {
+ throw new IllegalStateException("command != null");
+ }
+
+ // We don't know whether the remote side of the socket was closed or
+ // not until we attempt to read from it from processOneCommand. This shows up as
+ // a regular POLLIN event in our regular processing loop.
+ if (connection.isClosedByPeer()) {
+ connection.closeSocket();
+ peers.remove(i);
+ fds.remove(i);
+ }
+ }
+ } catch (Exception e) {
+ if (!mIsForkChild) {
+ // We're in the server so any exception here is one that has taken place
+ // pre-fork while processing commands or reading / writing from the
+ // control socket. Make a loud noise about any such exceptions so that
+ // we know exactly what failed and why.
+
+ Slog.e(TAG, "Exception executing zygote command: ", e);
+
+ // Make sure the socket is closed so that the other end knows immediately
+ // that something has gone wrong and doesn't time out waiting for a
+ // response.
+ ZygoteConnection conn = peers.remove(i);
+ conn.closeSocket();
+
+ fds.remove(i);
+ } else {
+ // We're in the child so any exception caught here has happened post
+ // fork and before we execute ActivityThread.main (or any other main()
+ // method). Log the details of the exception and bring down the process.
+ Log.e(TAG, "Caught post-fork exception in child process.", e);
+ throw e;
+ }
}
}
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index abd6278b056a..c5279e10d93f 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -231,6 +231,7 @@ cc_library_shared {
"libutils",
"libbinder",
"libui",
+ "libgraphicsenv",
"libgui",
"libsensor",
"libinput",
diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp
index 399dec8fc9cf..f749488f39b0 100644
--- a/core/jni/android_os_GraphicsEnvironment.cpp
+++ b/core/jni/android_os_GraphicsEnvironment.cpp
@@ -16,7 +16,7 @@
#define LOG_TAG "GraphicsEnvironment"
-#include <ui/GraphicsEnv.h>
+#include <graphicsenv/GraphicsEnv.h>
#include <nativehelper/ScopedUtfChars.h>
#include "core_jni_helpers.h"
diff --git a/core/jni/android_os_SystemProperties.cpp b/core/jni/android_os_SystemProperties.cpp
index 8844fb0a261f..a94cac0f18f5 100644
--- a/core/jni/android_os_SystemProperties.cpp
+++ b/core/jni/android_os_SystemProperties.cpp
@@ -17,188 +17,109 @@
#define LOG_TAG "SysPropJNI"
+#include "android-base/logging.h"
+#include "android-base/properties.h"
#include "cutils/properties.h"
#include "utils/misc.h"
#include <utils/Log.h>
#include "jni.h"
#include "core_jni_helpers.h"
#include <nativehelper/JNIHelp.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
+#include <nativehelper/ScopedUtfChars.h>
namespace android
{
-static jstring SystemProperties_getSS(JNIEnv *env, jobject clazz,
- jstring keyJ, jstring defJ)
-{
- int len;
- const char* key;
- char buf[PROPERTY_VALUE_MAX];
- jstring rvJ = NULL;
-
- if (keyJ == NULL) {
- jniThrowNullPointerException(env, "key must not be null.");
- goto error;
- }
-
- key = env->GetStringUTFChars(keyJ, NULL);
-
- len = property_get(key, buf, "");
- if ((len <= 0) && (defJ != NULL)) {
- rvJ = defJ;
- } else if (len >= 0) {
- rvJ = env->NewStringUTF(buf);
- } else {
- rvJ = env->NewStringUTF("");
+namespace {
+
+template <typename T, typename Handler>
+T ConvertKeyAndForward(JNIEnv *env, jstring keyJ, T defJ, Handler handler) {
+ std::string key;
+ {
+ // Scope the String access. If the handler can throw an exception,
+ // releasing the string characters late would trigger an abort.
+ ScopedUtfChars key_utf(env, keyJ);
+ if (key_utf.c_str() == nullptr) {
+ return defJ;
+ }
+ key = key_utf.c_str(); // This will make a copy, but we can't avoid
+ // with the existing interface in
+ // android::base.
}
-
- env->ReleaseStringUTFChars(keyJ, key);
-
-error:
- return rvJ;
+ return handler(key, defJ);
}
-static jstring SystemProperties_getS(JNIEnv *env, jobject clazz,
- jstring keyJ)
+jstring SystemProperties_getSS(JNIEnv *env, jclass clazz, jstring keyJ,
+ jstring defJ)
{
- return SystemProperties_getSS(env, clazz, keyJ, NULL);
+ // Using ConvertKeyAndForward is sub-optimal for copying the key string,
+ // but improves reuse and reasoning over code.
+ auto handler = [&](const std::string& key, jstring defJ) {
+ std::string prop_val = android::base::GetProperty(key, "");
+ if (!prop_val.empty()) {
+ return env->NewStringUTF(prop_val.c_str());
+ };
+ if (defJ != nullptr) {
+ return defJ;
+ }
+ // This function is specified to never return null (or have an
+ // exception pending).
+ return env->NewStringUTF("");
+ };
+ return ConvertKeyAndForward(env, keyJ, defJ, handler);
}
-static jint SystemProperties_get_int(JNIEnv *env, jobject clazz,
- jstring keyJ, jint defJ)
+jstring SystemProperties_getS(JNIEnv *env, jclass clazz, jstring keyJ)
{
- int len;
- const char* key;
- char buf[PROPERTY_VALUE_MAX];
- char* end;
- jint result = defJ;
-
- if (keyJ == NULL) {
- jniThrowNullPointerException(env, "key must not be null.");
- goto error;
- }
-
- key = env->GetStringUTFChars(keyJ, NULL);
-
- len = property_get(key, buf, "");
- if (len > 0) {
- result = strtol(buf, &end, 0);
- if (end == buf) {
- result = defJ;
- }
- }
-
- env->ReleaseStringUTFChars(keyJ, key);
-
-error:
- return result;
+ return SystemProperties_getSS(env, clazz, keyJ, nullptr);
}
-static jlong SystemProperties_get_long(JNIEnv *env, jobject clazz,
- jstring keyJ, jlong defJ)
+template <typename T>
+T SystemProperties_get_integral(JNIEnv *env, jclass, jstring keyJ,
+ T defJ)
{
- int len;
- const char* key;
- char buf[PROPERTY_VALUE_MAX];
- char* end;
- jlong result = defJ;
-
- if (keyJ == NULL) {
- jniThrowNullPointerException(env, "key must not be null.");
- goto error;
- }
-
- key = env->GetStringUTFChars(keyJ, NULL);
-
- len = property_get(key, buf, "");
- if (len > 0) {
- result = strtoll(buf, &end, 0);
- if (end == buf) {
- result = defJ;
- }
- }
-
- env->ReleaseStringUTFChars(keyJ, key);
-
-error:
- return result;
+ auto handler = [](const std::string& key, T defV) {
+ return android::base::GetIntProperty<T>(key, defV);
+ };
+ return ConvertKeyAndForward(env, keyJ, defJ, handler);
}
-static jboolean SystemProperties_get_boolean(JNIEnv *env, jobject clazz,
- jstring keyJ, jboolean defJ)
+jboolean SystemProperties_get_boolean(JNIEnv *env, jclass, jstring keyJ,
+ jboolean defJ)
{
- int len;
- const char* key;
- char buf[PROPERTY_VALUE_MAX];
- jboolean result = defJ;
-
- if (keyJ == NULL) {
- jniThrowNullPointerException(env, "key must not be null.");
- goto error;
- }
-
- key = env->GetStringUTFChars(keyJ, NULL);
-
- len = property_get(key, buf, "");
- if (len == 1) {
- char ch = buf[0];
- if (ch == '0' || ch == 'n')
- result = false;
- else if (ch == '1' || ch == 'y')
- result = true;
- } else if (len > 1) {
- if (!strcmp(buf, "no") || !strcmp(buf, "false") || !strcmp(buf, "off")) {
- result = false;
- } else if (!strcmp(buf, "yes") || !strcmp(buf, "true") || !strcmp(buf, "on")) {
- result = true;
- }
- }
-
- env->ReleaseStringUTFChars(keyJ, key);
-
-error:
- return result;
+ auto handler = [](const std::string& key, jboolean defV) -> jboolean {
+ bool result = android::base::GetBoolProperty(key, defV);
+ return result ? JNI_TRUE : JNI_FALSE;
+ };
+ return ConvertKeyAndForward(env, keyJ, defJ, handler);
}
-static void SystemProperties_set(JNIEnv *env, jobject clazz,
- jstring keyJ, jstring valJ)
+void SystemProperties_set(JNIEnv *env, jobject clazz, jstring keyJ,
+ jstring valJ)
{
- int err;
- const char* key;
- const char* val;
-
- if (keyJ == NULL) {
- jniThrowNullPointerException(env, "key must not be null.");
- return ;
- }
- key = env->GetStringUTFChars(keyJ, NULL);
-
- if (valJ == NULL) {
- val = ""; /* NULL pointer not allowed here */
- } else {
- val = env->GetStringUTFChars(valJ, NULL);
- }
-
- err = property_set(key, val);
-
- env->ReleaseStringUTFChars(keyJ, key);
-
- if (valJ != NULL) {
- env->ReleaseStringUTFChars(valJ, val);
- }
-
- if (err < 0) {
+ auto handler = [&](const std::string& key, bool) {
+ std::string val;
+ if (valJ != nullptr) {
+ ScopedUtfChars key_utf(env, valJ);
+ val = key_utf.c_str();
+ }
+ return android::base::SetProperty(key, val);
+ };
+ if (!ConvertKeyAndForward(env, keyJ, true, handler)) {
+ // Must have been a failure in SetProperty.
jniThrowException(env, "java/lang/RuntimeException",
"failed to set system property");
}
}
-static JavaVM* sVM = NULL;
-static jclass sClazz = NULL;
-static jmethodID sCallChangeCallbacks;
+JavaVM* sVM = nullptr;
+jclass sClazz = nullptr;
+jmethodID sCallChangeCallbacks;
-static void do_report_sysprop_change() {
+void do_report_sysprop_change() {
//ALOGI("Java SystemProperties: VM=%p, Clazz=%p", sVM, sClazz);
- if (sVM != NULL && sClazz != NULL) {
+ if (sVM != nullptr && sClazz != nullptr) {
JNIEnv* env;
if (sVM->GetEnv((void **)&env, JNI_VERSION_1_4) >= 0) {
//ALOGI("Java SystemProperties: calling %p", sCallChangeCallbacks);
@@ -207,47 +128,49 @@ static void do_report_sysprop_change() {
}
}
-static void SystemProperties_add_change_callback(JNIEnv *env, jobject clazz)
+void SystemProperties_add_change_callback(JNIEnv *env, jobject clazz)
{
// This is called with the Java lock held.
- if (sVM == NULL) {
+ if (sVM == nullptr) {
env->GetJavaVM(&sVM);
}
- if (sClazz == NULL) {
+ if (sClazz == nullptr) {
sClazz = (jclass) env->NewGlobalRef(clazz);
sCallChangeCallbacks = env->GetStaticMethodID(sClazz, "callChangeCallbacks", "()V");
add_sysprop_change_callback(do_report_sysprop_change, -10000);
}
}
-static void SystemProperties_report_sysprop_change(JNIEnv /**env*/, jobject /*clazz*/)
+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 },
- { "native_get", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
- (void*) SystemProperties_getSS },
- { "native_get_int", "(Ljava/lang/String;I)I",
- (void*) SystemProperties_get_int },
- { "native_get_long", "(Ljava/lang/String;J)J",
- (void*) SystemProperties_get_long },
- { "native_get_boolean", "(Ljava/lang/String;Z)Z",
- (void*) SystemProperties_get_boolean },
- { "native_set", "(Ljava/lang/String;Ljava/lang/String;)V",
- (void*) SystemProperties_set },
- { "native_add_change_callback", "()V",
- (void*) SystemProperties_add_change_callback },
- { "native_report_sysprop_change", "()V",
- (void*) SystemProperties_report_sysprop_change },
-};
+} // namespace
int register_android_os_SystemProperties(JNIEnv *env)
{
- return RegisterMethodsOrDie(env, "android/os/SystemProperties", method_table,
- NELEM(method_table));
+ const JNINativeMethod method_table[] = {
+ { "native_get", "(Ljava/lang/String;)Ljava/lang/String;",
+ (void*) SystemProperties_getS },
+ { "native_get",
+ "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
+ (void*) SystemProperties_getSS },
+ { "native_get_int", "(Ljava/lang/String;I)I",
+ (void*) SystemProperties_get_integral<jint> },
+ { "native_get_long", "(Ljava/lang/String;J)J",
+ (void*) SystemProperties_get_integral<jlong> },
+ { "native_get_boolean", "(Ljava/lang/String;Z)Z",
+ (void*) SystemProperties_get_boolean },
+ { "native_set", "(Ljava/lang/String;Ljava/lang/String;)V",
+ (void*) SystemProperties_set },
+ { "native_add_change_callback", "()V",
+ (void*) SystemProperties_add_change_callback },
+ { "native_report_sysprop_change", "()V",
+ (void*) SystemProperties_report_sysprop_change },
+ };
+ return RegisterMethodsOrDie(env, "android/os/SystemProperties",
+ method_table, NELEM(method_table));
}
};
diff --git a/core/jni/android_os_VintfObject.cpp b/core/jni/android_os_VintfObject.cpp
index 7ec4b8ea0799..5ef2a9e6465c 100644
--- a/core/jni/android_os_VintfObject.cpp
+++ b/core/jni/android_os_VintfObject.cpp
@@ -56,7 +56,7 @@ static inline jobjectArray toJavaStringArray(JNIEnv* env, const V& v) {
}
template<typename T>
-static void tryAddSchema(const T* object, const XmlConverter<T>& converter,
+static void tryAddSchema(const std::shared_ptr<const T>& object, const XmlConverter<T>& converter,
const std::string& description,
std::vector<std::string>* cStrings) {
if (object == nullptr) {
@@ -66,7 +66,7 @@ static void tryAddSchema(const T* object, const XmlConverter<T>& converter,
}
}
-static void tryAddHalNamesAndVersions(const HalManifest *manifest,
+static void tryAddHalNamesAndVersions(const std::shared_ptr<const HalManifest>& manifest,
const std::string& description,
std::set<std::string> *output) {
if (manifest == nullptr) {
@@ -119,7 +119,7 @@ static jobjectArray android_os_VintfObject_getHalNamesAndVersions(JNIEnv* env, j
}
static jstring android_os_VintfObject_getSepolicyVersion(JNIEnv* env, jclass) {
- const HalManifest *manifest = VintfObject::GetDeviceHalManifest();
+ std::shared_ptr<const HalManifest> manifest = VintfObject::GetDeviceHalManifest();
if (manifest == nullptr || manifest->type() != SchemaType::DEVICE) {
LOG(WARNING) << __FUNCTION__ << "Cannot get device manifest";
return nullptr;
@@ -129,7 +129,7 @@ static jstring android_os_VintfObject_getSepolicyVersion(JNIEnv* env, jclass) {
}
static jobject android_os_VintfObject_getVndkSnapshots(JNIEnv* env, jclass) {
- const HalManifest *manifest = VintfObject::GetFrameworkHalManifest();
+ std::shared_ptr<const HalManifest> manifest = VintfObject::GetFrameworkHalManifest();
if (manifest == nullptr || manifest->type() != SchemaType::FRAMEWORK) {
LOG(WARNING) << __FUNCTION__ << "Cannot get framework manifest";
return nullptr;
diff --git a/core/jni/android_os_VintfRuntimeInfo.cpp b/core/jni/android_os_VintfRuntimeInfo.cpp
index 19220cf05adb..315eac1b9414 100644
--- a/core/jni/android_os_VintfRuntimeInfo.cpp
+++ b/core/jni/android_os_VintfRuntimeInfo.cpp
@@ -32,7 +32,7 @@ using vintf::VintfObject;
#define MAP_STRING_METHOD(javaMethod, cppString) \
static jstring android_os_VintfRuntimeInfo_##javaMethod(JNIEnv* env, jclass clazz) \
{ \
- const RuntimeInfo *info = VintfObject::GetRuntimeInfo(); \
+ std::shared_ptr<const RuntimeInfo> info = VintfObject::GetRuntimeInfo(); \
if (info == nullptr) return nullptr; \
return env->NewStringUTF((cppString).c_str()); \
} \
@@ -50,7 +50,7 @@ MAP_STRING_METHOD(getBootVbmetaAvbVersion, vintf::to_string(info->bootVbmetaAvbV
static jlong android_os_VintfRuntimeInfo_getKernelSepolicyVersion(JNIEnv *env, jclass clazz)
{
- const RuntimeInfo *info = VintfObject::GetRuntimeInfo();
+ std::shared_ptr<const RuntimeInfo> info = VintfObject::GetRuntimeInfo();
if (info == nullptr) return 0;
return static_cast<jlong>(info->kernelSepolicyVersion());
}
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 914688e3e885..b08f031681c5 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -477,7 +477,7 @@ static void FillFileDescriptorVector(JNIEnv* env,
// Utility routine to fork zygote and specialize the child process.
static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGids,
- jint debug_flags, jobjectArray javaRlimits,
+ jint runtime_flags, jobjectArray javaRlimits,
jlong permittedCapabilities, jlong effectiveCapabilities,
jint mount_external,
jstring java_se_info, jstring java_se_name,
@@ -658,7 +658,7 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra
UnsetSigChldHandler();
- env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, debug_flags,
+ env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, runtime_flags,
is_system_server, instructionSet);
if (env->ExceptionCheck()) {
RuntimeAbort(env, __LINE__, "Error calling post fork hooks.");
@@ -700,7 +700,7 @@ static void com_android_internal_os_Zygote_nativePreApplicationInit(JNIEnv*, jcl
static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(
JNIEnv* env, jclass, jint uid, jint gid, jintArray gids,
- jint debug_flags, jobjectArray rlimits,
+ jint runtime_flags, jobjectArray rlimits,
jint mount_external, jstring se_info, jstring se_name,
jintArray fdsToClose,
jintArray fdsToIgnore,
@@ -744,17 +744,17 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(
// available.
capabilities &= GetEffectiveCapabilityMask(env);
- return ForkAndSpecializeCommon(env, uid, gid, gids, debug_flags,
+ return ForkAndSpecializeCommon(env, uid, gid, gids, runtime_flags,
rlimits, capabilities, capabilities, mount_external, se_info,
se_name, false, fdsToClose, fdsToIgnore, instructionSet, appDataDir);
}
static jint com_android_internal_os_Zygote_nativeForkSystemServer(
JNIEnv* env, jclass, uid_t uid, gid_t gid, jintArray gids,
- jint debug_flags, jobjectArray rlimits, jlong permittedCapabilities,
+ jint runtime_flags, jobjectArray rlimits, jlong permittedCapabilities,
jlong effectiveCapabilities) {
pid_t pid = ForkAndSpecializeCommon(env, uid, gid, gids,
- debug_flags, rlimits,
+ runtime_flags, rlimits,
permittedCapabilities, effectiveCapabilities,
MOUNT_EXTERNAL_DEFAULT, NULL, NULL, true, NULL,
NULL, NULL, NULL);
diff --git a/core/res/res/values-mcc505-mnc01/config.xml b/core/res/res/values-mcc505-mnc01/config.xml
index ff065856a643..5a5b8f78478e 100644
--- a/core/res/res/values-mcc505-mnc01/config.xml
+++ b/core/res/res/values-mcc505-mnc01/config.xml
@@ -31,15 +31,6 @@
<item>9</item>
</integer-array>
- <!-- String containing the apn value for tethering. May be overriden by secure settings
- TETHER_DUN_APN. Value is a comma separated series of strings:
- "name,apn,proxy,port,username,password,server,mmsc,mmsproxy,mmsport,mcc,mnc,auth,type",
- Or string format of ApnSettingV3.
- note that empty fields can be ommitted: "name,apn,,,,,,,,,310,260,,DUN" -->
- <string-array translatable="false" name="config_tether_apndata">
- <item>Telstra Tethering,telstra.internet,,,,,,,,,505,01,,DUN</item>
- </string-array>
-
<!--Thresholds for LTE dbm in status bar-->
<integer-array translatable="false" name="config_lteDbmThresholds">
<item>-140</item> <!-- SIGNAL_STRENGTH_NONE_OR_UNKNOWN -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 899551371f30..70c39afbc441 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -296,6 +296,17 @@
less than 0x600 -->
<bool translatable="false" name="config_apfDrop802_3Frames">true</bool>
+ <!-- An array of Black listed EtherType, packets with EtherTypes within this array
+ will be dropped
+ TODO: need to put proper values, these are for testing purposes only -->
+ <integer-array translatable="false" name="config_apfEthTypeBlackList">
+ <item>0x88A2</item>
+ <item>0x88A4</item>
+ <item>0x88B8</item>
+ <item>0x88CD</item>
+ <item>0x88E3</item>
+ </integer-array>
+
<!-- Default value for ConnectivityManager.getMultipathPreference() on metered networks. Actual
device behaviour is controlled by Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE.
This is the default value of that setting. -->
@@ -1493,16 +1504,16 @@
<integer translatable="false" name="config_bluetooth_max_advertisers">0</integer>
<!-- Idle current for bluetooth controller. 0 by default-->
- <integer translatable="false" name="config_bluetooth_idle_cur_ma">1</integer>
+ <integer translatable="false" name="config_bluetooth_idle_cur_ma">0</integer>
<!-- Rx current for bluetooth controller. 0 by default-->
- <integer translatable="false" name="config_bluetooth_rx_cur_ma">2</integer>
+ <integer translatable="false" name="config_bluetooth_rx_cur_ma">0</integer>
<!-- Tx current for bluetooth controller. 0 by default-->
- <integer translatable="false" name="config_bluetooth_tx_cur_ma">3</integer>
+ <integer translatable="false" name="config_bluetooth_tx_cur_ma">0</integer>
<!-- Operating volatage for bluetooth controller. 0 by default-->
- <integer translatable="false" name="config_bluetooth_operating_voltage_mv">4</integer>
+ <integer translatable="false" name="config_bluetooth_operating_voltage_mv">0</integer>
<!-- Whether supported profiles should be reloaded upon enabling bluetooth -->
<bool name="config_bluetooth_reload_supported_profiles_when_enabled">false</bool>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index c5275017a7cf..600c82f87d90 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1837,6 +1837,7 @@
<java-symbol type="integer" name="config_networkWakeupPacketMark" />
<java-symbol type="integer" name="config_networkWakeupPacketMask" />
<java-symbol type="bool" name="config_apfDrop802_3Frames" />
+ <java-symbol type="array" name="config_apfEthTypeBlackList" />
<java-symbol type="integer" name="config_networkMeteredMultipathPreference" />
<java-symbol type="integer" name="config_notificationsBatteryFullARGB" />
<java-symbol type="integer" name="config_notificationsBatteryLedOff" />
diff --git a/core/res/res/xml/sms_short_codes.xml b/core/res/res/xml/sms_short_codes.xml
index 7e9f561e68d5..f5b350b053d4 100644
--- a/core/res/res/xml/sms_short_codes.xml
+++ b/core/res/res/xml/sms_short_codes.xml
@@ -181,7 +181,7 @@
<shortcode country="mk" pattern="\\d{1,6}" free="129005|122" />
<!-- Mexico: 4-5 digits (not confirmed), known premium codes listed -->
- <shortcode country="mx" pattern="\\d{4,5}" premium="53035|7766" free="46645|5050|26259|50025|50052|9963" />
+ <shortcode country="mx" pattern="\\d{4,5}" premium="53035|7766" free="46645|5050|26259|50025|50052|9963|76551" />
<!-- Malaysia: 5 digits: http://www.skmm.gov.my/attachment/Consumer_Regulation/Mobile_Content_Services_FAQs.pdf -->
<shortcode country="my" pattern="\\d{5}" premium="32298|33776" free="22099|28288" />
diff --git a/core/tests/coretests/src/android/net/LinkPropertiesTest.java b/core/tests/coretests/src/android/net/LinkPropertiesTest.java
index d5f632190d06..bc2b9a632b72 100644
--- a/core/tests/coretests/src/android/net/LinkPropertiesTest.java
+++ b/core/tests/coretests/src/android/net/LinkPropertiesTest.java
@@ -19,15 +19,23 @@ package android.net;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
+import android.net.LinkProperties.CompareResult;
import android.net.LinkProperties.ProvisioningChange;
import android.net.RouteInfo;
import android.system.OsConstants;
import android.test.suitebuilder.annotation.SmallTest;
import android.test.suitebuilder.annotation.Suppress;
+import android.util.ArraySet;
+
import junit.framework.TestCase;
import java.net.InetAddress;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
public class LinkPropertiesTest extends TestCase {
@@ -678,4 +686,104 @@ public class LinkPropertiesTest extends TestCase {
stacked.addRoute(new RouteInfo((IpPrefix) null, stackedAddress));
assertTrue(v6lp.isReachable(DNS1));
}
+
+ @SmallTest
+ public void testLinkPropertiesEnsureDirectlyConnectedRoutes() {
+ // IPv4 case: no route added initially
+ LinkProperties rmnet0 = new LinkProperties();
+ rmnet0.setInterfaceName("rmnet0");
+ rmnet0.addLinkAddress(new LinkAddress("10.0.0.2/8"));
+ RouteInfo directRoute0 = new RouteInfo(new IpPrefix("10.0.0.0/8"), null,
+ rmnet0.getInterfaceName());
+
+ // Since no routes is added explicitly, getAllRoutes() should return empty.
+ assertTrue(rmnet0.getAllRoutes().isEmpty());
+ rmnet0.ensureDirectlyConnectedRoutes();
+ // ensureDirectlyConnectedRoutes() should have added the missing local route.
+ assertEqualRoutes(Collections.singletonList(directRoute0), rmnet0.getAllRoutes());
+
+ // IPv4 case: both direct and default routes added initially
+ LinkProperties rmnet1 = new LinkProperties();
+ rmnet1.setInterfaceName("rmnet1");
+ rmnet1.addLinkAddress(new LinkAddress("10.0.0.3/8"));
+ RouteInfo defaultRoute1 = new RouteInfo((IpPrefix) null,
+ NetworkUtils.numericToInetAddress("10.0.0.1"), rmnet1.getInterfaceName());
+ RouteInfo directRoute1 = new RouteInfo(new IpPrefix("10.0.0.0/8"), null,
+ rmnet1.getInterfaceName());
+ rmnet1.addRoute(defaultRoute1);
+ rmnet1.addRoute(directRoute1);
+
+ // Check added routes
+ assertEqualRoutes(Arrays.asList(defaultRoute1, directRoute1), rmnet1.getAllRoutes());
+ // ensureDirectlyConnectedRoutes() shouldn't change the routes since direct connected
+ // route is already part of the configuration.
+ rmnet1.ensureDirectlyConnectedRoutes();
+ assertEqualRoutes(Arrays.asList(defaultRoute1, directRoute1), rmnet1.getAllRoutes());
+
+ // IPv6 case: only default routes added initially
+ LinkProperties rmnet2 = new LinkProperties();
+ rmnet2.setInterfaceName("rmnet2");
+ rmnet2.addLinkAddress(new LinkAddress("fe80::cafe/64"));
+ rmnet2.addLinkAddress(new LinkAddress("2001:db8::2/64"));
+ RouteInfo defaultRoute2 = new RouteInfo((IpPrefix) null,
+ NetworkUtils.numericToInetAddress("2001:db8::1"), rmnet2.getInterfaceName());
+ RouteInfo directRoute2 = new RouteInfo(new IpPrefix("2001:db8::/64"), null,
+ rmnet2.getInterfaceName());
+ RouteInfo linkLocalRoute2 = new RouteInfo(new IpPrefix("fe80::/64"), null,
+ rmnet2.getInterfaceName());
+ rmnet2.addRoute(defaultRoute2);
+
+ assertEqualRoutes(Arrays.asList(defaultRoute2), rmnet2.getAllRoutes());
+ rmnet2.ensureDirectlyConnectedRoutes();
+ assertEqualRoutes(Arrays.asList(defaultRoute2, directRoute2, linkLocalRoute2),
+ rmnet2.getAllRoutes());
+
+ // Corner case: no interface name
+ LinkProperties rmnet3 = new LinkProperties();
+ rmnet3.addLinkAddress(new LinkAddress("192.168.0.2/24"));
+ RouteInfo directRoute3 = new RouteInfo(new IpPrefix("192.168.0.0/24"), null,
+ rmnet3.getInterfaceName());
+
+ assertTrue(rmnet3.getAllRoutes().isEmpty());
+ rmnet3.ensureDirectlyConnectedRoutes();
+ assertEqualRoutes(Collections.singletonList(directRoute3), rmnet3.getAllRoutes());
+
+ }
+
+ @SmallTest
+ public void testCompareResult() {
+ // Either adding or removing items
+ testCompareResult(Arrays.asList(1), Arrays.asList(1, 2, 3, 4),
+ Arrays.asList(2, 3, 4), new ArrayList<>());
+ testCompareResult(Arrays.asList(1, 2), Arrays.asList(3, 2, 1, 4),
+ new ArrayList<>(), Arrays.asList(3, 4));
+
+
+ // adding and removing items at the same time
+ testCompareResult(Arrays.asList(1, 2, 3, 4), Arrays.asList(2, 3, 4, 5),
+ Arrays.asList(1), Arrays.asList(5));
+ testCompareResult(Arrays.asList(1, 2, 3), Arrays.asList(4, 5, 6),
+ Arrays.asList(1, 2, 3), Arrays.asList(4, 5, 6));
+
+ // null cases
+ testCompareResult(Arrays.asList(1, 2, 3), null, Arrays.asList(1, 2, 3), new ArrayList<>());
+ testCompareResult(null, Arrays.asList(3, 2, 1), new ArrayList<>(), Arrays.asList(1, 2, 3));
+ testCompareResult(null, null, new ArrayList<>(), new ArrayList<>());
+ }
+
+ private void assertEqualRoutes(Collection<RouteInfo> expected, Collection<RouteInfo> actual) {
+ Set<RouteInfo> expectedSet = new ArraySet<>(expected);
+ Set<RouteInfo> actualSet = new ArraySet<>(actual);
+ // Duplicated entries in actual routes are considered failures
+ assertEquals(actual.size(), actualSet.size());
+
+ assertEquals(expectedSet, actualSet);
+ }
+
+ private <T> void testCompareResult(List<T> oldItems, List<T> newItems, List<T> expectAdded,
+ List<T> expectRemoved) {
+ CompareResult<T> result = new CompareResult<>(newItems, oldItems);
+ assertEquals(new ArraySet<>(expectAdded), new ArraySet<>(result.added));
+ assertEquals(new ArraySet<>(expectRemoved), (new ArraySet<>(result.removed)));
+ }
}
diff --git a/core/tests/systemproperties/run_core_systemproperties_test.sh b/core/tests/systemproperties/run_core_systemproperties_test.sh
index d39adbbfa390..9b1fe4be666b 100755
--- a/core/tests/systemproperties/run_core_systemproperties_test.sh
+++ b/core/tests/systemproperties/run_core_systemproperties_test.sh
@@ -15,7 +15,7 @@ fi
if [[ $rebuild == true ]]; then
make -j4 FrameworksCoreSystemPropertiesTests
- TESTAPP=${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreSystemPropertiesTests.apk
+ TESTAPP=${ANDROID_PRODUCT_OUT}/data/app/FrameworksCoreSystemPropertiesTests/FrameworksCoreSystemPropertiesTests.apk
COMMAND="adb install -r $TESTAPP"
echo $COMMAND
$COMMAND
diff --git a/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java b/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java
index 544a96727217..282b0011eede 100644
--- a/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java
+++ b/core/tests/systemproperties/src/android/os/SystemPropertiesTest.java
@@ -51,6 +51,11 @@ public class SystemPropertiesTest extends TestCase {
value = SystemProperties.get(KEY, "default");
assertEquals("default", value);
+ // null default value is the same as "".
+ SystemProperties.set(KEY, null);
+ value = SystemProperties.get(KEY, "default");
+ assertEquals("default", value);
+
SystemProperties.set(KEY, "SA");
value = SystemProperties.get(KEY, "default");
assertEquals("SA", value);
@@ -62,7 +67,78 @@ public class SystemPropertiesTest extends TestCase {
value = SystemProperties.get(KEY, "default");
assertEquals("default", value);
+ // null value is the same as "".
+ SystemProperties.set(KEY, "SA");
+ SystemProperties.set(KEY, null);
+ value = SystemProperties.get(KEY, "default");
+ assertEquals("default", value);
+
value = SystemProperties.get(KEY);
assertEquals("", value);
}
+
+ private static void testInt(String setVal, int defValue, int expected) {
+ SystemProperties.set(KEY, setVal);
+ int value = SystemProperties.getInt(KEY, defValue);
+ assertEquals(expected, value);
+ }
+
+ private static void testLong(String setVal, long defValue, long expected) {
+ SystemProperties.set(KEY, setVal);
+ long value = SystemProperties.getLong(KEY, defValue);
+ assertEquals(expected, value);
+ }
+
+ @SmallTest
+ public void testIntegralProperties() throws Exception {
+ testInt("", 123, 123);
+ testInt("", 0, 0);
+ testInt("", -123, -123);
+
+ testInt("123", 124, 123);
+ testInt("0", 124, 0);
+ testInt("-123", 124, -123);
+
+ testLong("", 3147483647L, 3147483647L);
+ testLong("", 0, 0);
+ testLong("", -3147483647L, -3147483647L);
+
+ testLong("3147483647", 124, 3147483647L);
+ testLong("0", 124, 0);
+ testLong("-3147483647", 124, -3147483647L);
+ }
+
+ @SmallTest
+ @SuppressWarnings("null")
+ public void testNullKey() throws Exception {
+ try {
+ SystemProperties.get(null);
+ fail("Expected NullPointerException");
+ } catch (NullPointerException npe) {
+ }
+
+ try {
+ SystemProperties.get(null, "default");
+ fail("Expected NullPointerException");
+ } catch (NullPointerException npe) {
+ }
+
+ try {
+ SystemProperties.set(null, "value");
+ fail("Expected NullPointerException");
+ } catch (NullPointerException npe) {
+ }
+
+ try {
+ SystemProperties.getInt(null, 0);
+ fail("Expected NullPointerException");
+ } catch (NullPointerException npe) {
+ }
+
+ try {
+ SystemProperties.getLong(null, 0);
+ fail("Expected NullPointerException");
+ } catch (NullPointerException npe) {
+ }
+ }
}
diff --git a/core/tests/utiltests/jni/Android.bp b/core/tests/utiltests/jni/Android.bp
index e9a4144e8164..b0b09c27e325 100644
--- a/core/tests/utiltests/jni/Android.bp
+++ b/core/tests/utiltests/jni/Android.bp
@@ -17,7 +17,6 @@ cc_library_shared {
shared_libs: [
"libcutils",
],
- clang: true,
stl: "libc++",
srcs: [
"registration.cpp",
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 0782269d7de1..7a0ef2b770ce 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -59,7 +59,6 @@ namespace android {
#endif
#define IDMAP_MAGIC 0x504D4449
-#define IDMAP_CURRENT_VERSION 0x00000001
#define APP_PACKAGE_ID 0x7f
#define SYS_PACKAGE_ID 0x01
@@ -246,11 +245,11 @@ static bool assertIdmapHeader(const void* idmap, size_t size) {
}
const uint32_t version = htodl(*(reinterpret_cast<const uint32_t*>(idmap) + 1));
- if (version != IDMAP_CURRENT_VERSION) {
+ if (version != ResTable::IDMAP_CURRENT_VERSION) {
// We are strict about versions because files with this format are
// auto-generated and don't need backwards compatibility.
ALOGW("idmap: version mismatch in header (is 0x%08x, expected 0x%08x)",
- version, IDMAP_CURRENT_VERSION);
+ version, ResTable::IDMAP_CURRENT_VERSION);
return false;
}
return true;
@@ -6855,7 +6854,7 @@ status_t ResTable::createIdmap(const ResTable& overlay,
uint32_t* data = (uint32_t*)*outData;
*data++ = htodl(IDMAP_MAGIC);
- *data++ = htodl(IDMAP_CURRENT_VERSION);
+ *data++ = htodl(ResTable::IDMAP_CURRENT_VERSION);
*data++ = htodl(targetCrc);
*data++ = htodl(overlayCrc);
const char* paths[] = { targetPath, overlayPath };
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index 7a6e37d41b7c..66c66c251d9b 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -1933,6 +1933,7 @@ public:
void** outData, size_t* outSize) const;
static const size_t IDMAP_HEADER_SIZE_BYTES = 4 * sizeof(uint32_t) + 2 * 256;
+ static const uint32_t IDMAP_CURRENT_VERSION = 0x00000001;
// Retrieve idmap meta-data.
//
diff --git a/libs/androidfw/include/androidfw/StringPiece.h b/libs/androidfw/include/androidfw/StringPiece.h
index a873d66803e7..99b424568a1f 100644
--- a/libs/androidfw/include/androidfw/StringPiece.h
+++ b/libs/androidfw/include/androidfw/StringPiece.h
@@ -37,6 +37,7 @@ class BasicStringPiece {
public:
using const_iterator = const TChar*;
using difference_type = size_t;
+ using size_type = size_t;
// End of string marker.
constexpr static const size_t npos = static_cast<size_t>(-1);
diff --git a/libs/usb/Android.bp b/libs/usb/Android.bp
new file mode 100644
index 000000000000..b8f29043e597
--- /dev/null
+++ b/libs/usb/Android.bp
@@ -0,0 +1 @@
+subdirs = ["tests/*"]
diff --git a/libs/usb/tests/AccessoryChat/Android.bp b/libs/usb/tests/AccessoryChat/Android.bp
new file mode 100644
index 000000000000..4af6274b7ece
--- /dev/null
+++ b/libs/usb/tests/AccessoryChat/Android.bp
@@ -0,0 +1 @@
+subdirs = ["accessorychat"]
diff --git a/libs/usb/tests/AccessoryChat/accessorychat/Android.bp b/libs/usb/tests/AccessoryChat/accessorychat/Android.bp
new file mode 100644
index 000000000000..5613745e966b
--- /dev/null
+++ b/libs/usb/tests/AccessoryChat/accessorychat/Android.bp
@@ -0,0 +1,30 @@
+cc_binary {
+ name: "accessorychat",
+ host_supported: true,
+
+ srcs: ["accessorychat.c"],
+ cflags: [
+ "-Werror",
+ "-Wno-unused-parameter",
+ ],
+
+ target: {
+ android: {
+ shared_libs: [
+ "libusbhost",
+ "libcutils",
+ ],
+ },
+ host: {
+ static_libs: [
+ "libusbhost",
+ "libcutils",
+ ],
+
+ cflags: ["-O0"],
+ },
+ darwin: {
+ enabled: false,
+ },
+ },
+}
diff --git a/libs/usb/tests/AccessoryChat/accessorychat/Android.mk b/libs/usb/tests/AccessoryChat/accessorychat/Android.mk
deleted file mode 100644
index 51f2111f1e0b..000000000000
--- a/libs/usb/tests/AccessoryChat/accessorychat/Android.mk
+++ /dev/null
@@ -1,35 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-# Build for Linux (desktop) host
-ifeq ($(HOST_OS),linux)
-
-include $(CLEAR_VARS)
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := accessorychat.c
-
-LOCAL_MODULE := accessorychat
-
-LOCAL_STATIC_LIBRARIES := libusbhost libcutils
-LOCAL_LDLIBS += -lpthread
-LOCAL_CFLAGS := -g -O0
-
-include $(BUILD_HOST_EXECUTABLE)
-
-endif
-
-# Build for device
-include $(CLEAR_VARS)
-LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := accessorychat.c
-
-LOCAL_MODULE := accessorychat
-
-LOCAL_SHARED_LIBRARIES := libusbhost libcutils
-
-include $(BUILD_EXECUTABLE)
diff --git a/libs/usb/tests/accessorytest/Android.bp b/libs/usb/tests/accessorytest/Android.bp
new file mode 100644
index 000000000000..c6340e32e60c
--- /dev/null
+++ b/libs/usb/tests/accessorytest/Android.bp
@@ -0,0 +1,28 @@
+cc_binary_host {
+ name: "accessorytest",
+
+ srcs: [
+ "accessory.c",
+ "audio.c",
+ "hid.c",
+ "usb.c",
+ ],
+
+ static_libs: [
+ "libusbhost",
+ "libcutils",
+ "libtinyalsa",
+ ],
+ cflags: [
+ "-O0",
+ "-Wno-unused-parameter",
+ "-Werror",
+ ],
+
+ target: {
+ darwin: {
+ // Build for Linux host only
+ enabled: false,
+ },
+ },
+}
diff --git a/libs/usb/tests/accessorytest/Android.mk b/libs/usb/tests/accessorytest/Android.mk
deleted file mode 100644
index 6d9a9460c675..000000000000
--- a/libs/usb/tests/accessorytest/Android.mk
+++ /dev/null
@@ -1,25 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-# Build for Linux host only
-ifeq ($(HOST_OS),linux)
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := accessory.c \
- audio.c \
- hid.c \
- usb.c
-
-LOCAL_C_INCLUDES += external/tinyalsa/include
-
-LOCAL_MODULE := accessorytest
-
-LOCAL_STATIC_LIBRARIES := libusbhost libcutils libtinyalsa
-LOCAL_LDLIBS += -lpthread
-LOCAL_CFLAGS := -g -O0
-
-include $(BUILD_HOST_EXECUTABLE)
-
-endif
diff --git a/libs/usb/tests/accessorytest/accessory.h b/libs/usb/tests/accessorytest/accessory.h
index 55c4550f79e0..e86986eb403f 100644
--- a/libs/usb/tests/accessorytest/accessory.h
+++ b/libs/usb/tests/accessorytest/accessory.h
@@ -19,7 +19,7 @@
int init_audio(unsigned int ic, unsigned int id, unsigned int oc, unsigned int od);
void init_hid();
-void usb_run(int enable_accessory);
+void usb_run(uintptr_t enable_accessory);
struct usb_device* usb_wait_for_device();
diff --git a/libs/usb/tests/accessorytest/hid.c b/libs/usb/tests/accessorytest/hid.c
index b70d678543ca..283755dbf215 100644
--- a/libs/usb/tests/accessorytest/hid.c
+++ b/libs/usb/tests/accessorytest/hid.c
@@ -139,7 +139,7 @@ static void open_hid(const char* name)
fprintf(stderr, "opened /dev/%s\n", name);
pthread_t th;
- pthread_create(&th, NULL, hid_thread, (void *)fd);
+ pthread_create(&th, NULL, hid_thread, (void *)(uintptr_t)fd);
}
static void* inotify_thread(void* arg)
diff --git a/libs/usb/tests/accessorytest/usb.c b/libs/usb/tests/accessorytest/usb.c
index ac72b35b908e..1a161adf8ac2 100644
--- a/libs/usb/tests/accessorytest/usb.c
+++ b/libs/usb/tests/accessorytest/usb.c
@@ -219,7 +219,7 @@ struct usb_device* usb_wait_for_device() {
return device;
}
-void usb_run(int enable_accessory) {
+void usb_run(uintptr_t enable_accessory) {
struct usb_host_context* context = usb_host_init();
usb_host_run(context, usb_device_added, usb_device_removed, NULL, (void *)enable_accessory);
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
index c1f03fd81151..c79c018d34a6 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
@@ -170,7 +170,8 @@ public class CaptivePortalLoginActivity extends Activity {
}
private void done(boolean success) {
- if (DBG) logd(String.format("Result success %b for %s", success, mUrl.toString()));
+ if (DBG) logd(String.format("Result success %b for %s", success,
+ mUrl != null ? mUrl.toString() : "null"));
if (success) {
// Trigger re-evaluation upon success http response code
CarrierActionUtils.applyCarrierAction(
@@ -226,7 +227,8 @@ public class CaptivePortalLoginActivity extends Activity {
int httpResponseCode = 500;
int oldTag = TrafficStats.getAndSetThreadStatsTag(TrafficStats.TAG_SYSTEM_PROBE);
try {
- urlConnection = (HttpURLConnection) mNetwork.openConnection(mUrl);
+ urlConnection = (HttpURLConnection) mNetwork.openConnection(
+ new URL(mCm.getCaptivePortalServerUrl()));
urlConnection.setInstanceFollowRedirects(false);
urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
@@ -234,6 +236,7 @@ public class CaptivePortalLoginActivity extends Activity {
urlConnection.getInputStream();
httpResponseCode = urlConnection.getResponseCode();
} catch (IOException e) {
+ loge(e.getMessage());
} finally {
if (urlConnection != null) urlConnection.disconnect();
TrafficStats.setThreadStatsTag(oldTag);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index e279a09c2bd2..5f302c6a5035 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -332,8 +332,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
}
public int getProfileConnectionState(LocalBluetoothProfile profile) {
- if (mProfileConnectionState == null ||
- mProfileConnectionState.get(profile) == null) {
+ if (mProfileConnectionState.get(profile) == null) {
// If cache is empty make the binder call to get the state
int state = profile.getConnectionStatus(mDevice);
mProfileConnectionState.put(profile, state);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
index 0cf890037f55..fe03fba5bfda 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinView.java
@@ -58,9 +58,19 @@ public class KeyguardSimPinView extends KeyguardPinBasedInputView {
KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() {
@Override
public void onSimStateChanged(int subId, int slotId, State simState) {
- if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")");
- resetState();
- };
+ if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")");
+ switch(simState) {
+ // If the SIM is removed, then we must remove the keyguard. It will be put up
+ // again when the PUK locked SIM is re-entered.
+ case ABSENT: {
+ KeyguardUpdateMonitor.getInstance(getContext()).reportSimUnlocked(mSubId);
+ mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
+ break;
+ }
+ default:
+ resetState();
+ }
+ }
};
public KeyguardSimPinView(Context context) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
index fb3cee74ab68..826a03d1b658 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukView.java
@@ -60,9 +60,23 @@ public class KeyguardSimPukView extends KeyguardPinBasedInputView {
KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() {
@Override
public void onSimStateChanged(int subId, int slotId, State simState) {
- if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")");
- resetState();
- };
+ if (DEBUG) Log.v(TAG, "onSimStateChanged(subId=" + subId + ",state=" + simState + ")");
+ switch(simState) {
+ // If the SIM is removed, then we must remove the keyguard. It will be put up
+ // again when the PUK locked SIM is re-entered.
+ case ABSENT:
+ // intentional fall-through
+ // If the SIM is unlocked via a key sequence through the emergency dialer, it will
+ // move into the READY state and the PUK lock keyguard should be removed.
+ case READY: {
+ KeyguardUpdateMonitor.getInstance(getContext()).reportSimUnlocked(mSubId);
+ mCallback.dismiss(true, KeyguardUpdateMonitor.getCurrentUser());
+ break;
+ }
+ default:
+ resetState();
+ }
+ }
};
public KeyguardSimPukView(Context context) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
index 2473335e6e15..cef7c8ac9a8c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
@@ -170,7 +170,8 @@ public class QSFooter extends FrameLayout implements
int defSpace = mContext.getResources().getDimensionPixelOffset(R.dimen.default_gear_space);
mAnimator = new Builder()
- .addFloat(mSettingsContainer, "translationX", -(remaining - defSpace), 0)
+ .addFloat(mSettingsContainer, "translationX",
+ isLayoutRtl() ? (remaining - defSpace) : -(remaining - defSpace), 0)
.addFloat(mSettingsButton, "rotation", -120, 0)
.build();
if (mAlarmShowing) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 0a0d2ce00a1b..bdc5e7d75b48 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -556,7 +556,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
@Override
public int getMovementFlags(RecyclerView recyclerView, ViewHolder viewHolder) {
- if (viewHolder.getItemViewType() == TYPE_EDIT) {
+ if (viewHolder.getItemViewType() == TYPE_EDIT || viewHolder.getItemViewType() == TYPE_DIVIDER) {
return makeMovementFlags(0, 0);
}
int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.RIGHT
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 672f2c2df06e..3495f570cc2c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -439,15 +439,22 @@ public abstract class QSTileImpl<TState extends State> implements QSTile {
public static class DrawableIcon extends Icon {
protected final Drawable mDrawable;
+ protected final Drawable mInvisibleDrawable;
public DrawableIcon(Drawable drawable) {
mDrawable = drawable;
+ mInvisibleDrawable = drawable.getConstantState().newDrawable();
}
@Override
public Drawable getDrawable(Context context) {
return mDrawable;
}
+
+ @Override
+ public Drawable getInvisibleDrawable(Context context) {
+ return mInvisibleDrawable;
+ }
}
public static class DrawableIconWithRes extends DrawableIcon {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index f58fe8290a43..55498af7a54b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -4486,7 +4486,7 @@ public class StatusBar extends SystemUI implements DemoMode,
animateCollapsePanels();
return true;
}
- if (mKeyguardUserSwitcher.hideIfNotSimple(true)) {
+ if (mKeyguardUserSwitcher != null && mKeyguardUserSwitcher.hideIfNotSimple(true)) {
return true;
}
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index efce87177faa..b21a5e86ee6c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -182,6 +182,7 @@ public class MobileSignalController extends SignalController<
mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EVDO_B, TelephonyIcons.THREE_G);
mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EHRPD, TelephonyIcons.THREE_G);
mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_UMTS, TelephonyIcons.THREE_G);
+ mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_TD_SCDMA, TelephonyIcons.THREE_G);
if (!mConfig.showAtLeast3G) {
mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_UNKNOWN,
diff --git a/proto/src/ipconnectivity.proto b/proto/src/ipconnectivity.proto
index 76c54185a774..d997a80cda83 100644
--- a/proto/src/ipconnectivity.proto
+++ b/proto/src/ipconnectivity.proto
@@ -23,7 +23,7 @@ message NetworkId {
// It is not intended to map one to one to the TRANSPORT_* constants defined in
// android.net.NetworkCapabilities. Instead it is intended to be used as
// a dimension field for metrics events and aggregated metrics.
-// Next tag: 7
+// Next tag: 10
enum LinkLayer {
// An unknown link layer technology.
UNKNOWN = 0;
@@ -32,6 +32,9 @@ enum LinkLayer {
CELLULAR = 2;
ETHERNET = 3;
WIFI = 4;
+ WIFI_P2P = 7;
+ WIFI_NAN = 8; // Also known as WiFi Aware
+ LOWPAN = 9;
// Indicates that the link layer dimension is not relevant for the metrics or
// event considered.
@@ -47,16 +50,18 @@ message Pair {
optional int32 value = 2;
};
-// Logs changes in the system default network. Changes can be 1) acquiring a
-// default network with no previous default, 2) a switch of the system default
-// network to a new default network, 3) a loss of the system default network.
-// This message is associated to android.net.metrics.DefaultNetworkEvent.
+// An event record when the system default network disconnects or the system
+// switches to a new default network.
+// Next tag: 10.
message DefaultNetworkEvent {
- // A value of 0 means this is a loss of the system default network.
- optional NetworkId network_id = 1;
- // A value of 0 means there was no previous default network.
- optional NetworkId previous_network_id = 2;
+ // Reason why this network stopped being the default.
+ enum LostReason {
+ UNKNOWN = 0;
+ OUTSCORED = 1;
+ INVALIDATION = 2;
+ DISCONNECT = 3;
+ };
// Whether the network supports IPv4, IPv6, or both.
enum IPSupport {
@@ -66,17 +71,52 @@ message DefaultNetworkEvent {
DUAL = 3;
};
+ // Duration in milliseconds when this network was the default.
+ // Since version 4
+ optional int64 default_network_duration_ms = 5;
+
+ // Duration in milliseconds without a default network before this network
+ // became the default.
+ // Since version 4
+ optional int64 no_default_network_duration_ms = 6;
+
+ // Network score of this network when it became the default network.
+ // Since version 4
+ optional int64 initial_score = 7;
+
+ // Network score of this network when it stopped being the default network.
+ // Since version 4
+ optional int64 final_score = 8;
+
+ // Best available information about IP support of this default network.
+ // Since version 4
+ optional IPSupport ip_support = 9;
+
+
+ // Deprecated fields
+
+ // A value of 0 means this is a loss of the system default network.
+ // Deprecated since version 3. Replaced by top level network_id.
+ optional NetworkId network_id = 1 [deprecated = true];
+
+ // A value of 0 means there was no previous default network.
+ // Deprecated since version 3. Replaced by previous_default_network_id.
+ optional NetworkId previous_network_id = 2 [deprecated = true];
+
// Best available information about IP support of the previous network when
// disconnecting or switching to a new default network.
- optional IPSupport previous_network_ip_support = 3;
+ // Deprecated since version 3. Replaced by ip_support field.
+ optional IPSupport previous_network_ip_support = 3 [deprecated = true];
// The transport types of the new default network, represented by
// TRANSPORT_* constants as defined in NetworkCapabilities.
- repeated int32 transport_types = 4;
+ // Deprecated since version 3. Replaced by top-level transports field.
+ repeated int32 transport_types = 4 [deprecated = true];
};
// Logs IpReachabilityMonitor probe events and NUD_FAILED events.
// This message is associated to android.net.metrics.IpReachabilityEvent.
+// Next tag: 3.
message IpReachabilityEvent {
// The interface name (wlan, rmnet, lo, ...) on which the probe was sent.
// Deprecated since version 2, to be replaced by link_layer field.
@@ -91,6 +131,7 @@ message IpReachabilityEvent {
// Logs NetworkMonitor and ConnectivityService events related to the state of
// a network: connection, evaluation, validation, lingering, and disconnection.
// This message is associated to android.net.metrics.NetworkEvent.
+// Next tag: 4.
message NetworkEvent {
// The id of the network on which this event happened.
// Deprecated since version 3.
@@ -108,6 +149,7 @@ message NetworkEvent {
// Logs individual captive portal probing events that are performed when
// evaluating or reevaluating networks for Internet connectivity.
// This message is associated to android.net.metrics.ValidationProbeEvent.
+// Next tag: 5.
message ValidationProbeEvent {
// The id of the network for which the probe was sent.
// Deprecated since version 3.
@@ -124,26 +166,64 @@ message ValidationProbeEvent {
optional int32 probe_result = 4;
}
-// Logs DNS lookup latencies. Repeated fields must have the same length.
+// Logs DNS lookup latencies.
// This message is associated to android.net.metrics.DnsEvent.
-// Deprecated since version 2.
+// Next tag: 11
message DNSLookupBatch {
+
+ // The time it took for successful DNS lookups to complete.
+ // The number of repeated values can be less than getaddrinfo_query_count
+ // + gethostbyname_query_count in case of event rate-limiting.
+ repeated int32 latencies_ms = 4;
+
+ // The total number of getaddrinfo queries.
+ // Since version 4.
+ optional int64 getaddrinfo_query_count = 5;
+
+ // The total number of gethostbyname queries.
+ // Since version 4.
+ optional int64 gethostbyname_query_count = 6;
+
+ // The total number of getaddrinfo errors.
+ // Since version 4.
+ optional int64 getaddrinfo_error_count = 7;
+
+ // The total number of gethostbyname errors.
+ // Since version 4.
+ optional int64 gethostbyname_error_count = 8;
+
+ // Counts of all errors returned by getaddrinfo.
+ // The Pair key field is the getaddrinfo error value.
+ // The value field is the count for that return value.
+ // Since version 4
+ repeated Pair getaddrinfo_errors = 9;
+
+ // Counts of all errors returned by gethostbyname.
+ // The Pair key field is the gethostbyname errno value.
+ // the Pair value field is the count for that errno code.
+ // Since version 4
+ repeated Pair gethostbyname_errors = 10;
+
+
+ // Deprecated fields
+
// The id of the network on which the DNS lookups took place.
- optional NetworkId network_id = 1;
+ // Deprecated since version 3.
+ optional NetworkId network_id = 1 [deprecated = true];
// The types of the DNS lookups, as defined in android.net.metrics.DnsEvent.
- repeated int32 event_types = 2;
+ // Deprecated since version 3.
+ repeated int32 event_types = 2 [deprecated = true];
// The return values of the DNS resolver for each DNS lookups.
- repeated int32 return_codes = 3;
-
- // The time it took for each DNS lookups to complete.
- repeated int32 latencies_ms = 4;
+ // Deprecated since version 3.
+ repeated int32 return_codes = 3 [deprecated = true];
};
// Represents a collections of DNS lookup latencies and counters for a
// particular combination of DNS query type and return code.
// Since version 2.
+// Next tag: 7.
message DNSLatencies {
// The type of the DNS lookups, as defined in android.net.metrics.DnsEvent.
// Acts as a key for a set of DNS query results.
@@ -203,6 +283,7 @@ message ConnectStatistics {
// state transition or a response packet parsing error.
// This message is associated to android.net.metrics.DhcpClientEvent and
// android.net.metrics.DhcpErrorEvent.
+// Next tag: 5
message DHCPEvent {
// The interface name (wlan, rmnet, lo, ...) on which the event happened.
// Deprecated since version 2, to be replaced by link_layer field.
@@ -255,7 +336,7 @@ message ApfProgramEvent {
// Represents Router Advertisement listening statistics for an interface with
// Android Packet Filter enabled.
// Since version 1.
-// Next tag: 12
+// Next tag: 15
message ApfStatistics {
// The time interval in milliseconds these stastistics cover.
optional int64 duration_ms = 1;
@@ -288,12 +369,28 @@ message ApfStatistics {
// The total number of APF program updates triggered when disabling the
// multicast filter. Since version 3.
+ // Since version 4.
optional int32 program_updates_allowing_multicast = 11;
+
+ // The total number of packets processed by the APF interpreter.
+ // Since version 4.
+ optional int32 total_packet_processed = 12;
+
+ // The total number of packets dropped by the APF interpreter.
+ // Since version 4.
+ optional int32 total_packet_dropped = 13;
+
+ // List of hardware counters collected by the APF interpreter.
+ // The Pair key is the counter id, defined in android.net.metrics.ApfStats.
+ // The Pair value is the counter value.
+ // Since version 4.
+ repeated Pair hardware_counters = 14;
}
// Represents the reception of a Router Advertisement packet for an interface
// with Android Packet Filter enabled.
// Since version 1.
+// Next tag: 7.
message RaEvent {
// All lifetime values are expressed in seconds. The default value for an
// option lifetime that was not present in the RA option list is -1.
@@ -322,6 +419,7 @@ message RaEvent {
// Represents an IP provisioning event in IpManager and how long the
// provisioning action took.
// This message is associated to android.net.metrics.IpManagerEvent.
+// Next tag: 4.
message IpProvisioningEvent {
// The interface name (wlan, rmnet, lo, ...) on which the probe was sent.
// Deprecated since version 2, to be replaced by link_layer field.
@@ -335,8 +433,80 @@ message IpProvisioningEvent {
optional int32 latency_ms = 3;
}
+// Represents statistics from a single android Network.
+// Since version 4. Replace NetworkEvent.
+// Next tag: 9.
+message NetworkStats {
+
+ // Duration of this Network lifecycle in milliseconds.
+ optional int64 duration_ms = 1;
+
+ // Information about IP support of this network.
+ optional DefaultNetworkEvent.IPSupport ip_support = 2;
+
+ // True if the network was validated at least once.
+ optional bool ever_validated = 3;
+
+ // True if a captive portal was found at least once on this network.
+ optional bool portal_found = 4;
+
+ // Total number of times no connectivity was reported for this network.
+ optional int32 no_connectivity_reports = 5;
+
+ // Total number of validation attempts.
+ optional int32 validation_attempts = 6;
+
+ // Results from all validation attempts.
+ // The Pair key is the result:
+ // 0 -> unvalidated
+ // 1 -> validated
+ // 2 -> captive portal
+ // The Pair value is the duration of the validation attempts in milliseconds.
+ repeated Pair validation_events = 7;
+
+ // Time series of validation states in time order.
+ // The Pair key is the state:
+ // 0 -> unvalidated
+ // 1 -> validated
+ // 2 -> captive portal,
+ // The Pair value is the duration of that state in milliseconds.
+ repeated Pair validation_states = 8;
+}
+
+// Represents statistics from NFLOG wakeup events due to ingress packets.
+// Since oc-mr1.
+// Next tag: 8.
+message WakeupStats {
+ // The time duration in seconds covered by these stats, for deriving
+ // exact wakeup rates.
+ optional int64 duration_sec = 1;
+
+ // The total number of ingress packets waking up the device.
+ optional int64 total_wakeups = 2;
+
+ // The total number of wakeup packets routed to a socket belonging to
+ // the root uid (uid 0).
+ optional int64 root_wakeups = 3;
+
+ // The total number of wakeup packets routed to a socket belonging to
+ // the system server (uid 1000).
+ optional int64 system_wakeups = 4;
+
+ // The total number of wakeup packets routed to a socket belonging to
+ // an application (uid > 9999).
+ optional int64 application_wakeups = 5;
+
+ // The total number of wakeup packets routed to a socket belonging to another
+ // uid than the root uid, system uid, or an application uid (any uid in
+ // between [1001, 9999]. See android.os.Process for possible uids.
+ optional int64 non_application_wakeups = 6;
+
+ // The total number of wakeup packets with no associated sockets.
+ optional int64 unrouted_wakeups = 7;
+}
+
// Represents one of the IP connectivity event defined in this file.
-// Next tag: 19
+// Next tag: 20
message IpConnectivityEvent {
// Time in ms when the event was recorded.
optional int64 time_ms = 1;
@@ -370,14 +540,13 @@ message IpConnectivityEvent {
oneof event {
// An event about the system default network.
- // The link_layer field is not relevant for this event and set to NONE.
DefaultNetworkEvent default_network_event = 2;
// An IP reachability probe event.
IpReachabilityEvent ip_reachability_event = 3;
// A network lifecycle event.
- NetworkEvent network_event = 4;
+ NetworkEvent network_event = 4 [deprecated = true];
// A batch of DNS lookups.
// Deprecated in the nyc-mr2 release since version 2,and replaced by
@@ -407,10 +576,17 @@ message IpConnectivityEvent {
// An RA packet reception event.
RaEvent ra_event = 11;
+
+ // Network statistics.
+ NetworkStats network_stats = 19;
+
+ // Ingress packet wakeup statistics.
+ WakeupStats wakeup_stats = 20;
};
};
// The information about IP connectivity events.
+// Next tag: 4.
message IpConnectivityLog {
// An array of IP connectivity events.
repeated IpConnectivityEvent events = 1;
@@ -424,5 +600,6 @@ message IpConnectivityLog {
// nyc-mr1: not populated, implicitly 1.
// nyc-mr2: 2.
// oc: 3.
+ // oc-dr1: 4.
optional int32 version = 3;
};
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
index 7e82edaae3e5..b1ac5891dafa 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationGestureHandler.java
@@ -690,6 +690,7 @@ class MagnificationGestureHandler implements EventStreamTransformation {
}
break;
case MotionEvent.ACTION_UP: {
+ mHandler.removeMessages(MESSAGE_ON_ACTION_TAP_AND_HOLD);
if (!mMagnificationController.magnificationRegionContains(
event.getX(), event.getY())) {
transitionToDelegatingState(!mShortcutTriggered);
@@ -703,7 +704,6 @@ class MagnificationGestureHandler implements EventStreamTransformation {
if (mLastDownEvent == null) {
return;
}
- mHandler.removeMessages(MESSAGE_ON_ACTION_TAP_AND_HOLD);
if (!GestureUtils.isTap(mLastDownEvent, event, mTapTimeSlop,
mTapDistanceSlop, 0)) {
transitionToDelegatingState(true);
diff --git a/services/core/Android.mk b/services/core/Android.mk
index 7fca21f71a6a..f61fd550885b 100644
--- a/services/core/Android.mk
+++ b/services/core/Android.mk
@@ -4,7 +4,10 @@ include $(CLEAR_VARS)
LOCAL_MODULE := services.core
-LOCAL_AIDL_INCLUDES := system/netd/server/binder
+LOCAL_AIDL_INCLUDES := \
+ frameworks/native/aidl/binder \
+ system/netd/server/binder
+
LOCAL_SRC_FILES += \
$(call all-java-files-under,java) \
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index fb20533837af..31fc874db2fb 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -458,6 +458,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
private static final int MAX_WAKELOCK_LOGS = 20;
private final LocalLog mWakelockLogs = new LocalLog(MAX_WAKELOCK_LOGS);
+ private int mTotalWakelockAcquisitions = 0;
+ private int mTotalWakelockReleases = 0;
+ private long mTotalWakelockDurationMs = 0;
+ private long mMaxWakelockDurationMs = 0;
+ private long mLastWakeLockAcquireTimestamp = 0;
// Array of <Network,ReadOnlyLocalLogs> tracking network validation and results
private static final int MAX_VALIDATION_LOGS = 10;
@@ -1959,6 +1964,14 @@ public class ConnectivityService extends IConnectivityManager.Stub
pw.println();
pw.println("NetTransition WakeLock activity (most recent first):");
pw.increaseIndent();
+ pw.println("total acquisitions: " + mTotalWakelockAcquisitions);
+ pw.println("total releases: " + mTotalWakelockReleases);
+ pw.println("cumulative duration: " + (mTotalWakelockDurationMs / 1000) + "s");
+ pw.println("longest duration: " + (mMaxWakelockDurationMs / 1000) + "s");
+ if (mTotalWakelockAcquisitions > mTotalWakelockReleases) {
+ long duration = SystemClock.elapsedRealtime() - mLastWakeLockAcquireTimestamp;
+ pw.println("currently holding WakeLock for: " + (duration / 1000) + "s");
+ }
mWakelockLogs.reverseDump(fd, pw, args);
pw.decreaseIndent();
}
@@ -3012,6 +3025,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
return;
}
mNetTransitionWakeLock.acquire();
+ mLastWakeLockAcquireTimestamp = SystemClock.elapsedRealtime();
+ mTotalWakelockAcquisitions++;
}
mWakelockLogs.log("ACQUIRE for " + forWhom);
Message msg = mHandler.obtainMessage(EVENT_EXPIRE_NET_TRANSITION_WAKELOCK);
@@ -3044,6 +3059,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
return;
}
mNetTransitionWakeLock.release();
+ long lockDuration = SystemClock.elapsedRealtime() - mLastWakeLockAcquireTimestamp;
+ mTotalWakelockDurationMs += lockDuration;
+ mMaxWakelockDurationMs = Math.max(mMaxWakelockDurationMs, lockDuration);
+ mTotalWakelockReleases++;
}
mWakelockLogs.log(String.format("RELEASE (%s)", event));
}
@@ -4316,11 +4335,13 @@ public class ConnectivityService extends IConnectivityManager.Stub
int currentScore, NetworkMisc networkMisc) {
enforceConnectivityInternalPermission();
+ LinkProperties lp = new LinkProperties(linkProperties);
+ lp.ensureDirectlyConnectedRoutes();
// TODO: Instead of passing mDefaultRequest, provide an API to determine whether a Network
// satisfies mDefaultRequest.
final NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),
- new Network(reserveNetId()), new NetworkInfo(networkInfo), new LinkProperties(
- linkProperties), new NetworkCapabilities(networkCapabilities), currentScore,
+ new Network(reserveNetId()), new NetworkInfo(networkInfo), lp,
+ new NetworkCapabilities(networkCapabilities), currentScore,
mContext, mTrackerHandler, new NetworkMisc(networkMisc), mDefaultRequest, this);
synchronized (this) {
nai.networkMonitor.systemReady = mSystemReady;
@@ -4414,12 +4435,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
private void updateInterfaces(LinkProperties newLp, LinkProperties oldLp, int netId,
NetworkCapabilities caps) {
- CompareResult<String> interfaceDiff = new CompareResult<String>();
- if (oldLp != null) {
- interfaceDiff = oldLp.compareAllInterfaceNames(newLp);
- } else if (newLp != null) {
- interfaceDiff.added = newLp.getAllInterfaceNames();
- }
+ CompareResult<String> interfaceDiff = new CompareResult<String>(
+ oldLp != null ? oldLp.getAllInterfaceNames() : null,
+ newLp != null ? newLp.getAllInterfaceNames() : null);
for (String iface : interfaceDiff.added) {
try {
if (DBG) log("Adding iface " + iface + " to network " + netId);
@@ -4445,12 +4463,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
* @return true if routes changed between oldLp and newLp
*/
private boolean updateRoutes(LinkProperties newLp, LinkProperties oldLp, int netId) {
- CompareResult<RouteInfo> routeDiff = new CompareResult<RouteInfo>();
- if (oldLp != null) {
- routeDiff = oldLp.compareAllRoutes(newLp);
- } else if (newLp != null) {
- routeDiff.added = newLp.getAllRoutes();
- }
+ // Compare the route diff to determine which routes should be added and removed.
+ CompareResult<RouteInfo> routeDiff = new CompareResult<RouteInfo>(
+ oldLp != null ? oldLp.getAllRoutes() : null,
+ newLp != null ? newLp.getAllRoutes() : null);
// add routes before removing old in case it helps with continuous connectivity
@@ -4616,6 +4632,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
public void handleUpdateLinkProperties(NetworkAgentInfo nai, LinkProperties newLp) {
+ if (mNetworkForNetId.get(nai.network.netId) != nai) {
+ // Ignore updates for disconnected networks
+ return;
+ }
+ // newLp is already a defensive copy.
+ newLp.ensureDirectlyConnectedRoutes();
if (VDBG) {
log("Update of LinkProperties for " + nai.name() +
"; created=" + nai.created +
diff --git a/services/core/java/com/android/server/NativeDaemonConnector.java b/services/core/java/com/android/server/NativeDaemonConnector.java
index f5f773214124..b5a8332375f4 100644
--- a/services/core/java/com/android/server/NativeDaemonConnector.java
+++ b/services/core/java/com/android/server/NativeDaemonConnector.java
@@ -24,11 +24,13 @@ import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
import android.os.SystemClock;
+import android.os.SystemProperties;
import android.util.LocalLog;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Preconditions;
+import com.android.server.power.ShutdownThread;
import com.google.android.collect.Lists;
import java.io.FileDescriptor;
@@ -136,6 +138,12 @@ final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdo
listenToSocket();
} catch (Exception e) {
loge("Error in NativeDaemonConnector: " + e);
+ String shutdownAct = SystemProperties.get(
+ ShutdownThread.SHUTDOWN_ACTION_PROPERTY, "");
+ if (shutdownAct != null && shutdownAct.length() > 0) {
+ // The device is in middle of shutdown.
+ break;
+ }
SystemClock.sleep(5000);
}
}
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 3e4453204b14..caa2d5112bf0 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -76,6 +76,7 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.INetworkActivityListener;
import android.os.INetworkManagementService;
+import android.os.PersistableBundle;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteCallbackList;
@@ -1897,38 +1898,34 @@ public class NetworkManagementService extends INetworkManagementService.Stub
return new NetworkStats(SystemClock.elapsedRealtime(), 0);
}
- final NativeDaemonEvent[] events;
+ final PersistableBundle bundle;
try {
- events = mConnector.executeForList("bandwidth", "gettetherstats");
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
+ bundle = mNetdService.tetherGetStats();
+ } catch (RemoteException | ServiceSpecificException e) {
+ throw new IllegalStateException("problem parsing tethering stats: ", e);
}
- final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1);
- for (NativeDaemonEvent event : events) {
- if (event.getCode() != TetheringStatsListResult) continue;
- // 114 ifaceIn ifaceOut rx_bytes rx_packets tx_bytes tx_packets
- final StringTokenizer tok = new StringTokenizer(event.getMessage());
- try {
- final String ifaceIn = tok.nextToken();
- final String ifaceOut = tok.nextToken();
+ final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(),
+ bundle.size());
+ final NetworkStats.Entry entry = new NetworkStats.Entry();
- final NetworkStats.Entry entry = new NetworkStats.Entry();
- entry.iface = ifaceOut;
+ for (String iface : bundle.keySet()) {
+ long[] statsArray = bundle.getLongArray(iface);
+ try {
+ entry.iface = iface;
entry.uid = UID_TETHERING;
entry.set = SET_DEFAULT;
entry.tag = TAG_NONE;
- entry.rxBytes = Long.parseLong(tok.nextToken());
- entry.rxPackets = Long.parseLong(tok.nextToken());
- entry.txBytes = Long.parseLong(tok.nextToken());
- entry.txPackets = Long.parseLong(tok.nextToken());
+ entry.rxBytes = statsArray[INetd.TETHER_STATS_RX_BYTES];
+ entry.rxPackets = statsArray[INetd.TETHER_STATS_RX_PACKETS];
+ entry.txBytes = statsArray[INetd.TETHER_STATS_TX_BYTES];
+ entry.txPackets = statsArray[INetd.TETHER_STATS_TX_PACKETS];
stats.combineValues(entry);
- } catch (NoSuchElementException e) {
- throw new IllegalStateException("problem parsing tethering stats: " + event);
- } catch (NumberFormatException e) {
- throw new IllegalStateException("problem parsing tethering stats: " + event);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new IllegalStateException("invalid tethering stats for " + iface, e);
}
}
+
return stats;
}
diff --git a/services/core/java/com/android/server/RecoverySystemService.java b/services/core/java/com/android/server/RecoverySystemService.java
index 3c8c699a65bb..1517887efec2 100644
--- a/services/core/java/com/android/server/RecoverySystemService.java
+++ b/services/core/java/com/android/server/RecoverySystemService.java
@@ -285,8 +285,9 @@ public final class RecoverySystemService extends SystemService {
// Send the BCB commands if it's to setup BCB.
if (isSetup) {
- dos.writeInt(command.length());
- dos.writeBytes(command);
+ byte[] cmdUtf8 = command.getBytes("UTF-8");
+ dos.writeInt(cmdUtf8.length);
+ dos.write(cmdUtf8, 0, cmdUtf8.length);
dos.flush();
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5f679db3287d..c699a56282ca 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3836,38 +3836,38 @@ public class ActivityManagerService extends IActivityManager.Stub
uid = 0;
}
}
- int debugFlags = 0;
+ int runtimeFlags = 0;
if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
- debugFlags |= Zygote.DEBUG_ENABLE_JDWP;
- debugFlags |= Zygote.DEBUG_JAVA_DEBUGGABLE;
+ runtimeFlags |= Zygote.DEBUG_ENABLE_JDWP;
+ runtimeFlags |= Zygote.DEBUG_JAVA_DEBUGGABLE;
// Also turn on CheckJNI for debuggable apps. It's quite
// awkward to turn on otherwise.
- debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
+ runtimeFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
}
// Run the app in safe mode if its manifest requests so or the
// system is booted in safe mode.
if ((app.info.flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0 ||
mSafeMode == true) {
- debugFlags |= Zygote.DEBUG_ENABLE_SAFEMODE;
+ runtimeFlags |= Zygote.DEBUG_ENABLE_SAFEMODE;
}
if ("1".equals(SystemProperties.get("debug.checkjni"))) {
- debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
+ runtimeFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
}
String genDebugInfoProperty = SystemProperties.get("debug.generate-debug-info");
if ("true".equals(genDebugInfoProperty)) {
- debugFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO;
+ runtimeFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO;
}
if ("1".equals(SystemProperties.get("debug.jni.logging"))) {
- debugFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING;
+ runtimeFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING;
}
if ("1".equals(SystemProperties.get("debug.assert"))) {
- debugFlags |= Zygote.DEBUG_ENABLE_ASSERT;
+ runtimeFlags |= Zygote.DEBUG_ENABLE_ASSERT;
}
if (mNativeDebuggingApp != null && mNativeDebuggingApp.equals(app.processName)) {
// Enable all debug flags required by the native debugger.
- debugFlags |= Zygote.DEBUG_ALWAYS_JIT; // Don't interpret anything
- debugFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO; // Generate debug info
- debugFlags |= Zygote.DEBUG_NATIVE_DEBUGGABLE; // Disbale optimizations
+ runtimeFlags |= Zygote.DEBUG_ALWAYS_JIT; // Don't interpret anything
+ runtimeFlags |= Zygote.DEBUG_GENERATE_DEBUG_INFO; // Generate debug info
+ runtimeFlags |= Zygote.DEBUG_NATIVE_DEBUGGABLE; // Disbale optimizations
mNativeDebuggingApp = null;
}
@@ -3917,12 +3917,12 @@ public class ActivityManagerService extends IActivityManager.Stub
ProcessStartResult startResult;
if (hostingType.equals("webview_service")) {
startResult = startWebView(entryPoint,
- app.processName, uid, uid, gids, debugFlags, mountExternal,
+ app.processName, uid, uid, gids, runtimeFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, null, entryPointArgs);
} else {
startResult = Process.start(entryPoint,
- app.processName, uid, uid, gids, debugFlags, mountExternal,
+ app.processName, uid, uid, gids, runtimeFlags, mountExternal,
app.info.targetSdkVersion, seInfo, requiredAbi, instructionSet,
app.info.dataDir, invokeWith, entryPointArgs);
}
diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java b/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
index 5dee91de0e77..22330e66e126 100644
--- a/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
+++ b/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
@@ -20,6 +20,7 @@ import static android.net.NetworkCapabilities.MAX_TRANSPORT;
import static android.net.NetworkCapabilities.TRANSPORT_BLUETOOTH;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
+import static android.net.NetworkCapabilities.TRANSPORT_LOWPAN;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
@@ -37,6 +38,7 @@ import android.net.metrics.IpReachabilityEvent;
import android.net.metrics.NetworkEvent;
import android.net.metrics.RaEvent;
import android.net.metrics.ValidationProbeEvent;
+import android.net.metrics.WakeupStats;
import android.os.Parcelable;
import android.util.SparseArray;
import android.util.SparseIntArray;
@@ -114,6 +116,22 @@ final public class IpConnectivityEventBuilder {
return out;
}
+ public static IpConnectivityEvent toProto(WakeupStats in) {
+ IpConnectivityLogClass.WakeupStats wakeupStats =
+ new IpConnectivityLogClass.WakeupStats();
+ in.updateDuration();
+ wakeupStats.durationSec = in.durationSec;
+ wakeupStats.totalWakeups = in.totalWakeups;
+ wakeupStats.rootWakeups = in.rootWakeups;
+ wakeupStats.systemWakeups = in.systemWakeups;
+ wakeupStats.nonApplicationWakeups = in.nonApplicationWakeups;
+ wakeupStats.applicationWakeups = in.applicationWakeups;
+ wakeupStats.unroutedWakeups = in.unroutedWakeups;
+ final IpConnectivityEvent out = buildEvent(0, 0, in.iface);
+ out.setWakeupStats(wakeupStats);
+ return out;
+ }
+
private static IpConnectivityEvent buildEvent(int netId, long transports, String ifname) {
final IpConnectivityEvent ev = new IpConnectivityEvent();
ev.networkId = netId;
@@ -362,29 +380,46 @@ final public class IpConnectivityEventBuilder {
TRANSPORT_LINKLAYER_MAP[TRANSPORT_BLUETOOTH] = IpConnectivityLogClass.BLUETOOTH;
TRANSPORT_LINKLAYER_MAP[TRANSPORT_ETHERNET] = IpConnectivityLogClass.ETHERNET;
TRANSPORT_LINKLAYER_MAP[TRANSPORT_VPN] = IpConnectivityLogClass.UNKNOWN;
- // TODO: change mapping TRANSPORT_WIFI_AWARE -> WIFI_AWARE
- TRANSPORT_LINKLAYER_MAP[TRANSPORT_WIFI_AWARE] = IpConnectivityLogClass.UNKNOWN;
+ TRANSPORT_LINKLAYER_MAP[TRANSPORT_WIFI_AWARE] = IpConnectivityLogClass.WIFI_NAN;
+ TRANSPORT_LINKLAYER_MAP[TRANSPORT_LOWPAN] = IpConnectivityLogClass.LOWPAN;
};
private static int ifnameToLinkLayer(String ifname) {
// Do not try to catch all interface names with regexes, instead only catch patterns that
// are cheap to check, and otherwise fallback on postprocessing in aggregation layer.
- for (int i = 0; i < IFNAME_LINKLAYER_MAP.size(); i++) {
- String pattern = IFNAME_LINKLAYER_MAP.valueAt(i);
+ for (int i = 0; i < KNOWN_PREFIX; i++) {
+ String pattern = IFNAME_PREFIXES[i];
if (ifname.startsWith(pattern)) {
- return IFNAME_LINKLAYER_MAP.keyAt(i);
+ return IFNAME_LINKLAYERS[i];
}
}
return IpConnectivityLogClass.UNKNOWN;
}
- private static final SparseArray<String> IFNAME_LINKLAYER_MAP = new SparseArray<String>();
+ private static final int KNOWN_PREFIX = 7;
+ private static final String[] IFNAME_PREFIXES = new String[KNOWN_PREFIX];
+ private static final int[] IFNAME_LINKLAYERS = new int[KNOWN_PREFIX];
static {
- IFNAME_LINKLAYER_MAP.put(IpConnectivityLogClass.CELLULAR, "rmnet");
- IFNAME_LINKLAYER_MAP.put(IpConnectivityLogClass.WIFI, "wlan");
- IFNAME_LINKLAYER_MAP.put(IpConnectivityLogClass.BLUETOOTH, "bt-pan");
- // TODO: rekey to USB
- IFNAME_LINKLAYER_MAP.put(IpConnectivityLogClass.ETHERNET, "usb");
- // TODO: add mappings for nan -> WIFI_AWARE and p2p -> WIFI_P2P
+ // Ordered from most likely link layer to least likely.
+ IFNAME_PREFIXES[0] = "rmnet";
+ IFNAME_LINKLAYERS[0] = IpConnectivityLogClass.CELLULAR;
+
+ IFNAME_PREFIXES[1] = "wlan";
+ IFNAME_LINKLAYERS[1] = IpConnectivityLogClass.WIFI;
+
+ IFNAME_PREFIXES[2] = "bt-pan";
+ IFNAME_LINKLAYERS[2] = IpConnectivityLogClass.BLUETOOTH;
+
+ IFNAME_PREFIXES[3] = "p2p";
+ IFNAME_LINKLAYERS[3] = IpConnectivityLogClass.WIFI_P2P;
+
+ IFNAME_PREFIXES[4] = "aware";
+ IFNAME_LINKLAYERS[4] = IpConnectivityLogClass.WIFI_NAN;
+
+ IFNAME_PREFIXES[5] = "eth";
+ IFNAME_LINKLAYERS[5] = IpConnectivityLogClass.ETHERNET;
+
+ IFNAME_PREFIXES[6] = "wpan";
+ IFNAME_LINKLAYERS[6] = IpConnectivityLogClass.LOWPAN;
}
}
diff --git a/services/core/java/com/android/server/connectivity/Nat464Xlat.java b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
index 10c8b8b1e0aa..e6585ad194ec 100644
--- a/services/core/java/com/android/server/connectivity/Nat464Xlat.java
+++ b/services/core/java/com/android/server/connectivity/Nat464Xlat.java
@@ -33,7 +33,7 @@ import java.util.Objects;
/**
* Class to manage a 464xlat CLAT daemon. Nat464Xlat is not thread safe and should be manipulated
- * from a consistent and unique thread context. It is the responsability of ConnectivityService to
+ * from a consistent and unique thread context. It is the responsibility of ConnectivityService to
* call into this class from its own Handler thread.
*
* @hide
@@ -60,7 +60,9 @@ public class Nat464Xlat extends BaseNetworkObserver {
private enum State {
IDLE, // start() not called. Base iface and stacked iface names are null.
STARTING, // start() called. Base iface and stacked iface names are known.
- RUNNING; // start() called, and the stacked iface is known to be up.
+ RUNNING, // start() called, and the stacked iface is known to be up.
+ STOPPING; // stop() called, this Nat464Xlat is still registered as a network observer for
+ // the stacked interface.
}
private String mBaseIface;
@@ -81,6 +83,8 @@ public class Nat464Xlat extends BaseNetworkObserver {
// TODO: migrate to NetworkCapabilities.TRANSPORT_*.
final int netType = nai.networkInfo.getType();
final boolean supported = ArrayUtils.contains(NETWORK_TYPES, nai.networkInfo.getType());
+ // TODO: this should also consider if the network is in SUSPENDED state to avoid stopping
+ // clatd in SUSPENDED state.
final boolean connected = nai.networkInfo.isConnected();
// We only run clat on networks that don't have a native IPv4 address.
final boolean hasIPv4Address =
@@ -111,18 +115,70 @@ public class Nat464Xlat extends BaseNetworkObserver {
}
/**
- * Sets internal state.
+ * @return true if clatd has been stopped.
+ */
+ public boolean isStopping() {
+ return mState == State.STOPPING;
+ }
+
+ /**
+ * Start clatd, register this Nat464Xlat as a network observer for the stacked interface,
+ * and set internal state.
*/
private void enterStartingState(String baseIface) {
+ try {
+ mNMService.registerObserver(this);
+ } catch(RemoteException e) {
+ Slog.e(TAG,
+ "startClat: Can't register interface observer for clat on " + mNetwork.name());
+ return;
+ }
+ try {
+ mNMService.startClatd(baseIface);
+ } catch(RemoteException|IllegalStateException e) {
+ Slog.e(TAG, "Error starting clatd on " + baseIface, e);
+ }
mIface = CLAT_PREFIX + baseIface;
mBaseIface = baseIface;
mState = State.STARTING;
}
/**
- * Clears internal state.
+ * Enter running state just after getting confirmation that the stacked interface is up, and
+ * turn ND offload off if on WiFi.
+ */
+ private void enterRunningState() {
+ maybeSetIpv6NdOffload(mBaseIface, false);
+ mState = State.RUNNING;
+ }
+
+ /**
+ * Stop clatd, and turn ND offload on if it had been turned off.
+ */
+ private void enterStoppingState() {
+ if (isRunning()) {
+ maybeSetIpv6NdOffload(mBaseIface, true);
+ }
+
+ try {
+ mNMService.stopClatd(mBaseIface);
+ } catch(RemoteException|IllegalStateException e) {
+ Slog.e(TAG, "Error stopping clatd on " + mBaseIface, e);
+ }
+
+ mState = State.STOPPING;
+ }
+
+ /**
+ * Unregister as a base observer for the stacked interface, and clear internal state.
*/
private void enterIdleState() {
+ try {
+ mNMService.unregisterObserver(this);
+ } catch(RemoteException|IllegalStateException e) {
+ Slog.e(TAG, "Error unregistering clatd observer on " + mBaseIface, e);
+ }
+
mIface = null;
mBaseIface = null;
mState = State.IDLE;
@@ -142,27 +198,14 @@ public class Nat464Xlat extends BaseNetworkObserver {
return;
}
- try {
- mNMService.registerObserver(this);
- } catch(RemoteException e) {
- Slog.e(TAG, "startClat: Can't register interface observer for clat on " + mNetwork);
- return;
- }
-
String baseIface = mNetwork.linkProperties.getInterfaceName();
if (baseIface == null) {
Slog.e(TAG, "startClat: Can't start clat on null interface");
return;
}
// TODO: should we only do this if mNMService.startClatd() succeeds?
+ Slog.i(TAG, "Starting clatd on " + baseIface);
enterStartingState(baseIface);
-
- Slog.i(TAG, "Starting clatd on " + mBaseIface);
- try {
- mNMService.startClatd(mBaseIface);
- } catch(RemoteException|IllegalStateException e) {
- Slog.e(TAG, "Error starting clatd on " + mBaseIface, e);
- }
}
/**
@@ -170,18 +213,15 @@ public class Nat464Xlat extends BaseNetworkObserver {
*/
public void stop() {
if (!isStarted()) {
- Slog.e(TAG, "stopClat: already stopped or not started");
return;
}
-
Slog.i(TAG, "Stopping clatd on " + mBaseIface);
- try {
- mNMService.stopClatd(mBaseIface);
- } catch(RemoteException|IllegalStateException e) {
- Slog.e(TAG, "Error stopping clatd on " + mBaseIface, e);
+
+ boolean wasStarting = isStarting();
+ enterStoppingState();
+ if (wasStarting) {
+ enterIdleState();
}
- // When clatd stops and its interface is deleted, handleInterfaceRemoved() will trigger
- // ConnectivityService#handleUpdateLinkProperties and call enterIdleState().
}
/**
@@ -255,55 +295,52 @@ public class Nat464Xlat extends BaseNetworkObserver {
if (!isStarting() || !up || !Objects.equals(mIface, iface)) {
return;
}
+
LinkAddress clatAddress = getLinkAddress(iface);
if (clatAddress == null) {
- Slog.e(TAG, "cladAddress was null for stacked iface " + iface);
+ Slog.e(TAG, "clatAddress was null for stacked iface " + iface);
return;
}
- mState = State.RUNNING;
+
Slog.i(TAG, String.format("interface %s is up, adding stacked link %s on top of %s",
mIface, mIface, mBaseIface));
-
- maybeSetIpv6NdOffload(mBaseIface, false);
+ enterRunningState();
LinkProperties lp = new LinkProperties(mNetwork.linkProperties);
lp.addStackedLink(makeLinkProperties(clatAddress));
- mNetwork.connService.handleUpdateLinkProperties(mNetwork, lp);
+ mNetwork.connService().handleUpdateLinkProperties(mNetwork, lp);
}
/**
* Removes stacked link on base link and transitions to IDLE state.
*/
private void handleInterfaceRemoved(String iface) {
- if (!isRunning() || !Objects.equals(mIface, iface)) {
+ if (!Objects.equals(mIface, iface)) {
+ return;
+ }
+ if (!isRunning() && !isStopping()) {
return;
}
Slog.i(TAG, "interface " + iface + " removed");
- // The interface going away likely means clatd has crashed. Ask netd to stop it,
- // because otherwise when we try to start it again on the same base interface netd
- // will complain that it's already started.
- try {
- mNMService.unregisterObserver(this);
- // TODO: add STOPPING state to avoid calling stopClatd twice.
- mNMService.stopClatd(mBaseIface);
- } catch(RemoteException|IllegalStateException e) {
- Slog.e(TAG, "Error stopping clatd on " + mBaseIface, e);
+ if (!isStopping()) {
+ // Ensure clatd is stopped if stop() has not been called: this likely means that clatd
+ // has crashed.
+ enterStoppingState();
}
- maybeSetIpv6NdOffload(mBaseIface, true);
- LinkProperties lp = new LinkProperties(mNetwork.linkProperties);
- lp.removeStackedLink(mIface);
enterIdleState();
- mNetwork.connService.handleUpdateLinkProperties(mNetwork, lp);
+ LinkProperties lp = new LinkProperties(mNetwork.linkProperties);
+ lp.removeStackedLink(iface);
+ mNetwork.connService().handleUpdateLinkProperties(mNetwork, lp);
}
@Override
public void interfaceLinkStateChanged(String iface, boolean up) {
- mNetwork.handler.post(() -> { handleInterfaceLinkStateChanged(iface, up); });
+ mNetwork.handler().post(() -> { handleInterfaceLinkStateChanged(iface, up); });
}
@Override
public void interfaceRemoved(String iface) {
- mNetwork.handler.post(() -> { handleInterfaceRemoved(iface); });
+ mNetwork.handler().post(() -> { handleInterfaceRemoved(iface); });
}
@Override
diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
index 4094083f138c..6f7ace2f6527 100644
--- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
+++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
@@ -16,6 +16,8 @@
package com.android.server.connectivity;
+import static android.util.TimeUtils.NANOS_PER_MS;
+
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.INetdEventCallback;
@@ -25,9 +27,12 @@ import android.net.metrics.ConnectStats;
import android.net.metrics.DnsEvent;
import android.net.metrics.INetdEventListener;
import android.net.metrics.IpConnectivityLog;
+import android.net.metrics.WakeupEvent;
+import android.net.metrics.WakeupStats;
import android.os.RemoteException;
import android.text.format.DateUtils;
import android.util.Log;
+import android.util.ArrayMap;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -59,12 +64,28 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
private static final int CONNECT_LATENCY_FILL_RATE = 15 * (int) DateUtils.SECOND_IN_MILLIS;
private static final int CONNECT_LATENCY_MAXIMUM_RECORDS = 20000;
+ @VisibleForTesting
+ static final int WAKEUP_EVENT_BUFFER_LENGTH = 1024;
+ // TODO: dedup this String constant with the one used in
+ // ConnectivityService#wakeupModifyInterface().
+ @VisibleForTesting
+ static final String WAKEUP_EVENT_IFACE_PREFIX = "iface:";
+
// Sparse arrays of DNS and connect events, grouped by net id.
@GuardedBy("this")
private final SparseArray<DnsEvent> mDnsEvents = new SparseArray<>();
@GuardedBy("this")
private final SparseArray<ConnectStats> mConnectEvents = new SparseArray<>();
+ // Array of aggregated wakeup event stats, grouped by interface name.
+ @GuardedBy("this")
+ private final ArrayMap<String, WakeupStats> mWakeupStats = new ArrayMap<>();
+ // Ring buffer array for storing packet wake up events sent by Netd.
+ @GuardedBy("this")
+ private final WakeupEvent[] mWakeupEvents = new WakeupEvent[WAKEUP_EVENT_BUFFER_LENGTH];
+ @GuardedBy("this")
+ private long mWakeupEventCursor = 0;
+
private final ConnectivityManager mCm;
@GuardedBy("this")
@@ -137,11 +158,62 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
@Override
public synchronized void onWakeupEvent(String prefix, int uid, int gid, long timestampNs) {
+ maybeVerboseLog("onWakeupEvent(%s, %d, %d, %sns)", prefix, uid, gid, timestampNs);
+
+ // TODO: add ip protocol and port
+
+ String iface = prefix.replaceFirst(WAKEUP_EVENT_IFACE_PREFIX, "");
+ final long timestampMs;
+ if (timestampNs > 0) {
+ timestampMs = timestampNs / NANOS_PER_MS;
+ } else {
+ timestampMs = System.currentTimeMillis();
+ }
+
+ addWakupEvent(iface, timestampMs, uid);
+ }
+
+ @GuardedBy("this")
+ private void addWakupEvent(String iface, long timestampMs, int uid) {
+ int index = wakeupEventIndex(mWakeupEventCursor);
+ mWakeupEventCursor++;
+ WakeupEvent event = new WakeupEvent();
+ event.iface = iface;
+ event.timestampMs = timestampMs;
+ event.uid = uid;
+ mWakeupEvents[index] = event;
+ WakeupStats stats = mWakeupStats.get(iface);
+ if (stats == null) {
+ stats = new WakeupStats(iface);
+ mWakeupStats.put(iface, stats);
+ }
+ stats.countEvent(event);
+ }
+
+ @GuardedBy("this")
+ private WakeupEvent[] getWakeupEvents() {
+ int length = (int) Math.min(mWakeupEventCursor, (long) mWakeupEvents.length);
+ WakeupEvent[] out = new WakeupEvent[length];
+ // Reverse iteration from youngest event to oldest event.
+ long inCursor = mWakeupEventCursor - 1;
+ int outIdx = out.length - 1;
+ while (outIdx >= 0) {
+ out[outIdx--] = mWakeupEvents[wakeupEventIndex(inCursor--)];
+ }
+ return out;
+ }
+
+ private static int wakeupEventIndex(long cursor) {
+ return (int) Math.abs(cursor % WAKEUP_EVENT_BUFFER_LENGTH);
}
public synchronized void flushStatistics(List<IpConnectivityEvent> events) {
flushProtos(events, mConnectEvents, IpConnectivityEventBuilder::toProto);
flushProtos(events, mDnsEvents, IpConnectivityEventBuilder::toProto);
+ for (int i = 0; i < mWakeupStats.size(); i++) {
+ events.add(IpConnectivityEventBuilder.toProto(mWakeupStats.valueAt(i)));
+ }
+ mWakeupStats.clear();
}
public synchronized void dump(PrintWriter writer) {
@@ -153,13 +225,22 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
}
public synchronized void list(PrintWriter pw) {
- listEvents(pw, mConnectEvents, (x) -> x);
- listEvents(pw, mDnsEvents, (x) -> x);
+ listEvents(pw, mConnectEvents, (x) -> x, "\n");
+ listEvents(pw, mDnsEvents, (x) -> x, "\n");
+ for (int i = 0; i < mWakeupStats.size(); i++) {
+ pw.println(mWakeupStats.valueAt(i));
+ }
+ for (WakeupEvent wakeup : getWakeupEvents()) {
+ pw.println(wakeup);
+ }
}
public synchronized void listAsProtos(PrintWriter pw) {
- listEvents(pw, mConnectEvents, IpConnectivityEventBuilder::toProto);
- listEvents(pw, mDnsEvents, IpConnectivityEventBuilder::toProto);
+ listEvents(pw, mConnectEvents, IpConnectivityEventBuilder::toProto, "");
+ listEvents(pw, mDnsEvents, IpConnectivityEventBuilder::toProto, "");
+ for (int i = 0; i < mWakeupStats.size(); i++) {
+ pw.print(IpConnectivityEventBuilder.toProto(mWakeupStats.valueAt(i)));
+ }
}
private static <T> void flushProtos(List<IpConnectivityEvent> out, SparseArray<T> in,
@@ -170,10 +251,13 @@ public class NetdEventListenerService extends INetdEventListener.Stub {
in.clear();
}
- public static <T> void listEvents(
- PrintWriter pw, SparseArray<T> events, Function<T, Object> mapper) {
+ private static <T> void listEvents(
+ PrintWriter pw, SparseArray<T> events, Function<T, Object> mapper, String separator) {
+ // Proto derived Classes have toString method that adds a \n at the end.
+ // Let the caller control that by passing in the line separator explicitly.
for (int i = 0; i < events.size(); i++) {
- pw.println(mapper.apply(events.valueAt(i)).toString());
+ pw.print(mapper.apply(events.valueAt(i)));
+ pw.print(separator);
}
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 7c4ef0f0f3b9..a4d7242086bf 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -249,9 +249,9 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
private static final String TAG = ConnectivityService.class.getSimpleName();
private static final boolean VDBG = false;
- public final ConnectivityService connService;
+ private final ConnectivityService mConnService;
private final Context mContext;
- final Handler handler;
+ private final Handler mHandler;
public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, Network net, NetworkInfo info,
LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler,
@@ -263,13 +263,25 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
linkProperties = lp;
networkCapabilities = nc;
currentScore = score;
- this.connService = connService;
+ mConnService = connService;
mContext = context;
- this.handler = handler;
- networkMonitor = connService.createNetworkMonitor(context, handler, this, defaultRequest);
+ mHandler = handler;
+ networkMonitor = mConnService.createNetworkMonitor(context, handler, this, defaultRequest);
networkMisc = misc;
}
+ public ConnectivityService connService() {
+ return mConnService;
+ }
+
+ public Handler handler() {
+ return mHandler;
+ }
+
+ public Network network() {
+ return network;
+ }
+
// Functions for manipulating the requests satisfied by this network.
//
// These functions must only called on ConnectivityService's main thread.
@@ -432,7 +444,7 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
private boolean ignoreWifiUnvalidationPenalty() {
boolean isWifi = networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) &&
networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
- boolean avoidBadWifi = connService.avoidBadWifi() || avoidUnvalidated;
+ boolean avoidBadWifi = mConnService.avoidBadWifi() || avoidUnvalidated;
return isWifi && !avoidBadWifi && everValidated;
}
@@ -516,8 +528,8 @@ public class NetworkAgentInfo implements Comparable<NetworkAgentInfo> {
}
if (newExpiry > 0) {
- mLingerMessage = connService.makeWakeupMessage(
- mContext, handler,
+ mLingerMessage = mConnService.makeWakeupMessage(
+ mContext, mHandler,
"NETWORK_LINGER_COMPLETE." + network.netId,
EVENT_NETWORK_LINGER_COMPLETE, this);
mLingerMessage.schedule(newExpiry);
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index d3a93542c740..8b886d6b3cff 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -20,7 +20,6 @@ import static android.net.CaptivePortal.APP_RETURN_DISMISSED;
import static android.net.CaptivePortal.APP_RETURN_UNWANTED;
import static android.net.CaptivePortal.APP_RETURN_WANTED_AS_IS;
-import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -229,6 +228,8 @@ public class NetworkMonitor extends StateMachine {
// Delay between reevaluations once a captive portal has been found.
private static final int CAPTIVE_PORTAL_REEVALUATE_DELAY_MS = 10*60*1000;
+ private static final int NUM_VALIDATION_LOG_LINES = 20;
+
private final Context mContext;
private final Handler mConnectivityServiceHandler;
private final NetworkAgentInfo mNetworkAgentInfo;
@@ -236,9 +237,15 @@ public class NetworkMonitor extends StateMachine {
private final int mNetId;
private final TelephonyManager mTelephonyManager;
private final WifiManager mWifiManager;
- private final AlarmManager mAlarmManager;
private final NetworkRequest mDefaultRequest;
private final IpConnectivityLog mMetricsLog;
+ private final NetworkMonitorSettings mSettings;
+
+ // Configuration values for captive portal detection probes.
+ private final String mCaptivePortalUserAgent;
+ private final URL mCaptivePortalHttpsUrl;
+ private final URL mCaptivePortalHttpUrl;
+ private final URL[] mCaptivePortalFallbackUrls;
@VisibleForTesting
protected boolean mIsCaptivePortalCheckEnabled;
@@ -262,40 +269,37 @@ public class NetworkMonitor extends StateMachine {
private CustomIntentReceiver mLaunchCaptivePortalAppBroadcastReceiver = null;
- private final LocalLog validationLogs = new LocalLog(20); // 20 lines
+ private final LocalLog validationLogs = new LocalLog(NUM_VALIDATION_LOG_LINES);
private final Stopwatch mEvaluationTimer = new Stopwatch();
// This variable is set before transitioning to the mCaptivePortalState.
private CaptivePortalProbeResult mLastPortalProbeResult = CaptivePortalProbeResult.FAILED;
- // Configuration values for captive portal detection probes.
- private final String mCaptivePortalUserAgent;
- private final URL mCaptivePortalHttpsUrl;
- private final URL mCaptivePortalHttpUrl;
- private final URL[] mCaptivePortalFallbackUrls;
private int mNextFallbackUrlIndex = 0;
public NetworkMonitor(Context context, Handler handler, NetworkAgentInfo networkAgentInfo,
NetworkRequest defaultRequest) {
- this(context, handler, networkAgentInfo, defaultRequest, new IpConnectivityLog());
+ this(context, handler, networkAgentInfo, defaultRequest, new IpConnectivityLog(),
+ NetworkMonitorSettings.DEFAULT);
}
@VisibleForTesting
protected NetworkMonitor(Context context, Handler handler, NetworkAgentInfo networkAgentInfo,
- NetworkRequest defaultRequest, IpConnectivityLog logger) {
+ NetworkRequest defaultRequest, IpConnectivityLog logger,
+ NetworkMonitorSettings settings) {
// Add suffix indicating which NetworkMonitor we're talking about.
super(TAG + networkAgentInfo.name());
mContext = context;
mMetricsLog = logger;
mConnectivityServiceHandler = handler;
+ mSettings = settings;
mNetworkAgentInfo = networkAgentInfo;
- mNetwork = new OneAddressPerFamilyNetwork(networkAgentInfo.network);
+ mNetwork = new OneAddressPerFamilyNetwork(networkAgentInfo.network());
mNetId = mNetwork.netId;
mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
- mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
mDefaultRequest = defaultRequest;
addState(mDefaultState);
@@ -305,16 +309,12 @@ public class NetworkMonitor extends StateMachine {
addState(mCaptivePortalState, mMaybeNotifyState);
setInitialState(mDefaultState);
- mIsCaptivePortalCheckEnabled = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.CAPTIVE_PORTAL_MODE, Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT)
- != Settings.Global.CAPTIVE_PORTAL_MODE_IGNORE;
- mUseHttps = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.CAPTIVE_PORTAL_USE_HTTPS, 1) == 1;
-
- mCaptivePortalUserAgent = getCaptivePortalUserAgent(context);
- mCaptivePortalHttpsUrl = makeURL(getCaptivePortalServerHttpsUrl(context));
- mCaptivePortalHttpUrl = makeURL(getCaptivePortalServerHttpUrl(context));
- mCaptivePortalFallbackUrls = makeCaptivePortalFallbackUrls(context);
+ mIsCaptivePortalCheckEnabled = getIsCaptivePortalCheckEnabled();
+ mUseHttps = getUseHttpsValidation();
+ mCaptivePortalUserAgent = getCaptivePortalUserAgent();
+ mCaptivePortalHttpsUrl = makeURL(getCaptivePortalServerHttpsUrl());
+ mCaptivePortalHttpUrl = makeURL(getCaptivePortalServerHttpUrl(settings, context));
+ mCaptivePortalFallbackUrls = makeCaptivePortalFallbackUrls();
start();
}
@@ -705,19 +705,42 @@ public class NetworkMonitor extends StateMachine {
}
}
- private static String getCaptivePortalServerHttpsUrl(Context context) {
- return getSetting(context, Settings.Global.CAPTIVE_PORTAL_HTTPS_URL, DEFAULT_HTTPS_URL);
+ public boolean getIsCaptivePortalCheckEnabled() {
+ String symbol = Settings.Global.CAPTIVE_PORTAL_MODE;
+ int defaultValue = Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT;
+ int mode = mSettings.getSetting(mContext, symbol, defaultValue);
+ return mode != Settings.Global.CAPTIVE_PORTAL_MODE_IGNORE;
}
+ public boolean getUseHttpsValidation() {
+ return mSettings.getSetting(mContext, Settings.Global.CAPTIVE_PORTAL_USE_HTTPS, 1) == 1;
+ }
+
+ public boolean getWifiScansAlwaysAvailableDisabled() {
+ return mSettings.getSetting(mContext, Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 0;
+ }
+
+ private String getCaptivePortalServerHttpsUrl() {
+ return mSettings.getSetting(mContext,
+ Settings.Global.CAPTIVE_PORTAL_HTTPS_URL, DEFAULT_HTTPS_URL);
+ }
+
+ // Static for direct access by ConnectivityService
public static String getCaptivePortalServerHttpUrl(Context context) {
- return getSetting(context, Settings.Global.CAPTIVE_PORTAL_HTTP_URL, DEFAULT_HTTP_URL);
+ return getCaptivePortalServerHttpUrl(NetworkMonitorSettings.DEFAULT, context);
+ }
+
+ public static String getCaptivePortalServerHttpUrl(
+ NetworkMonitorSettings settings, Context context) {
+ return settings.getSetting(
+ context, Settings.Global.CAPTIVE_PORTAL_HTTP_URL, DEFAULT_HTTP_URL);
}
- private URL[] makeCaptivePortalFallbackUrls(Context context) {
+ private URL[] makeCaptivePortalFallbackUrls() {
String separator = ",";
- String firstUrl = getSetting(context,
+ String firstUrl = mSettings.getSetting(mContext,
Settings.Global.CAPTIVE_PORTAL_FALLBACK_URL, DEFAULT_FALLBACK_URL);
- String joinedUrls = firstUrl + separator + getSetting(context,
+ String joinedUrls = firstUrl + separator + mSettings.getSetting(mContext,
Settings.Global.CAPTIVE_PORTAL_OTHER_FALLBACK_URLS, DEFAULT_OTHER_FALLBACK_URLS);
List<URL> urls = new ArrayList<>();
for (String s : joinedUrls.split(separator)) {
@@ -733,13 +756,9 @@ public class NetworkMonitor extends StateMachine {
return urls.toArray(new URL[urls.size()]);
}
- private static String getCaptivePortalUserAgent(Context context) {
- return getSetting(context, Settings.Global.CAPTIVE_PORTAL_USER_AGENT, DEFAULT_USER_AGENT);
- }
-
- private static String getSetting(Context context, String symbol, String defaultValue) {
- final String value = Settings.Global.getString(context.getContentResolver(), symbol);
- return value != null ? value : defaultValue;
+ private String getCaptivePortalUserAgent() {
+ return mSettings.getSetting(mContext,
+ Settings.Global.CAPTIVE_PORTAL_USER_AGENT, DEFAULT_USER_AGENT);
}
private URL nextFallbackUrl() {
@@ -1035,12 +1054,13 @@ public class NetworkMonitor extends StateMachine {
*/
private void sendNetworkConditionsBroadcast(boolean responseReceived, boolean isCaptivePortal,
long requestTimestampMs, long responseTimestampMs) {
- if (Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 0) {
+ if (getWifiScansAlwaysAvailableDisabled()) {
return;
}
- if (systemReady == false) return;
+ if (!systemReady) {
+ return;
+ }
Intent latencyBroadcast = new Intent(ACTION_NETWORK_CONDITIONS_MEASURED);
switch (mNetworkAgentInfo.networkInfo.getType()) {
@@ -1144,4 +1164,24 @@ public class NetworkMonitor extends StateMachine {
ev.durationMs = durationMs;
mMetricsLog.log(mNetId, transports, ev);
}
+
+ @VisibleForTesting
+ public interface NetworkMonitorSettings {
+ int getSetting(Context context, String symbol, int defaultValue);
+ String getSetting(Context context, String symbol, String defaultValue);
+
+ static NetworkMonitorSettings DEFAULT = new DefaultNetworkMonitorSettings();
+ }
+
+ @VisibleForTesting
+ public static class DefaultNetworkMonitorSettings implements NetworkMonitorSettings {
+ public int getSetting(Context context, String symbol, int defaultValue) {
+ return Settings.Global.getInt(context.getContentResolver(), symbol, defaultValue);
+ }
+
+ public String getSetting(Context context, String symbol, String defaultValue) {
+ final String value = Settings.Global.getString(context.getContentResolver(), symbol);
+ return value != null ? value : defaultValue;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
index 6d5c428e58f8..5eafe5f9f64f 100644
--- a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
+++ b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
@@ -30,6 +30,10 @@ import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.NetworkStats;
import android.net.RouteInfo;
+import android.net.netlink.ConntrackMessage;
+import android.net.netlink.NetlinkConstants;
+import android.net.netlink.NetlinkSocket;
+import android.net.util.IpUtils;
import android.net.util.SharedLog;
import android.os.Handler;
import android.os.Looper;
@@ -37,10 +41,12 @@ import android.os.INetworkManagementService;
import android.os.RemoteException;
import android.os.SystemClock;
import android.provider.Settings;
+import android.system.ErrnoException;
+import android.system.OsConstants;
import android.text.TextUtils;
-import com.android.server.connectivity.tethering.OffloadHardwareInterface.ForwardedStats;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.server.connectivity.tethering.OffloadHardwareInterface.ForwardedStats;
import java.net.Inet4Address;
import java.net.Inet6Address;
@@ -63,6 +69,9 @@ import java.util.concurrent.TimeUnit;
*/
public class OffloadController {
private static final String TAG = OffloadController.class.getSimpleName();
+ private static final boolean DBG = false;
+ private static final String ANYIP = "0.0.0.0";
+ private static final ForwardedStats EMPTY_STATS = new ForwardedStats();
private final Handler mHandler;
private final OffloadHardwareInterface mHwInterface;
@@ -94,6 +103,9 @@ public class OffloadController {
// includes upstream interfaces that have a quota set.
private HashMap<String, Long> mInterfaceQuotas = new HashMap<>();
+ private int mNatUpdateCallbacksReceived;
+ private int mNatUpdateNetlinkErrors;
+
public OffloadController(Handler h, OffloadHardwareInterface hwi,
ContentResolver contentResolver, INetworkManagementService nms, SharedLog log) {
mHandler = h;
@@ -113,12 +125,12 @@ public class OffloadController {
}
}
- public void start() {
- if (started()) return;
+ public boolean start() {
+ if (started()) return true;
if (isOffloadDisabled()) {
mLog.i("tethering offload disabled");
- return;
+ return false;
}
if (!mConfigInitialized) {
@@ -126,11 +138,14 @@ public class OffloadController {
if (!mConfigInitialized) {
mLog.i("tethering offload config not supported");
stop();
- return;
+ return false;
}
}
mControlInitialized = mHwInterface.initOffloadControl(
+ // OffloadHardwareInterface guarantees that these callback
+ // methods are called on the handler passed to it, which is the
+ // same as mHandler, as coordinated by the setup in Tethering.
new OffloadHardwareInterface.ControlCallback() {
@Override
public void onStarted() {
@@ -148,6 +163,14 @@ public class OffloadController {
public void onStoppedUnsupported() {
if (!started()) return;
mLog.log("onStoppedUnsupported");
+ // Poll for statistics and trigger a sweep of tethering
+ // stats by observers. This might not succeed, but it's
+ // worth trying anyway. We need to do this because from
+ // this point on we continue with software forwarding,
+ // and we need to synchronize stats and limits between
+ // software and hardware forwarding.
+ updateStatsForAllUpstreams();
+ forceTetherStatsPoll();
}
@Override
@@ -155,11 +178,15 @@ public class OffloadController {
if (!started()) return;
mLog.log("onSupportAvailable");
- // [1] Poll for statistics and notify NetworkStats
- // [2] (Re)Push all state:
- // [a] push local prefixes
- // [b] push downstreams
- // [c] push upstream parameters
+ // [1] Poll for statistics and trigger a sweep of stats
+ // by observers. We need to do this to ensure that any
+ // limits set take into account any software tethering
+ // traffic that has been happening in the meantime.
+ updateStatsForAllUpstreams();
+ forceTetherStatsPoll();
+ // [2] (Re)Push all state.
+ // TODO: computeAndPushLocalPrefixes()
+ // TODO: push all downstream state.
pushUpstreamParameters(null);
}
@@ -181,12 +208,7 @@ public class OffloadController {
// The stats for the previous upstream were already updated on this thread
// just after the upstream was changed, so they are also up-to-date.
updateStatsForCurrentUpstream();
-
- try {
- mNms.tetherLimitReached(mStatsProvider);
- } catch (RemoteException e) {
- mLog.e("Cannot report data limit reached: " + e);
- }
+ forceTetherStatsPoll();
}
@Override
@@ -194,15 +216,20 @@ public class OffloadController {
String srcAddr, int srcPort,
String dstAddr, int dstPort) {
if (!started()) return;
- mLog.log(String.format("NAT timeout update: %s (%s,%s) -> (%s,%s)",
- proto, srcAddr, srcPort, dstAddr, dstPort));
+ updateNatTimeout(proto, srcAddr, srcPort, dstAddr, dstPort);
}
});
- if (!mControlInitialized) {
+
+ final boolean isStarted = started();
+ if (!isStarted) {
mLog.i("tethering offload control not supported");
stop();
+ } else {
+ mLog.log("tethering offload started");
+ mNatUpdateCallbacksReceived = 0;
+ mNatUpdateNetlinkErrors = 0;
}
- mLog.log("tethering offload started");
+ return isStarted;
}
public void stop() {
@@ -218,6 +245,10 @@ public class OffloadController {
if (wasStarted) mLog.log("tethering offload stopped");
}
+ private boolean started() {
+ return mConfigInitialized && mControlInitialized;
+ }
+
private class OffloadTetheringStatsProvider extends ITetheringStatsProvider.Stub {
@Override
public NetworkStats getTetherStats(int how) {
@@ -305,13 +336,33 @@ public class OffloadController {
maybeUpdateStats(currentUpstreamInterface());
}
+ private void updateStatsForAllUpstreams() {
+ // In practice, there should only ever be a single digit number of
+ // upstream interfaces over the lifetime of an active tethering session.
+ // Roughly speaking, imagine a very ambitious one or two of each of the
+ // following interface types: [ "rmnet_data", "wlan", "eth", "rndis" ].
+ for (Map.Entry<String, ForwardedStats> kv : mForwardedStats.entrySet()) {
+ maybeUpdateStats(kv.getKey());
+ }
+ }
+
+ private void forceTetherStatsPoll() {
+ try {
+ mNms.tetherLimitReached(mStatsProvider);
+ } catch (RemoteException e) {
+ mLog.e("Cannot report data limit reached: " + e);
+ }
+ }
+
public void setUpstreamLinkProperties(LinkProperties lp) {
if (!started() || Objects.equals(mUpstreamLinkProperties, lp)) return;
- String prevUpstream = (mUpstreamLinkProperties != null) ?
- mUpstreamLinkProperties.getInterfaceName() : null;
+ final String prevUpstream = currentUpstreamInterface();
mUpstreamLinkProperties = (lp != null) ? new LinkProperties(lp) : null;
+ // Make sure we record this interface in the ForwardedStats map.
+ final String iface = currentUpstreamInterface();
+ if (!TextUtils.isEmpty(iface)) mForwardedStats.putIfAbsent(iface, EMPTY_STATS);
// TODO: examine return code and decide what to do if programming
// upstream parameters fails (probably just wait for a subsequent
@@ -373,21 +424,21 @@ public class OffloadController {
mContentResolver, TETHER_OFFLOAD_DISABLED, defaultDisposition) != 0);
}
- private boolean started() {
- return mConfigInitialized && mControlInitialized;
- }
-
private boolean pushUpstreamParameters(String prevUpstream) {
- if (mUpstreamLinkProperties == null) {
+ final String iface = currentUpstreamInterface();
+
+ if (TextUtils.isEmpty(iface)) {
+ final boolean rval = mHwInterface.setUpstreamParameters("", ANYIP, ANYIP, null);
+ // Update stats after we've told the hardware to stop forwarding so
+ // we don't miss packets.
maybeUpdateStats(prevUpstream);
- return mHwInterface.setUpstreamParameters(null, null, null, null);
+ return rval;
}
// A stacked interface cannot be an upstream for hardware offload.
// Consequently, we examine only the primary interface name, look at
// getAddresses() rather than getAllAddresses(), and check getRoutes()
// rather than getAllRoutes().
- final String iface = mUpstreamLinkProperties.getInterfaceName();
final ArrayList<String> v6gateways = new ArrayList<>();
String v4addr = null;
String v4gateway = null;
@@ -483,10 +534,113 @@ public class OffloadController {
pw.println("Offload disabled");
return;
}
- pw.println("Offload HALs " + (started() ? "started" : "not started"));
+ final boolean isStarted = started();
+ pw.println("Offload HALs " + (isStarted ? "started" : "not started"));
LinkProperties lp = mUpstreamLinkProperties;
String upstream = (lp != null) ? lp.getInterfaceName() : null;
pw.println("Current upstream: " + upstream);
pw.println("Exempt prefixes: " + mLastLocalPrefixStrs);
+ pw.println("NAT timeout update callbacks received during the "
+ + (isStarted ? "current" : "last")
+ + " offload session: "
+ + mNatUpdateCallbacksReceived);
+ pw.println("NAT timeout update netlink errors during the "
+ + (isStarted ? "current" : "last")
+ + " offload session: "
+ + mNatUpdateNetlinkErrors);
+ }
+
+ private void updateNatTimeout(
+ int proto, String srcAddr, int srcPort, String dstAddr, int dstPort) {
+ final String protoName = protoNameFor(proto);
+ if (protoName == null) {
+ mLog.e("Unknown NAT update callback protocol: " + proto);
+ return;
+ }
+
+ final Inet4Address src = parseIPv4Address(srcAddr);
+ if (src == null) {
+ mLog.e("Failed to parse IPv4 address: " + srcAddr);
+ return;
+ }
+
+ if (!IpUtils.isValidUdpOrTcpPort(srcPort)) {
+ mLog.e("Invalid src port: " + srcPort);
+ return;
+ }
+
+ final Inet4Address dst = parseIPv4Address(dstAddr);
+ if (dst == null) {
+ mLog.e("Failed to parse IPv4 address: " + dstAddr);
+ return;
+ }
+
+ if (!IpUtils.isValidUdpOrTcpPort(dstPort)) {
+ mLog.e("Invalid dst port: " + dstPort);
+ return;
+ }
+
+ mNatUpdateCallbacksReceived++;
+ if (DBG) {
+ mLog.log(String.format("NAT timeout update: %s (%s, %s) -> (%s, %s)",
+ protoName, srcAddr, srcPort, dstAddr, dstPort));
+ }
+
+ final int timeoutSec = connectionTimeoutUpdateSecondsFor(proto);
+ final byte[] msg = ConntrackMessage.newIPv4TimeoutUpdateRequest(
+ proto, src, srcPort, dst, dstPort, timeoutSec);
+
+ try {
+ NetlinkSocket.sendOneShotKernelMessage(OsConstants.NETLINK_NETFILTER, msg);
+ } catch (ErrnoException e) {
+ mNatUpdateNetlinkErrors++;
+ mLog.e("Error updating NAT conntrack entry: " + e
+ + ", msg: " + NetlinkConstants.hexify(msg));
+ mLog.log("NAT timeout update callbacks received: " + mNatUpdateCallbacksReceived);
+ mLog.log("NAT timeout update netlink errors: " + mNatUpdateNetlinkErrors);
+ }
+ }
+
+ private static Inet4Address parseIPv4Address(String addrString) {
+ try {
+ final InetAddress ip = InetAddress.parseNumericAddress(addrString);
+ // TODO: Consider other sanitization steps here, including perhaps:
+ // not eql to 0.0.0.0
+ // not within 169.254.0.0/16
+ // not within ::ffff:0.0.0.0/96
+ // not within ::/96
+ // et cetera.
+ if (ip instanceof Inet4Address) {
+ return (Inet4Address) ip;
+ }
+ } catch (IllegalArgumentException iae) {}
+ return null;
+ }
+
+ private static String protoNameFor(int proto) {
+ // OsConstants values are not constant expressions; no switch statement.
+ if (proto == OsConstants.IPPROTO_UDP) {
+ return "UDP";
+ } else if (proto == OsConstants.IPPROTO_TCP) {
+ return "TCP";
+ }
+ return null;
+ }
+
+ private static int connectionTimeoutUpdateSecondsFor(int proto) {
+ // TODO: Replace this with more thoughtful work, perhaps reading from
+ // and maybe writing to any required
+ //
+ // /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_*
+ // /proc/sys/net/netfilter/nf_conntrack_udp_timeout{,_stream}
+ //
+ // entries. TBD.
+ if (proto == OsConstants.IPPROTO_TCP) {
+ // Cf. /proc/sys/net/netfilter/nf_conntrack_tcp_timeout_established
+ return 432000;
+ } else {
+ // Cf. /proc/sys/net/netfilter/nf_conntrack_udp_timeout_stream
+ return 180;
+ }
}
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java b/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
index 865a98902d0b..76195c4ac7d4 100644
--- a/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
+++ b/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
@@ -21,10 +21,12 @@ import static com.android.internal.util.BitUtils.uint16;
import android.hardware.tetheroffload.control.V1_0.IOffloadControl;
import android.hardware.tetheroffload.control.V1_0.ITetheringOffloadCallback;
import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate;
+import android.hardware.tetheroffload.control.V1_0.NetworkProtocol;
import android.hardware.tetheroffload.control.V1_0.OffloadCallbackEvent;
import android.os.Handler;
import android.os.RemoteException;
import android.net.util.SharedLog;
+import android.system.OsConstants;
import java.util.ArrayList;
@@ -107,6 +109,10 @@ public class OffloadHardwareInterface {
mLog.e("tethering offload control not supported: " + e);
return false;
}
+ if (mOffloadControl == null) {
+ mLog.e("tethering IOffloadControl.getService() returned null");
+ return false;
+ }
}
final String logmsg = String.format("initOffloadControl(%s)",
@@ -327,13 +333,24 @@ public class OffloadHardwareInterface {
public void updateTimeout(NatTimeoutUpdate params) {
handler.post(() -> {
controlCb.onNatTimeoutUpdate(
- params.proto,
+ networkProtocolToOsConstant(params.proto),
params.src.addr, uint16(params.src.port),
params.dst.addr, uint16(params.dst.port));
});
}
}
+ private static int networkProtocolToOsConstant(int proto) {
+ switch (proto) {
+ case NetworkProtocol.TCP: return OsConstants.IPPROTO_TCP;
+ case NetworkProtocol.UDP: return OsConstants.IPPROTO_UDP;
+ default:
+ // The caller checks this value and will log an error. Just make
+ // sure it won't collide with valid OsContants.IPPROTO_* values.
+ return -Math.abs(proto);
+ }
+ }
+
private static class CbResults {
boolean success;
String errMsg;
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
index 57d2502c6dc7..17adb1a74e30 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
@@ -27,6 +27,7 @@ import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.NetworkUtils;
import android.net.RouteInfo;
+import android.net.ip.InterfaceController;
import android.net.ip.RouterAdvertisementDaemon;
import android.net.ip.RouterAdvertisementDaemon.RaParams;
import android.net.util.NetdService;
@@ -107,8 +108,10 @@ public class TetherInterfaceStateMachine extends StateMachine {
private final SharedLog mLog;
private final INetworkManagementService mNMService;
+ private final INetd mNetd;
private final INetworkStatsService mStatsService;
private final IControlsTethering mTetherController;
+ private final InterfaceController mInterfaceCtrl;
private final String mIfaceName;
private final int mInterfaceType;
@@ -136,8 +139,11 @@ public class TetherInterfaceStateMachine extends StateMachine {
super(ifaceName, looper);
mLog = log.forSubComponent(ifaceName);
mNMService = nMService;
+ // TODO: This should be passed in for testability.
+ mNetd = NetdService.getInstance();
mStatsService = statsService;
mTetherController = tetherController;
+ mInterfaceCtrl = new InterfaceController(ifaceName, nMService, mNetd, mLog);
mIfaceName = ifaceName;
mInterfaceType = interfaceType;
mLinkProperties = new LinkProperties();
@@ -179,6 +185,7 @@ public class TetherInterfaceStateMachine extends StateMachine {
private void stopIPv4() { configureIPv4(false); }
+ // TODO: Refactor this in terms of calls to InterfaceController.
private boolean configureIPv4(boolean enabled) {
if (VDBG) Log.d(TAG, "configureIPv4(" + enabled + ")");
@@ -381,8 +388,8 @@ public class TetherInterfaceStateMachine extends StateMachine {
private void configureLocalIPv6Dns(
HashSet<Inet6Address> deprecatedDnses, HashSet<Inet6Address> newDnses) {
- final INetd netd = NetdService.getInstance();
- if (netd == null) {
+ // TODO: Is this really necessary? Can we not fail earlier if INetd cannot be located?
+ if (mNetd == null) {
if (newDnses != null) newDnses.clear();
mLog.e("No netd service instance available; not setting local IPv6 addresses");
return;
@@ -391,11 +398,8 @@ public class TetherInterfaceStateMachine extends StateMachine {
// [1] Remove deprecated local DNS IP addresses.
if (!deprecatedDnses.isEmpty()) {
for (Inet6Address dns : deprecatedDnses) {
- final String dnsString = dns.getHostAddress();
- try {
- netd.interfaceDelAddress(mIfaceName, dnsString, RFC7421_PREFIX_LENGTH);
- } catch (ServiceSpecificException | RemoteException e) {
- mLog.e("Failed to remove local dns IP " + dnsString + ": " + e);
+ if (!mInterfaceCtrl.removeAddress(dns, RFC7421_PREFIX_LENGTH)) {
+ mLog.e("Failed to remove local dns IP " + dns);
}
mLinkProperties.removeLinkAddress(new LinkAddress(dns, RFC7421_PREFIX_LENGTH));
@@ -410,11 +414,8 @@ public class TetherInterfaceStateMachine extends StateMachine {
}
for (Inet6Address dns : addedDnses) {
- final String dnsString = dns.getHostAddress();
- try {
- netd.interfaceAddAddress(mIfaceName, dnsString, RFC7421_PREFIX_LENGTH);
- } catch (ServiceSpecificException | RemoteException e) {
- mLog.e("Failed to add local dns IP " + dnsString + ": " + e);
+ if (!mInterfaceCtrl.addAddress(dns, RFC7421_PREFIX_LENGTH)) {
+ mLog.e("Failed to add local dns IP " + dns);
newDnses.remove(dns);
}
@@ -423,7 +424,7 @@ public class TetherInterfaceStateMachine extends StateMachine {
}
try {
- netd.tetherApplyDnsInterfaces();
+ mNetd.tetherApplyDnsInterfaces();
} catch (ServiceSpecificException | RemoteException e) {
mLog.e("Failed to update local DNS caching server");
if (newDnses != null) newDnses.clear();
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index d9ca00c1cb77..ea260a08c2e6 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -4184,7 +4184,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
}
public static String getString(int uid, long procStateSeq) {
- return "UID=" + uid + " procStateSeq=" + procStateSeq;
+ return "UID=" + uid + " Seq=" + procStateSeq;
}
private int increaseNext(int next, int increment) {
diff --git a/services/core/java/com/android/server/om/IdmapManager.java b/services/core/java/com/android/server/om/IdmapManager.java
index 04d91f882d04..807c343d0d10 100644
--- a/services/core/java/com/android/server/om/IdmapManager.java
+++ b/services/core/java/com/android/server/om/IdmapManager.java
@@ -92,26 +92,10 @@ class IdmapManager {
return new File(getIdmapPath(overlayPackage.applicationInfo.getBaseCodePath())).isFile();
}
- boolean isDangerous(@NonNull final PackageInfo overlayPackage, final int userId) {
- // unused userId: see comment in OverlayManagerServiceImpl.removeIdmapIfPossible
- return isDangerous(getIdmapPath(overlayPackage.applicationInfo.getBaseCodePath()));
- }
-
private String getIdmapPath(@NonNull final String baseCodePath) {
final StringBuilder sb = new StringBuilder("/data/resource-cache/");
sb.append(baseCodePath.substring(1).replace('/', '@'));
sb.append("@idmap");
return sb.toString();
}
-
- private boolean isDangerous(@NonNull final String idmapPath) {
- try (DataInputStream dis = new DataInputStream(new FileInputStream(idmapPath))) {
- final int magic = dis.readInt();
- final int version = dis.readInt();
- final int dangerous = dis.readInt();
- return dangerous != 0;
- } catch (IOException e) {
- return true;
- }
- }
}
diff --git a/services/core/java/com/android/server/timezone/IntentHelperImpl.java b/services/core/java/com/android/server/timezone/IntentHelperImpl.java
index 11928b964ec5..6db70cd870db 100644
--- a/services/core/java/com/android/server/timezone/IntentHelperImpl.java
+++ b/services/core/java/com/android/server/timezone/IntentHelperImpl.java
@@ -53,20 +53,34 @@ final class IntentHelperImpl implements IntentHelper {
// The intent filter that triggers when package update events happen that indicate there may
// be work to do.
IntentFilter packageIntentFilter = new IntentFilter();
- // Either of these mean a downgrade?
- packageIntentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
- packageIntentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+
packageIntentFilter.addDataScheme("package");
packageIntentFilter.addDataSchemeSpecificPart(
updaterAppPackageName, PatternMatcher.PATTERN_LITERAL);
packageIntentFilter.addDataSchemeSpecificPart(
dataAppPackageName, PatternMatcher.PATTERN_LITERAL);
+
+ // ACTION_PACKAGE_ADDED is fired when a package is upgraded or downgraded (in addition to
+ // ACTION_PACKAGE_REMOVED and ACTION_PACKAGE_REPLACED). A system/priv-app can never be
+ // removed entirely so we do not need to trigger on ACTION_PACKAGE_REMOVED or
+ // ACTION_PACKAGE_FULLY_REMOVED.
+ packageIntentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+
+ // ACTION_PACKAGE_CHANGED is used when a package is disabled / re-enabled. It is not
+ // strictly necessary to trigger on this but it won't hurt anything and may catch some cases
+ // where a package has changed while disabled.
+ // Note: ACTION_PACKAGE_CHANGED is not fired when updating a suspended app, but
+ // ACTION_PACKAGE_ADDED, ACTION_PACKAGE_REMOVED and ACTION_PACKAGE_REPLACED are (and the app
+ // is left in an unsuspended state after this).
+ packageIntentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+
+ // We do not register for ACTION_PACKAGE_RESTARTED because it doesn't imply an update.
+ // We do not register for ACTION_PACKAGE_DATA_CLEARED because the updater / data apps are
+ // not expected to need local data.
+
Receiver packageUpdateReceiver = new Receiver(listener, true /* packageUpdated */);
mContext.registerReceiver(packageUpdateReceiver, packageIntentFilter);
- // TODO(nfuller): Add more exotic intents as needed. e.g.
- // packageIntentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- // Also, disabled...?
mReliabilityReceiver = new Receiver(listener, false /* packageUpdated */);
}
diff --git a/services/core/java/com/android/server/timezone/RulesManagerService.java b/services/core/java/com/android/server/timezone/RulesManagerService.java
index 50f27ed67a36..3ad4419c7273 100644
--- a/services/core/java/com/android/server/timezone/RulesManagerService.java
+++ b/services/core/java/com/android/server/timezone/RulesManagerService.java
@@ -343,16 +343,20 @@ public final class RulesManagerService extends IRulesManager.Stub {
@Override
public void run() {
EventLogTags.writeTimezoneUninstallStarted(toStringOrNull(mCheckToken));
- boolean success = false;
+ boolean packageTrackerStatus = false;
try {
- success = mInstaller.stageUninstall();
- // Right now we just have success (0) / failure (1). All clients should be checking
- // against SUCCESS. More granular failures may be added in future.
- int resultCode = success ? Callback.SUCCESS
- : Callback.ERROR_UNKNOWN_FAILURE;
+ int uninstallResult = mInstaller.stageUninstall();
+ packageTrackerStatus = (uninstallResult == TimeZoneDistroInstaller.UNINSTALL_SUCCESS
+ || uninstallResult == TimeZoneDistroInstaller.UNINSTALL_NOTHING_INSTALLED);
+
+ // Right now we just have Callback.SUCCESS / Callback.ERROR_UNKNOWN_FAILURE for
+ // uninstall. All clients should be checking against SUCCESS. More granular failures
+ // may be added in future.
+ int callbackResultCode =
+ packageTrackerStatus ? Callback.SUCCESS : Callback.ERROR_UNKNOWN_FAILURE;
EventLogTags.writeTimezoneUninstallComplete(
- toStringOrNull(mCheckToken), resultCode);
- sendFinishedStatus(mCallback, resultCode);
+ toStringOrNull(mCheckToken), callbackResultCode);
+ sendFinishedStatus(mCallback, callbackResultCode);
} catch (Exception e) {
EventLogTags.writeTimezoneUninstallComplete(
toStringOrNull(mCheckToken), Callback.ERROR_UNKNOWN_FAILURE);
@@ -360,7 +364,7 @@ public final class RulesManagerService extends IRulesManager.Stub {
sendFinishedStatus(mCallback, Callback.ERROR_UNKNOWN_FAILURE);
} finally {
// Notify the package tracker that the operation is now complete.
- mPackageTracker.recordCheckResult(mCheckToken, success);
+ mPackageTracker.recordCheckResult(mCheckToken, packageTrackerStatus);
mOperationInProgress.set(false);
}
diff --git a/services/core/jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp b/services/core/jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp
index f9cbd1601290..9a17635721b8 100644
--- a/services/core/jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp
+++ b/services/core/jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp
@@ -113,7 +113,7 @@ static jboolean android_server_connectivity_tethering_OffloadHardwareInterface_c
hidl_handle h1(handleFromFileDescriptor(std::move(fd1))),
h2(handleFromFileDescriptor(std::move(fd2)));
- bool rval;
+ bool rval(false);
hidl_string msg;
const auto status = configInterface->setHandles(h1, h2,
[&rval, &msg](bool success, const hidl_string& errMsg) {
@@ -123,6 +123,8 @@ static jboolean android_server_connectivity_tethering_OffloadHardwareInterface_c
if (!status.isOk() || !rval) {
ALOGE("IOffloadConfig::setHandles() error: '%s' / '%s'",
status.description().c_str(), msg.c_str());
+ // If status is somehow not ok, make sure rval captures this too.
+ rval = false;
}
return rval;
diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java
index 8cb2df761fbe..190b3a613ca1 100644
--- a/services/net/java/android/net/apf/ApfFilter.java
+++ b/services/net/java/android/net/apf/ApfFilter.java
@@ -180,6 +180,7 @@ public class ApfFilter {
private static final int ETH_DEST_ADDR_OFFSET = 0;
private static final int ETH_ETHERTYPE_OFFSET = 12;
private static final int ETH_TYPE_MIN = 0x0600;
+ private static final int ETH_TYPE_MAX = 0xFFFF;
private static final byte[] ETH_BROADCAST_MAC_ADDRESS =
{(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
// TODO: Make these offsets relative to end of link-layer header; don't include ETH_HEADER_LEN.
@@ -232,6 +233,9 @@ public class ApfFilter {
private static final int ARP_TARGET_IP_ADDRESS_OFFSET = ETH_HEADER_LEN + 24;
// Do not log ApfProgramEvents whose actual lifetimes was less than this.
private static final int APF_PROGRAM_EVENT_LIFETIME_THRESHOLD = 2;
+ // Limit on the Black List size to cap on program usage for this
+ // TODO: Select a proper max length
+ private static final int APF_MAX_ETH_TYPE_BLACK_LIST_LEN = 20;
private final ApfCapabilities mApfCapabilities;
private final IpManager.Callback mIpManagerCallback;
@@ -247,6 +251,8 @@ public class ApfFilter {
@GuardedBy("this")
private boolean mMulticastFilter;
private final boolean mDrop802_3Frames;
+ private final int[] mEthTypeBlackList;
+
// Our IPv4 address, if we have just one, otherwise null.
@GuardedBy("this")
private byte[] mIPv4Address;
@@ -257,12 +263,16 @@ public class ApfFilter {
@VisibleForTesting
ApfFilter(ApfCapabilities apfCapabilities, NetworkInterface networkInterface,
IpManager.Callback ipManagerCallback, boolean multicastFilter,
- boolean ieee802_3Filter, IpConnectivityLog log) {
+ boolean ieee802_3Filter, int[] ethTypeBlackList, IpConnectivityLog log) {
mApfCapabilities = apfCapabilities;
mIpManagerCallback = ipManagerCallback;
mNetworkInterface = networkInterface;
mMulticastFilter = multicastFilter;
mDrop802_3Frames = ieee802_3Filter;
+
+ // Now fill the black list from the passed array
+ mEthTypeBlackList = filterEthTypeBlackList(ethTypeBlackList);
+
mMetricsLog = log;
// TODO: ApfFilter should not generate programs until IpManager sends provisioning success.
@@ -278,6 +288,35 @@ public class ApfFilter {
return mUniqueCounter++;
}
+ @GuardedBy("this")
+ private static int[] filterEthTypeBlackList(int[] ethTypeBlackList) {
+ ArrayList<Integer> bl = new ArrayList<Integer>();
+
+ for (int p : ethTypeBlackList) {
+ // Check if the protocol is a valid ether type
+ if ((p < ETH_TYPE_MIN) || (p > ETH_TYPE_MAX)) {
+ continue;
+ }
+
+ // Check if the protocol is not repeated in the passed array
+ if (bl.contains(p)) {
+ continue;
+ }
+
+ // Check if list reach its max size
+ if (bl.size() == APF_MAX_ETH_TYPE_BLACK_LIST_LEN) {
+ Log.w(TAG, "Passed EthType Black List size too large (" + bl.size() +
+ ") using top " + APF_MAX_ETH_TYPE_BLACK_LIST_LEN + " protocols");
+ break;
+ }
+
+ // Now add the protocol to the list
+ bl.add(p);
+ }
+
+ return bl.stream().mapToInt(Integer::intValue).toArray();
+ }
+
/**
* Attempt to start listening for RAs and, if RAs are received, generating and installing
* filters to ignore useless RAs.
@@ -891,6 +930,7 @@ public class ApfFilter {
* Begin generating an APF program to:
* <ul>
* <li>Drop/Pass 802.3 frames (based on policy)
+ * <li>Drop packets with EtherType within the Black List
* <li>Drop ARP requests not for us, if mIPv4Address is set,
* <li>Drop IPv4 broadcast packets, except DHCP destined to our MAC,
* <li>Drop IPv4 multicast packets, if mMulticastFilter,
@@ -914,6 +954,8 @@ public class ApfFilter {
//
// if it's a 802.3 Frame (ethtype < 0x0600):
// drop or pass based on configurations
+ // if it has a ether-type that belongs to the black list
+ // drop
// if it's ARP:
// insert ARP filter to drop or pass these appropriately
// if it's IPv4:
@@ -931,6 +973,11 @@ public class ApfFilter {
gen.addJumpIfR0LessThan(ETH_TYPE_MIN, gen.DROP_LABEL);
}
+ // Handle ether-type black list
+ for (int p : mEthTypeBlackList) {
+ gen.addJumpIfR0Equals(p, gen.DROP_LABEL);
+ }
+
// Add ARP filters:
String skipArpFiltersLabel = "skipArpFilters";
gen.addJumpIfR0NotEquals(ETH_P_ARP, skipArpFiltersLabel);
@@ -1115,7 +1162,7 @@ public class ApfFilter {
*/
public static ApfFilter maybeCreate(ApfCapabilities apfCapabilities,
NetworkInterface networkInterface, IpManager.Callback ipManagerCallback,
- boolean multicastFilter, boolean ieee802_3Filter) {
+ boolean multicastFilter, boolean ieee802_3Filter, int[] ethTypeBlackList) {
if (apfCapabilities == null || networkInterface == null) return null;
if (apfCapabilities.apfVersionSupported == 0) return null;
if (apfCapabilities.maximumApfProgramSize < 512) {
@@ -1132,7 +1179,7 @@ public class ApfFilter {
return null;
}
return new ApfFilter(apfCapabilities, networkInterface, ipManagerCallback,
- multicastFilter, ieee802_3Filter, new IpConnectivityLog());
+ multicastFilter, ieee802_3Filter, ethTypeBlackList, new IpConnectivityLog());
}
public synchronized void shutdown() {
diff --git a/services/net/java/android/net/ip/InterfaceController.java b/services/net/java/android/net/ip/InterfaceController.java
new file mode 100644
index 000000000000..02e4f875230a
--- /dev/null
+++ b/services/net/java/android/net/ip/InterfaceController.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.ip;
+
+import android.net.INetd;
+import android.net.InterfaceConfiguration;
+import android.net.LinkAddress;
+import android.net.util.NetdService;
+import android.net.util.SharedLog;
+import android.os.INetworkManagementService;
+import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.system.OsConstants;
+
+import java.net.InetAddress;
+
+
+/**
+ * Encapsulates the multiple IP configuration operations performed on an interface.
+ *
+ * TODO: refactor/eliminate the redundant ways to set and clear addresses.
+ *
+ * @hide
+ */
+public class InterfaceController {
+ private final static boolean DBG = false;
+
+ private final String mIfName;
+ private final INetworkManagementService mNMS;
+ private final INetd mNetd;
+ private final SharedLog mLog;
+
+ public InterfaceController(String ifname, INetworkManagementService nms, INetd netd,
+ SharedLog log) {
+ mIfName = ifname;
+ mNMS = nms;
+ mNetd = netd;
+ mLog = log;
+ }
+
+ public boolean setIPv4Address(LinkAddress address) {
+ final InterfaceConfiguration ifcg = new InterfaceConfiguration();
+ ifcg.setLinkAddress(address);
+ try {
+ mNMS.setInterfaceConfig(mIfName, ifcg);
+ if (DBG) mLog.log("IPv4 configuration succeeded");
+ } catch (IllegalStateException | RemoteException e) {
+ logError("IPv4 configuration failed: %s", e);
+ return false;
+ }
+ return true;
+ }
+
+ public boolean clearIPv4Address() {
+ try {
+ final InterfaceConfiguration ifcg = new InterfaceConfiguration();
+ ifcg.setLinkAddress(new LinkAddress("0.0.0.0/0"));
+ mNMS.setInterfaceConfig(mIfName, ifcg);
+ } catch (IllegalStateException | RemoteException e) {
+ logError("Failed to clear IPv4 address on interface %s: %s", mIfName, e);
+ return false;
+ }
+ return true;
+ }
+
+ public boolean enableIPv6() {
+ try {
+ mNMS.enableIpv6(mIfName);
+ } catch (IllegalStateException | RemoteException e) {
+ logError("enabling IPv6 failed: %s", e);
+ return false;
+ }
+ return true;
+ }
+
+ public boolean disableIPv6() {
+ try {
+ mNMS.disableIpv6(mIfName);
+ } catch (IllegalStateException | RemoteException e) {
+ logError("disabling IPv6 failed: %s", e);
+ return false;
+ }
+ return true;
+ }
+
+ public boolean setIPv6PrivacyExtensions(boolean enabled) {
+ try {
+ mNMS.setInterfaceIpv6PrivacyExtensions(mIfName, enabled);
+ } catch (IllegalStateException | RemoteException e) {
+ logError("error setting IPv6 privacy extensions: %s", e);
+ return false;
+ }
+ return true;
+ }
+
+ public boolean setIPv6AddrGenModeIfSupported(int mode) {
+ try {
+ mNMS.setIPv6AddrGenMode(mIfName, mode);
+ } catch (RemoteException e) {
+ logError("Unable to set IPv6 addrgen mode: %s", e);
+ return false;
+ } catch (ServiceSpecificException e) {
+ if (e.errorCode != OsConstants.EOPNOTSUPP) {
+ logError("Unable to set IPv6 addrgen mode: %s", e);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public boolean addAddress(LinkAddress addr) {
+ return addAddress(addr.getAddress(), addr.getPrefixLength());
+ }
+
+ public boolean addAddress(InetAddress ip, int prefixLen) {
+ try {
+ mNetd.interfaceAddAddress(mIfName, ip.getHostAddress(), prefixLen);
+ } catch (ServiceSpecificException | RemoteException e) {
+ logError("failed to add %s/%d: %s", ip, prefixLen, e);
+ return false;
+ }
+ return true;
+ }
+
+ public boolean removeAddress(InetAddress ip, int prefixLen) {
+ try {
+ mNetd.interfaceDelAddress(mIfName, ip.getHostAddress(), prefixLen);
+ } catch (ServiceSpecificException | RemoteException e) {
+ logError("failed to remove %s/%d: %s", ip, prefixLen, e);
+ return false;
+ }
+ return true;
+ }
+
+ public boolean clearAllAddresses() {
+ try {
+ mNMS.clearInterfaceAddresses(mIfName);
+ } catch (Exception e) {
+ logError("Failed to clear addresses: %s", e);
+ return false;
+ }
+ return true;
+ }
+
+ private void logError(String fmt, Object... args) {
+ mLog.e(String.format(fmt, args));
+ }
+}
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index facdb8510759..b1eb0854e334 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -22,7 +22,6 @@ import com.android.internal.util.WakeupMessage;
import android.content.Context;
import android.net.DhcpResults;
import android.net.INetd;
-import android.net.InterfaceConfiguration;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties.ProvisioningChange;
@@ -43,9 +42,7 @@ import android.os.INetworkManagementService;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.os.ServiceSpecificException;
import android.os.SystemClock;
-import android.system.OsConstants;
import android.text.TextUtils;
import android.util.LocalLog;
import android.util.Log;
@@ -94,7 +91,6 @@ import java.util.stream.Collectors;
*/
public class IpManager extends StateMachine {
private static final boolean DBG = false;
- private static final boolean VDBG = false;
// For message logging.
private static final Class[] sMessageClasses = { IpManager.class, DhcpClient.class };
@@ -526,17 +522,18 @@ public class IpManager extends StateMachine {
public static final String DUMP_ARG = "ipmanager";
public static final String DUMP_ARG_CONFIRM = "confirm";
- private static final int CMD_STOP = 1;
- private static final int CMD_START = 2;
- private static final int CMD_CONFIRM = 3;
- private static final int EVENT_PRE_DHCP_ACTION_COMPLETE = 4;
+ private static final int CMD_TERMINATE_AFTER_STOP = 1;
+ private static final int CMD_STOP = 2;
+ private static final int CMD_START = 3;
+ private static final int CMD_CONFIRM = 4;
+ private static final int EVENT_PRE_DHCP_ACTION_COMPLETE = 5;
// Sent by NetlinkTracker to communicate netlink events.
- private static final int EVENT_NETLINK_LINKPROPERTIES_CHANGED = 5;
- private static final int CMD_UPDATE_TCP_BUFFER_SIZES = 6;
- private static final int CMD_UPDATE_HTTP_PROXY = 7;
- private static final int CMD_SET_MULTICAST_FILTER = 8;
- private static final int EVENT_PROVISIONING_TIMEOUT = 9;
- private static final int EVENT_DHCPACTION_TIMEOUT = 10;
+ private static final int EVENT_NETLINK_LINKPROPERTIES_CHANGED = 6;
+ private static final int CMD_UPDATE_TCP_BUFFER_SIZES = 7;
+ private static final int CMD_UPDATE_HTTP_PROXY = 8;
+ private static final int CMD_SET_MULTICAST_FILTER = 9;
+ private static final int EVENT_PROVISIONING_TIMEOUT = 10;
+ private static final int EVENT_DHCPACTION_TIMEOUT = 11;
private static final int MAX_LOG_RECORDS = 500;
private static final int MAX_PACKET_RECORDS = 100;
@@ -568,7 +565,7 @@ public class IpManager extends StateMachine {
private final LocalLog mConnectivityPacketLog;
private final MessageHandlingLogger mMsgStateLogger;
private final IpConnectivityLog mMetricsLog = new IpConnectivityLog();
- private final INetd mNetd;
+ private final InterfaceController mInterfaceCtrl;
private NetworkInterface mNetworkInterface;
@@ -612,12 +609,13 @@ public class IpManager extends StateMachine {
mClatInterfaceName = CLAT_PREFIX + ifName;
mCallback = new LoggingCallbackWrapper(callback);
mNwService = nwService;
- mNetd = netd;
mLog = new SharedLog(MAX_LOG_RECORDS, mTag);
mConnectivityPacketLog = new LocalLog(MAX_PACKET_RECORDS);
mMsgStateLogger = new MessageHandlingLogger();
+ mInterfaceCtrl = new InterfaceController(mInterfaceName, mNwService, netd, mLog);
+
mNetlinkTracker = new NetlinkTracker(
mInterfaceName,
new NetlinkTracker.Callback() {
@@ -704,6 +702,16 @@ public class IpManager extends StateMachine {
mMultinetworkPolicyTracker.start();
}
+ private void stopStateMachineUpdaters() {
+ try {
+ mNwService.unregisterObserver(mNetlinkTracker);
+ } catch (RemoteException e) {
+ logError("Couldn't unregister NetlinkTracker: %s", e);
+ }
+
+ mMultinetworkPolicyTracker.shutdown();
+ }
+
@Override
protected void onQuitting() {
mCallback.onQuit();
@@ -712,8 +720,7 @@ public class IpManager extends StateMachine {
// Shut down this IpManager instance altogether.
public void shutdown() {
stop();
- mMultinetworkPolicyTracker.shutdown();
- quit();
+ sendMessage(CMD_TERMINATE_AFTER_STOP);
}
public static ProvisioningConfiguration.Builder buildProvisioningConfiguration() {
@@ -858,7 +865,7 @@ public class IpManager extends StateMachine {
final String richerLogLine = getWhatToString(msg.what) + " " + logLine;
mLog.log(richerLogLine);
- if (VDBG) {
+ if (DBG) {
Log.d(mTag, richerLogLine);
}
@@ -1013,19 +1020,19 @@ public class IpManager extends StateMachine {
private void dispatchCallback(ProvisioningChange delta, LinkProperties newLp) {
switch (delta) {
case GAINED_PROVISIONING:
- if (VDBG) { Log.d(mTag, "onProvisioningSuccess()"); }
+ if (DBG) { Log.d(mTag, "onProvisioningSuccess()"); }
recordMetric(IpManagerEvent.PROVISIONING_OK);
mCallback.onProvisioningSuccess(newLp);
break;
case LOST_PROVISIONING:
- if (VDBG) { Log.d(mTag, "onProvisioningFailure()"); }
+ if (DBG) { Log.d(mTag, "onProvisioningFailure()"); }
recordMetric(IpManagerEvent.PROVISIONING_FAIL);
mCallback.onProvisioningFailure(newLp);
break;
default:
- if (VDBG) { Log.d(mTag, "onLinkPropertiesChange()"); }
+ if (DBG) { Log.d(mTag, "onLinkPropertiesChange()"); }
mCallback.onLinkPropertiesChange(newLp);
break;
}
@@ -1113,7 +1120,7 @@ public class IpManager extends StateMachine {
addAllReachableDnsServers(newLp, config.dnsServers);
}
final LinkProperties oldLp = mLinkProperties;
- if (VDBG) {
+ if (DBG) {
Log.d(mTag, String.format("Netlink-seen LPs: %s, new LPs: %s; old LPs: %s",
netlinkLinkProperties, newLp, oldLp));
}
@@ -1148,35 +1155,12 @@ public class IpManager extends StateMachine {
return (delta != ProvisioningChange.LOST_PROVISIONING);
}
- private boolean setIPv4Address(LinkAddress address) {
- final InterfaceConfiguration ifcg = new InterfaceConfiguration();
- ifcg.setLinkAddress(address);
- try {
- mNwService.setInterfaceConfig(mInterfaceName, ifcg);
- if (VDBG) Log.d(mTag, "IPv4 configuration succeeded");
- } catch (IllegalStateException | RemoteException e) {
- logError("IPv4 configuration failed: %s", e);
- return false;
- }
- return true;
- }
-
- private void clearIPv4Address() {
- try {
- final InterfaceConfiguration ifcg = new InterfaceConfiguration();
- ifcg.setLinkAddress(new LinkAddress("0.0.0.0/0"));
- mNwService.setInterfaceConfig(mInterfaceName, ifcg);
- } catch (IllegalStateException | RemoteException e) {
- logError("Failed to clear IPv4 address on interface %s: %s", mInterfaceName, e);
- }
- }
-
private void handleIPv4Success(DhcpResults dhcpResults) {
mDhcpResults = new DhcpResults(dhcpResults);
final LinkProperties newLp = assembleLinkProperties();
final ProvisioningChange delta = setLinkProperties(newLp);
- if (VDBG) {
+ if (DBG) {
Log.d(mTag, "onNewDhcpResults(" + Objects.toString(dhcpResults) + ")");
}
mCallback.onNewDhcpResults(dhcpResults);
@@ -1190,9 +1174,9 @@ public class IpManager extends StateMachine {
// that could trigger a call to this function. If we missed handling
// that message in StartedState for some reason we would still clear
// any addresses upon entry to StoppedState.
- clearIPv4Address();
+ mInterfaceCtrl.clearIPv4Address();
mDhcpResults = null;
- if (VDBG) { Log.d(mTag, "onNewDhcpResults(null)"); }
+ if (DBG) { Log.d(mTag, "onNewDhcpResults(null)"); }
mCallback.onNewDhcpResults(null);
handleProvisioningFailure();
@@ -1229,7 +1213,7 @@ public class IpManager extends StateMachine {
// If we have a StaticIpConfiguration attempt to apply it and
// handle the result accordingly.
if (mConfiguration.mStaticIpConfig != null) {
- if (setIPv4Address(mConfiguration.mStaticIpConfig.ipAddress)) {
+ if (mInterfaceCtrl.setIPv4Address(mConfiguration.mStaticIpConfig.ipAddress)) {
handleIPv4Success(new DhcpResults(mConfiguration.mStaticIpConfig));
} else {
return false;
@@ -1244,46 +1228,16 @@ public class IpManager extends StateMachine {
return true;
}
- private void setIPv6AddrGenModeIfSupported() throws RemoteException {
- try {
- mNwService.setIPv6AddrGenMode(mInterfaceName, mConfiguration.mIPv6AddrGenMode);
- } catch (ServiceSpecificException e) {
- if (e.errorCode != OsConstants.EOPNOTSUPP) {
- logError("Unable to set IPv6 addrgen mode: %s", e);
- }
- }
- }
-
private boolean startIPv6() {
- // Set privacy extensions.
- try {
- mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);
-
- setIPv6AddrGenModeIfSupported();
- mNwService.enableIpv6(mInterfaceName);
- } catch (IllegalStateException | RemoteException | ServiceSpecificException e) {
- logError("Unable to change interface settings: %s", e);
- return false;
- }
-
- return true;
+ return mInterfaceCtrl.setIPv6PrivacyExtensions(true) &&
+ mInterfaceCtrl.setIPv6AddrGenModeIfSupported(mConfiguration.mIPv6AddrGenMode) &&
+ mInterfaceCtrl.enableIPv6();
}
private boolean applyInitialConfig(InitialConfiguration config) {
- if (mNetd == null) {
- logError("tried to add %s to %s but INetd was null", config, mInterfaceName);
- return false;
- }
-
// TODO: also support specifying a static IPv4 configuration in InitialConfiguration.
for (LinkAddress addr : findAll(config.ipAddresses, LinkAddress::isIPv6)) {
- try {
- mNetd.interfaceAddAddress(
- mInterfaceName, addr.getAddress().getHostAddress(), addr.getPrefixLength());
- } catch (ServiceSpecificException | RemoteException e) {
- logError("failed to add %s to %s: %s", addr, mInterfaceName, e);
- return false;
- }
+ if (!mInterfaceCtrl.addAddress(addr)) return false;
}
return true;
@@ -1320,17 +1274,8 @@ public class IpManager extends StateMachine {
// - we don't get IPv4 routes from netlink
// so we neither react to nor need to wait for changes in either.
- try {
- mNwService.disableIpv6(mInterfaceName);
- } catch (Exception e) {
- logError("Failed to disable IPv6: %s", e);
- }
-
- try {
- mNwService.clearInterfaceAddresses(mInterfaceName);
- } catch (Exception e) {
- logError("Failed to clear addresses: %s", e);
- }
+ mInterfaceCtrl.disableIPv6();
+ mInterfaceCtrl.clearAllAddresses();
}
class StoppedState extends State {
@@ -1348,6 +1293,11 @@ public class IpManager extends StateMachine {
@Override
public boolean processMessage(Message msg) {
switch (msg.what) {
+ case CMD_TERMINATE_AFTER_STOP:
+ stopStateMachineUpdaters();
+ quit();
+ break;
+
case CMD_STOP:
break;
@@ -1404,7 +1354,7 @@ public class IpManager extends StateMachine {
break;
case DhcpClient.CMD_CLEAR_LINKADDRESS:
- clearIPv4Address();
+ mInterfaceCtrl.clearIPv4Address();
break;
case DhcpClient.CMD_ON_QUIT:
@@ -1494,8 +1444,11 @@ public class IpManager extends StateMachine {
boolean filter802_3Frames =
mContext.getResources().getBoolean(R.bool.config_apfDrop802_3Frames);
+ int[] ethTypeBlackList = mContext.getResources().getIntArray(
+ R.array.config_apfEthTypeBlackList);
+
mApfFilter = ApfFilter.maybeCreate(mConfiguration.mApfCapabilities, mNetworkInterface,
- mCallback, mMulticastFiltering, filter802_3Frames);
+ mCallback, mMulticastFiltering, filter802_3Frames, ethTypeBlackList);
// TODO: investigate the effects of any multicast filtering racing/interfering with the
// rest of this IP configuration startup.
if (mApfFilter == null) {
@@ -1657,12 +1610,12 @@ public class IpManager extends StateMachine {
break;
case DhcpClient.CMD_CLEAR_LINKADDRESS:
- clearIPv4Address();
+ mInterfaceCtrl.clearIPv4Address();
break;
case DhcpClient.CMD_CONFIGURE_LINKADDRESS: {
final LinkAddress ipAddress = (LinkAddress) msg.obj;
- if (setIPv4Address(ipAddress)) {
+ if (mInterfaceCtrl.setIPv4Address(ipAddress)) {
mDhcpClient.sendMessage(DhcpClient.EVENT_LINKADDRESS_CONFIGURED);
} else {
logError("Failed to set IPv4 address.");
diff --git a/services/net/java/android/net/ip/IpReachabilityMonitor.java b/services/net/java/android/net/ip/IpReachabilityMonitor.java
index e833f6a03bce..714b35a03396 100644
--- a/services/net/java/android/net/ip/IpReachabilityMonitor.java
+++ b/services/net/java/android/net/ip/IpReachabilityMonitor.java
@@ -205,44 +205,14 @@ public class IpReachabilityMonitor {
final byte[] msg = RtNetlinkNeighborMessage.newNewNeighborMessage(
1, ip, StructNdMsg.NUD_PROBE, ifIndex, null);
- int errno = -OsConstants.EPROTO;
- try (NetlinkSocket nlSocket = new NetlinkSocket(OsConstants.NETLINK_ROUTE)) {
- final long IO_TIMEOUT = 300L;
- nlSocket.connectToKernel();
- nlSocket.sendMessage(msg, 0, msg.length, IO_TIMEOUT);
- final ByteBuffer bytes = nlSocket.recvMessage(IO_TIMEOUT);
- // recvMessage() guaranteed to not return null if it did not throw.
- final NetlinkMessage response = NetlinkMessage.parse(bytes);
- if (response != null && response instanceof NetlinkErrorMessage &&
- (((NetlinkErrorMessage) response).getNlMsgError() != null)) {
- errno = ((NetlinkErrorMessage) response).getNlMsgError().error;
- if (errno != 0) {
- // TODO: consider ignoring EINVAL (-22), which appears to be
- // normal when probing a neighbor for which the kernel does
- // not already have / no longer has a link layer address.
- Log.e(TAG, "Error " + msgSnippet + ", errmsg=" + response.toString());
- }
- } else {
- String errmsg;
- if (response == null) {
- bytes.position(0);
- errmsg = "raw bytes: " + NetlinkConstants.hexify(bytes);
- } else {
- errmsg = response.toString();
- }
- Log.e(TAG, "Error " + msgSnippet + ", errmsg=" + errmsg);
- }
+ try {
+ NetlinkSocket.sendOneShotKernelMessage(OsConstants.NETLINK_ROUTE, msg);
} catch (ErrnoException e) {
- Log.e(TAG, "Error " + msgSnippet, e);
- errno = -e.errno;
- } catch (InterruptedIOException e) {
- Log.e(TAG, "Error " + msgSnippet, e);
- errno = -OsConstants.ETIMEDOUT;
- } catch (SocketException e) {
- Log.e(TAG, "Error " + msgSnippet, e);
- errno = -OsConstants.EIO;
+ Log.e(TAG, "Error " + msgSnippet + ": " + e);
+ return -e.errno;
}
- return errno;
+
+ return 0;
}
public IpReachabilityMonitor(Context context, String ifName, SharedLog log, Callback callback) {
diff --git a/services/net/java/android/net/netlink/ConntrackMessage.java b/services/net/java/android/net/netlink/ConntrackMessage.java
new file mode 100644
index 000000000000..605c46b3b4e0
--- /dev/null
+++ b/services/net/java/android/net/netlink/ConntrackMessage.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.netlink;
+
+import static android.net.netlink.NetlinkConstants.alignedLengthOf;
+import static android.net.netlink.StructNlAttr.makeNestedType;
+import static android.net.netlink.StructNlAttr.NLA_HEADERLEN;
+import static android.net.netlink.StructNlMsgHdr.NLM_F_ACK;
+import static android.net.netlink.StructNlMsgHdr.NLM_F_DUMP;
+import static android.net.netlink.StructNlMsgHdr.NLM_F_REPLACE;
+import static android.net.netlink.StructNlMsgHdr.NLM_F_REQUEST;
+import static android.net.util.NetworkConstants.IPV4_ADDR_LEN;
+import static java.nio.ByteOrder.BIG_ENDIAN;
+
+import android.system.OsConstants;
+import android.util.Log;
+import libcore.io.SizeOf;
+
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+
+/**
+ * A NetlinkMessage subclass for netlink conntrack messages.
+ *
+ * see also: &lt;linux_src&gt;/include/uapi/linux/netfilter/nfnetlink_conntrack.h
+ *
+ * @hide
+ */
+public class ConntrackMessage extends NetlinkMessage {
+ public static final int STRUCT_SIZE = StructNlMsgHdr.STRUCT_SIZE + StructNfGenMsg.STRUCT_SIZE;
+
+ public static final short NFNL_SUBSYS_CTNETLINK = 1;
+ public static final short IPCTNL_MSG_CT_NEW = 0;
+
+ // enum ctattr_type
+ public static final short CTA_TUPLE_ORIG = 1;
+ public static final short CTA_TUPLE_REPLY = 2;
+ public static final short CTA_TIMEOUT = 7;
+
+ // enum ctattr_tuple
+ public static final short CTA_TUPLE_IP = 1;
+ public static final short CTA_TUPLE_PROTO = 2;
+
+ // enum ctattr_ip
+ public static final short CTA_IP_V4_SRC = 1;
+ public static final short CTA_IP_V4_DST = 2;
+
+ // enum ctattr_l4proto
+ public static final short CTA_PROTO_NUM = 1;
+ public static final short CTA_PROTO_SRC_PORT = 2;
+ public static final short CTA_PROTO_DST_PORT = 3;
+
+ public static byte[] newIPv4TimeoutUpdateRequest(
+ int proto, Inet4Address src, int sport, Inet4Address dst, int dport, int timeoutSec) {
+ // *** STYLE WARNING ***
+ //
+ // Code below this point uses extra block indentation to highlight the
+ // packing of nested tuple netlink attribute types.
+ final StructNlAttr ctaTupleOrig = new StructNlAttr(CTA_TUPLE_ORIG,
+ new StructNlAttr(CTA_TUPLE_IP,
+ new StructNlAttr(CTA_IP_V4_SRC, src),
+ new StructNlAttr(CTA_IP_V4_DST, dst)),
+ new StructNlAttr(CTA_TUPLE_PROTO,
+ new StructNlAttr(CTA_PROTO_NUM, (byte) proto),
+ new StructNlAttr(CTA_PROTO_SRC_PORT, (short) sport, BIG_ENDIAN),
+ new StructNlAttr(CTA_PROTO_DST_PORT, (short) dport, BIG_ENDIAN)));
+
+ final StructNlAttr ctaTimeout = new StructNlAttr(CTA_TIMEOUT, timeoutSec, BIG_ENDIAN);
+
+ final int payloadLength = ctaTupleOrig.getAlignedLength() + ctaTimeout.getAlignedLength();
+ final byte[] bytes = new byte[STRUCT_SIZE + payloadLength];
+ final ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
+ byteBuffer.order(ByteOrder.nativeOrder());
+
+ final ConntrackMessage ctmsg = new ConntrackMessage();
+ ctmsg.mHeader.nlmsg_len = bytes.length;
+ ctmsg.mHeader.nlmsg_type = (NFNL_SUBSYS_CTNETLINK << 8) | IPCTNL_MSG_CT_NEW;
+ ctmsg.mHeader.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_REPLACE;
+ ctmsg.mHeader.nlmsg_seq = 1;
+ ctmsg.pack(byteBuffer);
+
+ ctaTupleOrig.pack(byteBuffer);
+ ctaTimeout.pack(byteBuffer);
+
+ return bytes;
+ }
+
+ protected StructNfGenMsg mNfGenMsg;
+
+ private ConntrackMessage() {
+ super(new StructNlMsgHdr());
+ mNfGenMsg = new StructNfGenMsg((byte) OsConstants.AF_INET);
+ }
+
+ public void pack(ByteBuffer byteBuffer) {
+ mHeader.pack(byteBuffer);
+ mNfGenMsg.pack(byteBuffer);
+ }
+}
diff --git a/services/net/java/android/net/netlink/NetlinkSocket.java b/services/net/java/android/net/netlink/NetlinkSocket.java
index 657d48c15250..a9e0cd996fbd 100644
--- a/services/net/java/android/net/netlink/NetlinkSocket.java
+++ b/services/net/java/android/net/netlink/NetlinkSocket.java
@@ -51,6 +51,47 @@ public class NetlinkSocket implements Closeable {
private long mLastRecvTimeoutMs;
private long mLastSendTimeoutMs;
+ public static void sendOneShotKernelMessage(int nlProto, byte[] msg) throws ErrnoException {
+ final String errPrefix = "Error in NetlinkSocket.sendOneShotKernelMessage";
+
+ try (NetlinkSocket nlSocket = new NetlinkSocket(nlProto)) {
+ final long IO_TIMEOUT = 300L;
+ nlSocket.connectToKernel();
+ nlSocket.sendMessage(msg, 0, msg.length, IO_TIMEOUT);
+ final ByteBuffer bytes = nlSocket.recvMessage(IO_TIMEOUT);
+ // recvMessage() guaranteed to not return null if it did not throw.
+ final NetlinkMessage response = NetlinkMessage.parse(bytes);
+ if (response != null && response instanceof NetlinkErrorMessage &&
+ (((NetlinkErrorMessage) response).getNlMsgError() != null)) {
+ final int errno = ((NetlinkErrorMessage) response).getNlMsgError().error;
+ if (errno != 0) {
+ // TODO: consider ignoring EINVAL (-22), which appears to be
+ // normal when probing a neighbor for which the kernel does
+ // not already have / no longer has a link layer address.
+ Log.e(TAG, errPrefix + ", errmsg=" + response.toString());
+ // Note: convert kernel errnos (negative) into userspace errnos (positive).
+ throw new ErrnoException(response.toString(), Math.abs(errno));
+ }
+ } else {
+ final String errmsg;
+ if (response == null) {
+ bytes.position(0);
+ errmsg = "raw bytes: " + NetlinkConstants.hexify(bytes);
+ } else {
+ errmsg = response.toString();
+ }
+ Log.e(TAG, errPrefix + ", errmsg=" + errmsg);
+ throw new ErrnoException(errmsg, OsConstants.EPROTO);
+ }
+ } catch (InterruptedIOException e) {
+ Log.e(TAG, errPrefix, e);
+ throw new ErrnoException(errPrefix, OsConstants.ETIMEDOUT, e);
+ } catch (SocketException e) {
+ Log.e(TAG, errPrefix, e);
+ throw new ErrnoException(errPrefix, OsConstants.EIO, e);
+ }
+ }
+
public NetlinkSocket(int nlProto) throws ErrnoException {
mDescriptor = Os.socket(
OsConstants.AF_NETLINK, OsConstants.SOCK_DGRAM, nlProto);
diff --git a/services/net/java/android/net/netlink/RtNetlinkNeighborMessage.java b/services/net/java/android/net/netlink/RtNetlinkNeighborMessage.java
index 02df1313c43f..e784fbb5e0dc 100644
--- a/services/net/java/android/net/netlink/RtNetlinkNeighborMessage.java
+++ b/services/net/java/android/net/netlink/RtNetlinkNeighborMessage.java
@@ -36,7 +36,7 @@ import java.nio.ByteOrder;
/**
- * A NetlinkMessage subclass for netlink error messages.
+ * A NetlinkMessage subclass for rtnetlink neighbor messages.
*
* see also: &lt;linux_src&gt;/include/uapi/linux/neighbour.h
*
diff --git a/services/net/java/android/net/netlink/StructNfGenMsg.java b/services/net/java/android/net/netlink/StructNfGenMsg.java
new file mode 100644
index 000000000000..99695e23b248
--- /dev/null
+++ b/services/net/java/android/net/netlink/StructNfGenMsg.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.netlink;
+
+import libcore.io.SizeOf;
+
+import java.nio.ByteBuffer;
+
+
+/**
+ * struct nfgenmsg
+ *
+ * see &lt;linux_src&gt;/include/uapi/linux/netfilter/nfnetlink.h
+ *
+ * @hide
+ */
+public class StructNfGenMsg {
+ public static final int STRUCT_SIZE = 2 + SizeOf.SHORT;
+
+ public static final int NFNETLINK_V0 = 0;
+
+ final public byte nfgen_family;
+ final public byte version;
+ final public short res_id; // N.B.: this is big endian in the kernel
+
+ public StructNfGenMsg(byte family) {
+ nfgen_family = family;
+ version = (byte) NFNETLINK_V0;
+ res_id = (short) 0;
+ }
+
+ public void pack(ByteBuffer byteBuffer) {
+ byteBuffer.put(nfgen_family);
+ byteBuffer.put(version);
+ byteBuffer.putShort(res_id);
+ }
+}
diff --git a/services/net/java/android/net/netlink/StructNlAttr.java b/services/net/java/android/net/netlink/StructNlAttr.java
index 597a6aa1c9eb..811bdbbe821a 100644
--- a/services/net/java/android/net/netlink/StructNlAttr.java
+++ b/services/net/java/android/net/netlink/StructNlAttr.java
@@ -34,7 +34,12 @@ import java.nio.ByteBuffer;
*/
public class StructNlAttr {
// Already aligned.
- public static final int NLA_HEADERLEN = 4;
+ public static final int NLA_HEADERLEN = 4;
+ public static final int NLA_F_NESTED = (1 << 15);
+
+ public static short makeNestedType(short type) {
+ return (short) (type | NLA_F_NESTED);
+ }
// Return a (length, type) object only, without consuming any bytes in
// |byteBuffer| and without copying or interpreting any value bytes.
@@ -46,10 +51,17 @@ public class StructNlAttr {
}
final int baseOffset = byteBuffer.position();
- final StructNlAttr struct = new StructNlAttr();
- struct.nla_len = byteBuffer.getShort();
- struct.nla_type = byteBuffer.getShort();
- struct.mByteOrder = byteBuffer.order();
+ // Assume the byte order of the buffer is the expected byte order of the value.
+ final StructNlAttr struct = new StructNlAttr(byteBuffer.order());
+ // The byte order of nla_len and nla_type is always native.
+ final ByteOrder originalOrder = byteBuffer.order();
+ byteBuffer.order(ByteOrder.nativeOrder());
+ try {
+ struct.nla_len = byteBuffer.getShort();
+ struct.nla_type = byteBuffer.getShort();
+ } finally {
+ byteBuffer.order(originalOrder);
+ }
byteBuffer.position(baseOffset);
if (struct.nla_len < NLA_HEADERLEN) {
@@ -78,13 +90,65 @@ public class StructNlAttr {
return struct;
}
- public short nla_len;
+ public short nla_len = (short) NLA_HEADERLEN;
public short nla_type;
public byte[] nla_value;
- public ByteOrder mByteOrder;
- public StructNlAttr() {
- mByteOrder = ByteOrder.nativeOrder();
+ // The byte order used to read/write the value member. Netlink length and
+ // type members are always read/written in native order.
+ private ByteOrder mByteOrder = ByteOrder.nativeOrder();
+
+ public StructNlAttr() {}
+
+ public StructNlAttr(ByteOrder byteOrder) {
+ mByteOrder = byteOrder;
+ }
+
+ public StructNlAttr(short type, byte value) {
+ nla_type = type;
+ setValue(new byte[1]);
+ nla_value[0] = value;
+ }
+
+ public StructNlAttr(short type, short value) {
+ this(type, value, ByteOrder.nativeOrder());
+ }
+
+ public StructNlAttr(short type, short value, ByteOrder order) {
+ this(order);
+ nla_type = type;
+ setValue(new byte[SizeOf.SHORT]);
+ getValueAsByteBuffer().putShort(value);
+ }
+
+ public StructNlAttr(short type, int value) {
+ this(type, value, ByteOrder.nativeOrder());
+ }
+
+ public StructNlAttr(short type, int value, ByteOrder order) {
+ this(order);
+ nla_type = type;
+ setValue(new byte[SizeOf.INT]);
+ getValueAsByteBuffer().putInt(value);
+ }
+
+ public StructNlAttr(short type, InetAddress ip) {
+ nla_type = type;
+ setValue(ip.getAddress());
+ }
+
+ public StructNlAttr(short type, StructNlAttr... nested) {
+ this();
+ nla_type = makeNestedType(type);
+
+ int payloadLength = 0;
+ for (StructNlAttr nla : nested) payloadLength += nla.getAlignedLength();
+ setValue(new byte[payloadLength]);
+
+ final ByteBuffer buf = getValueAsByteBuffer();
+ for (StructNlAttr nla : nested) {
+ nla.pack(buf);
+ }
}
public int getAlignedLength() {
@@ -117,13 +181,25 @@ public class StructNlAttr {
}
public void pack(ByteBuffer byteBuffer) {
+ final ByteOrder originalOrder = byteBuffer.order();
final int originalPosition = byteBuffer.position();
- byteBuffer.putShort(nla_len);
- byteBuffer.putShort(nla_type);
- byteBuffer.put(nla_value);
+
+ byteBuffer.order(ByteOrder.nativeOrder());
+ try {
+ byteBuffer.putShort(nla_len);
+ byteBuffer.putShort(nla_type);
+ if (nla_value != null) byteBuffer.put(nla_value);
+ } finally {
+ byteBuffer.order(originalOrder);
+ }
byteBuffer.position(originalPosition + getAlignedLength());
}
+ private void setValue(byte[] value) {
+ nla_value = value;
+ nla_len = (short) (NLA_HEADERLEN + ((nla_value != null) ? nla_value.length : 0));
+ }
+
@Override
public String toString() {
return "StructNlAttr{ "
diff --git a/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java
index 2887e3bb520f..d09d0c8d9e5c 100644
--- a/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java
@@ -585,7 +585,39 @@ public class RulesManagerServiceTest {
verifyNoPackageTrackerCallsMade();
// Set up the installer.
- configureStageUninstallExpectation(true /* success */);
+ configureStageUninstallExpectation(TimeZoneDistroInstaller.UNINSTALL_SUCCESS);
+
+ // Simulate the async execution.
+ mFakeExecutor.simulateAsyncExecutionOfLastCommand();
+
+ // Verify the expected calls were made to other components.
+ verifyStageUninstallCalled();
+ verifyPackageTrackerCalled(token, true /* success */);
+
+ // Check the callback was called.
+ callback.assertResultReceived(Callback.SUCCESS);
+ }
+
+ @Test
+ public void requestUninstall_asyncNothingInstalled() throws Exception {
+ configureCallerHasPermission();
+
+ CheckToken token = createArbitraryToken();
+ byte[] tokenBytes = token.toByteArray();
+
+ TestCallback callback = new TestCallback();
+
+ // Request the uninstall.
+ assertEquals(RulesManager.SUCCESS,
+ mRulesManagerService.requestUninstall(tokenBytes, callback));
+
+ // Assert nothing has happened yet.
+ callback.assertNoResultReceived();
+ verifyNoInstallerCallsMade();
+ verifyNoPackageTrackerCallsMade();
+
+ // Set up the installer.
+ configureStageUninstallExpectation(TimeZoneDistroInstaller.UNINSTALL_NOTHING_INSTALLED);
// Simulate the async execution.
mFakeExecutor.simulateAsyncExecutionOfLastCommand();
@@ -613,7 +645,7 @@ public class RulesManagerServiceTest {
callback.assertNoResultReceived();
// Set up the installer.
- configureStageUninstallExpectation(true /* success */);
+ configureStageUninstallExpectation(TimeZoneDistroInstaller.UNINSTALL_SUCCESS);
// Simulate the async execution.
mFakeExecutor.simulateAsyncExecutionOfLastCommand();
@@ -644,7 +676,7 @@ public class RulesManagerServiceTest {
callback.assertNoResultReceived();
// Set up the installer.
- configureStageUninstallExpectation(false /* success */);
+ configureStageUninstallExpectation(TimeZoneDistroInstaller.UNINSTALL_FAIL);
// Simulate the async execution.
mFakeExecutor.simulateAsyncExecutionOfLastCommand();
@@ -849,8 +881,8 @@ public class RulesManagerServiceTest {
.thenReturn(resultCode);
}
- private void configureStageUninstallExpectation(boolean success) throws Exception {
- doReturn(success).when(mMockTimeZoneDistroInstaller).stageUninstall();
+ private void configureStageUninstallExpectation(int resultCode) throws Exception {
+ doReturn(resultCode).when(mMockTimeZoneDistroInstaller).stageUninstall();
}
private void verifyStageInstallCalled() throws Exception {
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index d0b36c93db2f..fdb1f09bce34 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1307,10 +1307,7 @@ public class TelecomManager {
/**
* Returns whether TTY is supported on this device.
- *
- * @hide
*/
- @SystemApi
@RequiresPermission(anyOf = {
android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
android.Manifest.permission.READ_PHONE_STATE
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index fe8774fa1f62..3c841a478d68 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -199,7 +199,7 @@ public class CarrierConfigManager {
/** Display carrier settings menu if true */
public static final String KEY_CARRIER_SETTINGS_ENABLE_BOOL = "carrier_settings_enable_bool";
- /** Does not display additional call seting for IMS phone based on GSM Phone */
+ /** Does not display additional call setting for IMS phone based on GSM Phone */
public static final String KEY_ADDITIONAL_CALL_SETTING_BOOL = "additional_call_setting_bool";
/** Show APN Settings for some CDMA carriers */
@@ -1500,6 +1500,14 @@ public class CarrierConfigManager {
public static final String IMSI_KEY_EXPIRATION_DAYS_TIME_INT =
"imsi_key_expiration_days_time_int";
+ /**
+ * Flag specifying whether IMS registration state menu is shown in Status Info setting,
+ * default to false.
+ * @hide
+ */
+ public static final String KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL =
+ "show_ims_registration_status_bool";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -1697,7 +1705,6 @@ public class CarrierConfigManager {
sDefaults.putStringArray(KEY_CARRIER_APP_WAKE_SIGNAL_CONFIG_STRING_ARRAY,
new String[]{
"com.android.carrierdefaultapp/.CarrierDefaultBroadcastReceiver:" +
- "com.android.internal.telephony.CARRIER_SIGNAL_REDIRECTED," +
"com.android.internal.telephony.CARRIER_SIGNAL_RESET"
});
sDefaults.putStringArray(KEY_CARRIER_APP_NO_WAKE_SIGNAL_CONFIG_STRING_ARRAY, null);
@@ -1753,6 +1760,7 @@ public class CarrierConfigManager {
sDefaults.putStringArray(KEY_ROAMING_OPERATOR_STRING_ARRAY, null);
sDefaults.putInt(IMSI_KEY_EXPIRATION_DAYS_TIME_INT, IMSI_ENCRYPTION_DAYS_TIME_DISABLED);
sDefaults.putString(IMSI_KEY_DOWNLOAD_URL_STRING, null);
+ sDefaults.putBoolean(KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL, false);
}
/**
diff --git a/telephony/java/android/telephony/MbmsDownloadManager.java b/telephony/java/android/telephony/MbmsDownloadManager.java
deleted file mode 100644
index 1e8cf185d4e4..000000000000
--- a/telephony/java/android/telephony/MbmsDownloadManager.java
+++ /dev/null
@@ -1,586 +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.telephony;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SdkConstant;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.content.SharedPreferences;
-import android.net.Uri;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.telephony.mbms.DownloadProgressListener;
-import android.telephony.mbms.FileInfo;
-import android.telephony.mbms.DownloadRequest;
-import android.telephony.mbms.MbmsDownloadManagerCallback;
-import android.telephony.mbms.MbmsDownloadReceiver;
-import android.telephony.mbms.MbmsException;
-import android.telephony.mbms.MbmsTempFileProvider;
-import android.telephony.mbms.MbmsUtils;
-import android.telephony.mbms.vendor.IMbmsDownloadService;
-import android.util.Log;
-
-import java.io.File;
-import java.io.IOException;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.List;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicReference;
-
-import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
-
-/** @hide */
-public class MbmsDownloadManager {
- private static final String LOG_TAG = MbmsDownloadManager.class.getSimpleName();
-
- /** @hide */
- // TODO: systemapi
- @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
- public static final String MBMS_DOWNLOAD_SERVICE_ACTION =
- "android.telephony.action.EmbmsDownload";
-
- /**
- * Integer extra indicating the result code of the download. One of
- * {@link #RESULT_SUCCESSFUL}, {@link #RESULT_EXPIRED}, or {@link #RESULT_CANCELLED}.
- */
- public static final String EXTRA_RESULT = "android.telephony.mbms.extra.RESULT";
-
- /**
- * Extra containing the {@link android.telephony.mbms.FileInfo} for which the download result
- * is for. Must not be null.
- */
- public static final String EXTRA_FILE_INFO = "android.telephony.mbms.extra.FILE_INFO";
-
- /**
- * Extra containing a single {@link Uri} indicating the location of the successfully
- * downloaded file. Set on the intent provided via
- * {@link android.telephony.mbms.DownloadRequest.Builder#setAppIntent(Intent)}.
- * Will always be set to a non-null value if {@link #EXTRA_RESULT} is set to
- * {@link #RESULT_SUCCESSFUL}.
- */
- public static final String EXTRA_COMPLETED_FILE_URI =
- "android.telephony.mbms.extra.COMPLETED_FILE_URI";
-
- public static final int RESULT_SUCCESSFUL = 1;
- public static final int RESULT_CANCELLED = 2;
- public static final int RESULT_EXPIRED = 3;
- public static final int RESULT_IO_ERROR = 4;
- // TODO - more results!
-
- /** @hide */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({STATUS_UNKNOWN, STATUS_ACTIVELY_DOWNLOADING, STATUS_PENDING_DOWNLOAD,
- STATUS_PENDING_REPAIR, STATUS_PENDING_DOWNLOAD_WINDOW})
- public @interface DownloadStatus {}
-
- public static final int STATUS_UNKNOWN = 0;
- public static final int STATUS_ACTIVELY_DOWNLOADING = 1;
- public static final int STATUS_PENDING_DOWNLOAD = 2;
- public static final int STATUS_PENDING_REPAIR = 3;
- public static final int STATUS_PENDING_DOWNLOAD_WINDOW = 4;
-
- private static AtomicBoolean sIsInitialized = new AtomicBoolean(false);
-
- private final Context mContext;
- private int mSubscriptionId = INVALID_SUBSCRIPTION_ID;
- private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
- @Override
- public void binderDied() {
- sendErrorToApp(MbmsException.ERROR_MIDDLEWARE_LOST, "Received death notification");
- }
- };
-
- private AtomicReference<IMbmsDownloadService> mService = new AtomicReference<>(null);
- private final MbmsDownloadManagerCallback mCallback;
-
- private MbmsDownloadManager(Context context, MbmsDownloadManagerCallback callback, int subId) {
- mContext = context;
- mCallback = callback;
- mSubscriptionId = subId;
- }
-
- /**
- * Create a new MbmsDownloadManager using the system default data subscription ID.
- * See {@link #create(Context, MbmsDownloadManagerCallback, int)}
- *
- * @hide
- */
- public static MbmsDownloadManager create(Context context,
- MbmsDownloadManagerCallback listener)
- throws MbmsException {
- return create(context, listener, SubscriptionManager.getDefaultSubscriptionId());
- }
-
- /**
- * Create a new MbmsDownloadManager using the given subscription ID.
- *
- * Note that this call will bind a remote service and that may take a bit. The instance of
- * {@link MbmsDownloadManager} that is returned will not be ready for use until
- * {@link MbmsDownloadManagerCallback#middlewareReady()} is called on the provided callback.
- * If you attempt to use the manager before it is ready, a {@link MbmsException} will be thrown.
- *
- * This also may throw an {@link IllegalArgumentException} or an {@link IllegalStateException}.
- *
- * You may only have one instance of {@link MbmsDownloadManager} per UID. If you call this
- * method while there is an active instance of {@link MbmsDownloadManager} in your process
- * (in other words, one that has not had {@link #dispose()} called on it), this method will
- * throw an {@link MbmsException}. If you call this method in a different process
- * running under the same UID, an error will be indicated via
- * {@link MbmsDownloadManagerCallback#error(int, String)}.
- *
- * Note that initialization may fail asynchronously. If you wish to try again after you
- * receive such an asynchronous error, you must call dispose() on the instance of
- * {@link MbmsDownloadManager} that you received before calling this method again.
- *
- * @param context The instance of {@link Context} to use
- * @param listener A callback to get asynchronous error messages and file service updates.
- * @param subscriptionId The data subscription ID to use
- * @hide
- */
- public static MbmsDownloadManager create(Context context,
- MbmsDownloadManagerCallback listener, int subscriptionId)
- throws MbmsException {
- if (!sIsInitialized.compareAndSet(false, true)) {
- throw new MbmsException(MbmsException.InitializationErrors.ERROR_DUPLICATE_INITIALIZE);
- }
- MbmsDownloadManager mdm = new MbmsDownloadManager(context, listener, subscriptionId);
- try {
- mdm.bindAndInitialize();
- } catch (MbmsException e) {
- sIsInitialized.set(false);
- throw e;
- }
- return mdm;
- }
-
- private void bindAndInitialize() throws MbmsException {
- MbmsUtils.startBinding(mContext, MBMS_DOWNLOAD_SERVICE_ACTION,
- new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- IMbmsDownloadService downloadService =
- IMbmsDownloadService.Stub.asInterface(service);
- int result;
- try {
- result = downloadService.initialize(mSubscriptionId, mCallback);
- } catch (RemoteException e) {
- Log.e(LOG_TAG, "Service died before initialization");
- sIsInitialized.set(false);
- return;
- } catch (RuntimeException e) {
- Log.e(LOG_TAG, "Runtime exception during initialization");
- sendErrorToApp(
- MbmsException.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
- e.toString());
- sIsInitialized.set(false);
- return;
- }
- if (result != MbmsException.SUCCESS) {
- sendErrorToApp(result, "Error returned during initialization");
- sIsInitialized.set(false);
- return;
- }
- try {
- downloadService.asBinder().linkToDeath(mDeathRecipient, 0);
- } catch (RemoteException e) {
- sendErrorToApp(MbmsException.ERROR_MIDDLEWARE_LOST,
- "Middleware lost during initialization");
- sIsInitialized.set(false);
- return;
- }
- mService.set(downloadService);
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- sIsInitialized.set(false);
- mService.set(null);
- }
- });
- }
-
- /**
- * An inspection API to retrieve the list of available
- * {@link android.telephony.mbms.FileServiceInfo}s currently being advertised.
- * The results are returned asynchronously via a call to
- * {@link MbmsDownloadManagerCallback#fileServicesUpdated(List)}
- *
- * The serviceClasses argument lets the app filter on types of programming and is opaque data
- * negotiated beforehand between the app and the carrier.
- *
- * This may throw an {@link MbmsException} containing one of the following errors:
- * {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}
- * {@link MbmsException#ERROR_MIDDLEWARE_LOST}
- *
- * Asynchronous error codes via the {@link MbmsDownloadManagerCallback#error(int, String)}
- * callback can include any of the errors except:
- * {@link MbmsException.StreamingErrors#ERROR_UNABLE_TO_START_SERVICE}
- *
- * @param classList A list of service classes which the app wishes to receive
- * {@link MbmsDownloadManagerCallback#fileServicesUpdated(List)} callbacks
- * about. Subsequent calls to this method will replace this list of service
- * classes (i.e. the middleware will no longer send updates for services
- * matching classes only in the old list).
- */
- public void getFileServices(List<String> classList) throws MbmsException {
- IMbmsDownloadService downloadService = mService.get();
- if (downloadService == null) {
- throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
- }
- try {
- int returnCode = downloadService.getFileServices(mSubscriptionId, classList);
- if (returnCode != MbmsException.SUCCESS) {
- throw new MbmsException(returnCode);
- }
- } catch (RemoteException e) {
- Log.w(LOG_TAG, "Remote process died");
- mService.set(null);
- throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
- }
- }
-
- /**
- * Sets the temp file root for downloads.
- * All temp files created for the middleware to write to will be contained in the specified
- * directory. Applications that wish to specify a location only need to call this method once
- * as long their data is persisted in storage -- the argument will be stored both in a
- * local instance of {@link android.content.SharedPreferences} and by the middleware.
- *
- * If this method is not called at least once before calling
- * {@link #download(DownloadRequest, DownloadProgressListener)}, the framework
- * will default to a directory formed by the concatenation of the app's files directory and
- * {@link android.telephony.mbms.MbmsTempFileProvider#DEFAULT_TOP_LEVEL_TEMP_DIRECTORY}.
- *
- * Before calling this method, the app must cancel all of its pending
- * {@link DownloadRequest}s via {@link #cancelDownload(DownloadRequest)}. If this is not done,
- * an {@link MbmsException} will be thrown with code
- * {@link MbmsException.DownloadErrors#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT} unless the
- * provided directory is the same as what has been previously configured.
- *
- * The {@link File} supplied as a root temp file directory must already exist. If not, an
- * {@link IllegalArgumentException} will be thrown.
- * @param tempFileRootDirectory A directory to place temp files in.
- */
- public void setTempFileRootDirectory(@NonNull File tempFileRootDirectory)
- throws MbmsException {
- IMbmsDownloadService downloadService = mService.get();
- if (downloadService == null) {
- throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
- }
- if (!tempFileRootDirectory.exists()) {
- throw new IllegalArgumentException("Provided directory does not exist");
- }
- if (!tempFileRootDirectory.isDirectory()) {
- throw new IllegalArgumentException("Provided File is not a directory");
- }
- String filePath;
- try {
- filePath = tempFileRootDirectory.getCanonicalPath();
- } catch (IOException e) {
- throw new IllegalArgumentException("Unable to canonicalize the provided path: " + e);
- }
-
- try {
- int result = downloadService.setTempFileRootDirectory(mSubscriptionId, filePath);
- if (result != MbmsException.SUCCESS) {
- throw new MbmsException(result);
- }
- } catch (RemoteException e) {
- mService.set(null);
- throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
- }
-
- SharedPreferences prefs = mContext.getSharedPreferences(
- MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_FILE_NAME, 0);
- prefs.edit().putString(MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_NAME, filePath).apply();
- }
-
- /**
- * Retrieves the currently configured temp file root directory. Returns the file that was
- * configured via {@link #setTempFileRootDirectory(File)} or the default directory
- * {@link #download(DownloadRequest, DownloadProgressListener)} was called without ever setting
- * the temp file root. If neither method has been called since the last time the app's shared
- * preferences were reset, returns null.
- *
- * @return A {@link File} pointing to the configured temp file directory, or null if not yet
- * configured.
- */
- public @Nullable File getTempFileRootDirectory() {
- SharedPreferences prefs = mContext.getSharedPreferences(
- MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_FILE_NAME, 0);
- String path = prefs.getString(MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_NAME, null);
- if (path != null) {
- return new File(path);
- }
- return null;
- }
-
- /**
- * Requests a download of a file that is available via multicast.
- *
- * downloadListener is an optional callback object which can be used to get progress reports
- * of a currently occuring download. Note this can only run while the calling app
- * is running, so future downloads will simply result in resultIntents being sent
- * for completed or errored-out downloads. A NULL indicates no callbacks are needed.
- *
- * May throw an {@link IllegalArgumentException}
- *
- * If {@link #setTempFileRootDirectory(File)} has not called after the app has been installed,
- * this method will create a directory at the default location defined at
- * {@link MbmsTempFileProvider#DEFAULT_TOP_LEVEL_TEMP_DIRECTORY} and store that as the temp
- * file root directory.
- *
- * Asynchronous errors through the listener include any of the errors
- *
- * @param request The request that specifies what should be downloaded
- * @param progressListener Optional listener that will be provided progress updates
- * if the app is running.
- */
- public void download(DownloadRequest request, DownloadProgressListener progressListener)
- throws MbmsException {
- IMbmsDownloadService downloadService = mService.get();
- if (downloadService == null) {
- throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
- }
-
- // Check to see whether the app's set a temp root dir yet, and set it if not.
- SharedPreferences prefs = mContext.getSharedPreferences(
- MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_FILE_NAME, 0);
- if (prefs.getString(MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_NAME, null) == null) {
- File tempRootDirectory = new File(mContext.getFilesDir(),
- MbmsTempFileProvider.DEFAULT_TOP_LEVEL_TEMP_DIRECTORY);
- tempRootDirectory.mkdirs();
- setTempFileRootDirectory(tempRootDirectory);
- }
-
- checkValidDownloadDestination(request);
- writeDownloadRequestToken(request);
- try {
- downloadService.download(request, progressListener);
- } catch (RemoteException e) {
- mService.set(null);
- throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
- }
- }
-
- /**
- * Returns a list of pending {@link DownloadRequest}s that originated from this application.
- * A pending request is one that was issued via
- * {@link #download(DownloadRequest, DownloadProgressListener)} but not cancelled through
- * {@link #cancelDownload(DownloadRequest)}.
- * @return A list, possibly empty, of {@link DownloadRequest}s
- */
- public @NonNull List<DownloadRequest> listPendingDownloads() throws MbmsException {
- IMbmsDownloadService downloadService = mService.get();
- if (downloadService == null) {
- throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
- }
-
- try {
- return downloadService.listPendingDownloads(mSubscriptionId);
- } catch (RemoteException e) {
- mService.set(null);
- throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
- }
- }
-
- /**
- * Attempts to cancel the specified {@link DownloadRequest}.
- *
- * If the middleware is not aware of the specified download request, an MbmsException will be
- * thrown with error code {@link MbmsException.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}.
- *
- * If this method returns without throwing an exception, you may assume that cancellation
- * was successful.
- * @param downloadRequest The download request that you wish to cancel.
- */
- public void cancelDownload(DownloadRequest downloadRequest) throws MbmsException {
- IMbmsDownloadService downloadService = mService.get();
- if (downloadService == null) {
- throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
- }
-
- try {
- int result = downloadService.cancelDownload(downloadRequest);
- if (result != MbmsException.SUCCESS) {
- throw new MbmsException(result);
- }
- } catch (RemoteException e) {
- mService.set(null);
- throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
- }
- deleteDownloadRequestToken(downloadRequest);
- }
-
- /**
- * Gets information about the status of a file pending download.
- *
- * If the middleware has not yet been properly initialized or if it has no records of the
- * file indicated by {@code fileInfo} being associated with {@code downloadRequest},
- * {@link #STATUS_UNKNOWN} will be returned.
- *
- * @param downloadRequest The download request to query.
- * @param fileInfo The particular file within the request to get information on.
- * @return The status of the download.
- */
- @DownloadStatus
- public int getDownloadStatus(DownloadRequest downloadRequest, FileInfo fileInfo)
- throws MbmsException {
- IMbmsDownloadService downloadService = mService.get();
- if (downloadService == null) {
- throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
- }
-
- try {
- return downloadService.getDownloadStatus(downloadRequest, fileInfo);
- } catch (RemoteException e) {
- mService.set(null);
- throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
- }
- }
-
- /**
- * Resets the middleware's knowledge of previously-downloaded files in this download request.
- *
- * Normally, the middleware keeps track of the hashes of downloaded files and won't re-download
- * files whose server-reported hash matches one of the already-downloaded files. This means
- * that if the file is accidentally deleted by the user or by the app, the middleware will
- * not try to download it again.
- * This method will reset the middleware's cache of hashes for the provided
- * {@link DownloadRequest}, so that previously downloaded content will be downloaded again
- * when available.
- * This will not interrupt in-progress downloads.
- *
- * If the middleware is not aware of the specified download request, an MbmsException will be
- * thrown with error code {@link MbmsException.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}.
- *
- * May throw a {@link MbmsException} with error code
- * @param downloadRequest The request to re-download files for.
- */
- public void resetDownloadKnowledge(DownloadRequest downloadRequest) throws MbmsException {
- IMbmsDownloadService downloadService = mService.get();
- if (downloadService == null) {
- throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
- }
-
- try {
- int result = downloadService.resetDownloadKnowledge(downloadRequest);
- if (result != MbmsException.SUCCESS) {
- throw new MbmsException(result);
- }
- } catch (RemoteException e) {
- mService.set(null);
- throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
- }
- }
-
- public void dispose() {
- try {
- IMbmsDownloadService downloadService = mService.get();
- if (downloadService == null) {
- Log.i(LOG_TAG, "Service already dead");
- return;
- }
- downloadService.dispose(mSubscriptionId);
- } catch (RemoteException e) {
- // Ignore
- Log.i(LOG_TAG, "Remote exception while disposing of service");
- } finally {
- mService.set(null);
- sIsInitialized.set(false);
- }
- }
-
- private void writeDownloadRequestToken(DownloadRequest request) {
- File token = getDownloadRequestTokenPath(request);
- if (!token.getParentFile().exists()) {
- token.getParentFile().mkdirs();
- }
- if (token.exists()) {
- Log.w(LOG_TAG, "Download token " + token.getName() + " already exists");
- return;
- }
- try {
- if (!token.createNewFile()) {
- throw new RuntimeException("Failed to create download token for request "
- + request);
- }
- } catch (IOException e) {
- throw new RuntimeException("Failed to create download token for request " + request
- + " due to IOException " + e);
- }
- }
-
- private void deleteDownloadRequestToken(DownloadRequest request) {
- File token = getDownloadRequestTokenPath(request);
- if (!token.isFile()) {
- Log.w(LOG_TAG, "Attempting to delete non-existent download token at " + token);
- return;
- }
- if (!token.delete()) {
- Log.w(LOG_TAG, "Couldn't delete download token at " + token);
- }
- }
-
- private File getDownloadRequestTokenPath(DownloadRequest request) {
- File tempFileLocation = MbmsUtils.getEmbmsTempFileDirForService(mContext,
- request.getFileServiceId());
- String downloadTokenFileName = request.getHash()
- + MbmsDownloadReceiver.DOWNLOAD_TOKEN_SUFFIX;
- return new File(tempFileLocation, downloadTokenFileName);
- }
-
- /**
- * Verifies the following:
- * If a request is multi-part,
- * 1. Destination Uri must exist and be a directory
- * 2. Directory specified must contain no files.
- * Otherwise
- * 1. The file specified by the destination Uri must not exist.
- */
- private void checkValidDownloadDestination(DownloadRequest request) {
- File toFile = new File(request.getDestinationUri().getSchemeSpecificPart());
- if (request.isMultipartDownload()) {
- if (!toFile.isDirectory()) {
- throw new IllegalArgumentException("Multipart download must specify valid " +
- "destination directory.");
- }
- if (toFile.listFiles().length > 0) {
- throw new IllegalArgumentException("Destination directory must be clear of all " +
- "files.");
- }
- } else {
- if (toFile.exists()) {
- throw new IllegalArgumentException("Destination file must not exist.");
- }
- }
- }
-
- private void sendErrorToApp(int errorCode, String message) {
- try {
- mCallback.error(errorCode, message);
- } catch (RemoteException e) {
- // Ignore, should not happen locally.
- }
- }
-}
diff --git a/telephony/java/android/telephony/MbmsDownloadSession.java b/telephony/java/android/telephony/MbmsDownloadSession.java
new file mode 100644
index 000000000000..ebac0419785c
--- /dev/null
+++ b/telephony/java/android/telephony/MbmsDownloadSession.java
@@ -0,0 +1,773 @@
+/*
+ * 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.telephony;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.SharedPreferences;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.telephony.mbms.DownloadStateCallback;
+import android.telephony.mbms.FileInfo;
+import android.telephony.mbms.DownloadRequest;
+import android.telephony.mbms.InternalDownloadSessionCallback;
+import android.telephony.mbms.InternalDownloadStateCallback;
+import android.telephony.mbms.MbmsDownloadSessionCallback;
+import android.telephony.mbms.MbmsDownloadReceiver;
+import android.telephony.mbms.MbmsErrors;
+import android.telephony.mbms.MbmsTempFileProvider;
+import android.telephony.mbms.MbmsUtils;
+import android.telephony.mbms.vendor.IMbmsDownloadService;
+import android.util.Log;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+/**
+ * This class provides functionality for file download over MBMS.
+ */
+public class MbmsDownloadSession implements AutoCloseable {
+ private static final String LOG_TAG = MbmsDownloadSession.class.getSimpleName();
+
+ /**
+ * Service action which must be handled by the middleware implementing the MBMS file download
+ * interface.
+ * @hide
+ */
+ @SystemApi
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+ public static final String MBMS_DOWNLOAD_SERVICE_ACTION =
+ "android.telephony.action.EmbmsDownload";
+
+ /**
+ * Integer extra that Android will attach to the intent supplied via
+ * {@link android.telephony.mbms.DownloadRequest.Builder#setAppIntent(Intent)}
+ * Indicates the result code of the download. One of
+ * {@link #RESULT_SUCCESSFUL}, {@link #RESULT_EXPIRED}, {@link #RESULT_CANCELLED}, or
+ * {@link #RESULT_IO_ERROR}.
+ *
+ * This extra may also be used by the middleware when it is sending intents to the app.
+ */
+ public static final String EXTRA_MBMS_DOWNLOAD_RESULT =
+ "android.telephony.extra.MBMS_DOWNLOAD_RESULT";
+
+ /**
+ * {@link FileInfo} extra that Android will attach to the intent supplied via
+ * {@link android.telephony.mbms.DownloadRequest.Builder#setAppIntent(Intent)}
+ * Indicates the file for which the download result is for. Never null.
+ *
+ * This extra may also be used by the middleware when it is sending intents to the app.
+ */
+ public static final String EXTRA_MBMS_FILE_INFO = "android.telephony.extra.MBMS_FILE_INFO";
+
+ /**
+ * {@link Uri} extra that Android will attach to the intent supplied via
+ * {@link android.telephony.mbms.DownloadRequest.Builder#setAppIntent(Intent)}
+ * Indicates the location of the successfully downloaded file within the temp file root set
+ * via {@link #setTempFileRootDirectory(File)}.
+ * While you may use this file in-place, it is highly encouraged that you move
+ * this file to a different location after receiving the download completion intent, as this
+ * file resides within the temp file directory.
+ *
+ * Will always be set to a non-null value if
+ * {@link #EXTRA_MBMS_DOWNLOAD_RESULT} is set to {@link #RESULT_SUCCESSFUL}.
+ */
+ public static final String EXTRA_MBMS_COMPLETED_FILE_URI =
+ "android.telephony.extra.MBMS_COMPLETED_FILE_URI";
+
+ /**
+ * Extra containing the {@link DownloadRequest} for which the download result or file
+ * descriptor request is for. Must not be null.
+ */
+ public static final String EXTRA_MBMS_DOWNLOAD_REQUEST =
+ "android.telephony.extra.MBMS_DOWNLOAD_REQUEST";
+
+ /**
+ * The default directory name for all MBMS temp files. If you call
+ * {@link #download(DownloadRequest)} without first calling
+ * {@link #setTempFileRootDirectory(File)}, this directory will be created for you under the
+ * path returned by {@link Context#getFilesDir()}.
+ */
+ public static final String DEFAULT_TOP_LEVEL_TEMP_DIRECTORY = "androidMbmsTempFileRoot";
+
+ /**
+ * Indicates that the download was successful.
+ */
+ public static final int RESULT_SUCCESSFUL = 1;
+
+ /**
+ * Indicates that the download was cancelled via {@link #cancelDownload(DownloadRequest)}.
+ */
+ public static final int RESULT_CANCELLED = 2;
+
+ /**
+ * Indicates that the download will not be completed due to the expiration of its download
+ * window on the carrier's network.
+ */
+ public static final int RESULT_EXPIRED = 3;
+
+ /**
+ * Indicates that the download will not be completed due to an I/O error incurred while
+ * writing to temp files. This commonly indicates that the device is out of storage space,
+ * but may indicate other conditions as well (such as an SD card being removed).
+ */
+ public static final int RESULT_IO_ERROR = 4;
+ // TODO - more results!
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({STATUS_UNKNOWN, STATUS_ACTIVELY_DOWNLOADING, STATUS_PENDING_DOWNLOAD,
+ STATUS_PENDING_REPAIR, STATUS_PENDING_DOWNLOAD_WINDOW})
+ public @interface DownloadStatus {}
+
+ /**
+ * Indicates that the middleware has no information on the file.
+ */
+ public static final int STATUS_UNKNOWN = 0;
+
+ /**
+ * Indicates that the file is actively downloading.
+ */
+ public static final int STATUS_ACTIVELY_DOWNLOADING = 1;
+
+ /**
+ * TODO: I don't know...
+ */
+ public static final int STATUS_PENDING_DOWNLOAD = 2;
+
+ /**
+ * Indicates that the file is being repaired after the download being interrupted.
+ */
+ public static final int STATUS_PENDING_REPAIR = 3;
+
+ /**
+ * Indicates that the file is waiting to download because its download window has not yet
+ * started.
+ */
+ public static final int STATUS_PENDING_DOWNLOAD_WINDOW = 4;
+
+ private static AtomicBoolean sIsInitialized = new AtomicBoolean(false);
+
+ private final Context mContext;
+ private int mSubscriptionId = INVALID_SUBSCRIPTION_ID;
+ private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, "Received death notification");
+ }
+ };
+
+ private AtomicReference<IMbmsDownloadService> mService = new AtomicReference<>(null);
+ private final InternalDownloadSessionCallback mInternalCallback;
+ private final Map<DownloadStateCallback, InternalDownloadStateCallback>
+ mInternalDownloadCallbacks = new HashMap<>();
+
+ private MbmsDownloadSession(Context context, MbmsDownloadSessionCallback callback,
+ int subscriptionId, Handler handler) {
+ mContext = context;
+ mSubscriptionId = subscriptionId;
+ if (handler == null) {
+ handler = new Handler(Looper.getMainLooper());
+ }
+ mInternalCallback = new InternalDownloadSessionCallback(callback, handler);
+ }
+
+ /**
+ * Create a new {@link MbmsDownloadSession} using the system default data subscription ID.
+ * See {@link #create(Context, MbmsDownloadSessionCallback, int, Handler)}
+ */
+ public static MbmsDownloadSession create(@NonNull Context context,
+ @NonNull MbmsDownloadSessionCallback callback, @NonNull Handler handler) {
+ return create(context, callback, SubscriptionManager.getDefaultSubscriptionId(), handler);
+ }
+
+ /**
+ * Create a new MbmsDownloadManager using the given subscription ID.
+ *
+ * Note that this call will bind a remote service and that may take a bit. The instance of
+ * {@link MbmsDownloadSession} that is returned will not be ready for use until
+ * {@link MbmsDownloadSessionCallback#onMiddlewareReady()} is called on the provided callback.
+ * If you attempt to use the instance before it is ready, an {@link IllegalStateException}
+ * will be thrown or an error will be delivered through
+ * {@link MbmsDownloadSessionCallback#onError(int, String)}.
+ *
+ * This also may throw an {@link IllegalArgumentException}.
+ *
+ * You may only have one instance of {@link MbmsDownloadSession} per UID. If you call this
+ * method while there is an active instance of {@link MbmsDownloadSession} in your process
+ * (in other words, one that has not had {@link #close()} called on it), this method will
+ * throw an {@link IllegalStateException}. If you call this method in a different process
+ * running under the same UID, an error will be indicated via
+ * {@link MbmsDownloadSessionCallback#onError(int, String)}.
+ *
+ * Note that initialization may fail asynchronously. If you wish to try again after you
+ * receive such an asynchronous error, you must call {@link #close()} on the instance of
+ * {@link MbmsDownloadSession} that you received before calling this method again.
+ *
+ * @param context The instance of {@link Context} to use
+ * @param callback A callback to get asynchronous error messages and file service updates.
+ * @param subscriptionId The data subscription ID to use
+ * @param handler The {@link Handler} on which callbacks should be enqueued.
+ * @return A new instance of {@link MbmsDownloadSession}, or null if an error occurred during
+ * setup.
+ */
+ public static @Nullable MbmsDownloadSession create(@NonNull Context context,
+ final @NonNull MbmsDownloadSessionCallback callback,
+ int subscriptionId, @NonNull Handler handler) {
+ if (!sIsInitialized.compareAndSet(false, true)) {
+ throw new IllegalStateException("Cannot have two active instances");
+ }
+ MbmsDownloadSession session =
+ new MbmsDownloadSession(context, callback, subscriptionId, handler);
+ final int result = session.bindAndInitialize();
+ if (result != MbmsErrors.SUCCESS) {
+ sIsInitialized.set(false);
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onError(result, null);
+ }
+ });
+ return null;
+ }
+ return session;
+ }
+
+ private int bindAndInitialize() {
+ return MbmsUtils.startBinding(mContext, MBMS_DOWNLOAD_SERVICE_ACTION,
+ new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ IMbmsDownloadService downloadService =
+ IMbmsDownloadService.Stub.asInterface(service);
+ int result;
+ try {
+ result = downloadService.initialize(mSubscriptionId, mInternalCallback);
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Service died before initialization");
+ sIsInitialized.set(false);
+ return;
+ } catch (RuntimeException e) {
+ Log.e(LOG_TAG, "Runtime exception during initialization");
+ sendErrorToApp(
+ MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+ e.toString());
+ sIsInitialized.set(false);
+ return;
+ }
+ if (result != MbmsErrors.SUCCESS) {
+ sendErrorToApp(result, "Error returned during initialization");
+ sIsInitialized.set(false);
+ return;
+ }
+ try {
+ downloadService.asBinder().linkToDeath(mDeathRecipient, 0);
+ } catch (RemoteException e) {
+ sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST,
+ "Middleware lost during initialization");
+ sIsInitialized.set(false);
+ return;
+ }
+ mService.set(downloadService);
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ sIsInitialized.set(false);
+ mService.set(null);
+ }
+ });
+ }
+
+ /**
+ * An inspection API to retrieve the list of available
+ * {@link android.telephony.mbms.FileServiceInfo}s currently being advertised.
+ * The results are returned asynchronously via a call to
+ * {@link MbmsDownloadSessionCallback#onFileServicesUpdated(List)}
+ *
+ * Asynchronous error codes via the {@link MbmsDownloadSessionCallback#onError(int, String)}
+ * callback may include any of the errors that are not specific to the streaming use-case.
+ *
+ * May throw an {@link IllegalStateException} or {@link IllegalArgumentException}.
+ *
+ * @param classList A list of service classes which the app wishes to receive
+ * {@link MbmsDownloadSessionCallback#onFileServicesUpdated(List)} callbacks
+ * about. Subsequent calls to this method will replace this list of service
+ * classes (i.e. the middleware will no longer send updates for services
+ * matching classes only in the old list).
+ * Values in this list should be negotiated with the wireless carrier prior
+ * to using this API.
+ */
+ public void requestUpdateFileServices(@NonNull List<String> classList) {
+ IMbmsDownloadService downloadService = mService.get();
+ if (downloadService == null) {
+ throw new IllegalStateException("Middleware not yet bound");
+ }
+ try {
+ int returnCode = downloadService.requestUpdateFileServices(mSubscriptionId, classList);
+ if (returnCode != MbmsErrors.SUCCESS) {
+ sendErrorToApp(returnCode, null);
+ }
+ } catch (RemoteException e) {
+ Log.w(LOG_TAG, "Remote process died");
+ mService.set(null);
+ sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+ }
+ }
+
+ /**
+ * Sets the temp file root for downloads.
+ * All temp files created for the middleware to write to will be contained in the specified
+ * directory. Applications that wish to specify a location only need to call this method once
+ * as long their data is persisted in storage -- the argument will be stored both in a
+ * local instance of {@link android.content.SharedPreferences} and by the middleware.
+ *
+ * If this method is not called at least once before calling
+ * {@link #download(DownloadRequest)}, the framework
+ * will default to a directory formed by the concatenation of the app's files directory and
+ * {@link MbmsDownloadSession#DEFAULT_TOP_LEVEL_TEMP_DIRECTORY}.
+ *
+ * Before calling this method, the app must cancel all of its pending
+ * {@link DownloadRequest}s via {@link #cancelDownload(DownloadRequest)}. If this is not done,
+ * you will receive an asynchronous error with code
+ * {@link MbmsErrors.DownloadErrors#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT} unless the
+ * provided directory is the same as what has been previously configured.
+ *
+ * The {@link File} supplied as a root temp file directory must already exist. If not, an
+ * {@link IllegalArgumentException} will be thrown. In addition, as an additional sanity
+ * check, an {@link IllegalArgumentException} will be thrown if you attempt to set the temp
+ * file root directory to one of your data roots (the value of {@link Context#getDataDir()},
+ * {@link Context#getFilesDir()}, or {@link Context#getCacheDir()}).
+ * @param tempFileRootDirectory A directory to place temp files in.
+ */
+ public void setTempFileRootDirectory(@NonNull File tempFileRootDirectory) {
+ IMbmsDownloadService downloadService = mService.get();
+ if (downloadService == null) {
+ throw new IllegalStateException("Middleware not yet bound");
+ }
+ try {
+ validateTempFileRootSanity(tempFileRootDirectory);
+ } catch (IOException e) {
+ throw new IllegalStateException("Got IOException checking directory sanity");
+ }
+ String filePath;
+ try {
+ filePath = tempFileRootDirectory.getCanonicalPath();
+ } catch (IOException e) {
+ throw new IllegalArgumentException("Unable to canonicalize the provided path: " + e);
+ }
+
+ try {
+ int result = downloadService.setTempFileRootDirectory(mSubscriptionId, filePath);
+ if (result != MbmsErrors.SUCCESS) {
+ sendErrorToApp(result, null);
+ }
+ } catch (RemoteException e) {
+ mService.set(null);
+ sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+ return;
+ }
+
+ SharedPreferences prefs = mContext.getSharedPreferences(
+ MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_FILE_NAME, 0);
+ prefs.edit().putString(MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_NAME, filePath).apply();
+ }
+
+ private void validateTempFileRootSanity(File tempFileRootDirectory) throws IOException {
+ if (!tempFileRootDirectory.exists()) {
+ throw new IllegalArgumentException("Provided directory does not exist");
+ }
+ if (!tempFileRootDirectory.isDirectory()) {
+ throw new IllegalArgumentException("Provided File is not a directory");
+ }
+ String canonicalTempFilePath = tempFileRootDirectory.getCanonicalPath();
+ if (mContext.getDataDir().getCanonicalPath().equals(canonicalTempFilePath)) {
+ throw new IllegalArgumentException("Temp file root cannot be your data dir");
+ }
+ if (mContext.getCacheDir().getCanonicalPath().equals(canonicalTempFilePath)) {
+ throw new IllegalArgumentException("Temp file root cannot be your cache dir");
+ }
+ if (mContext.getFilesDir().getCanonicalPath().equals(canonicalTempFilePath)) {
+ throw new IllegalArgumentException("Temp file root cannot be your files dir");
+ }
+ }
+ /**
+ * Retrieves the currently configured temp file root directory. Returns the file that was
+ * configured via {@link #setTempFileRootDirectory(File)} or the default directory
+ * {@link #download(DownloadRequest)} was called without ever
+ * setting the temp file root. If neither method has been called since the last time the app's
+ * shared preferences were reset, returns {@code null}.
+ *
+ * @return A {@link File} pointing to the configured temp file directory, or null if not yet
+ * configured.
+ */
+ public @Nullable File getTempFileRootDirectory() {
+ SharedPreferences prefs = mContext.getSharedPreferences(
+ MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_FILE_NAME, 0);
+ String path = prefs.getString(MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_NAME, null);
+ if (path != null) {
+ return new File(path);
+ }
+ return null;
+ }
+
+ /**
+ * Requests the download of a file or set of files that the carrier has indicated to be
+ * available.
+ *
+ * May throw an {@link IllegalArgumentException}
+ *
+ * If {@link #setTempFileRootDirectory(File)} has not called after the app has been installed,
+ * this method will create a directory at the default location defined at
+ * {@link MbmsDownloadSession#DEFAULT_TOP_LEVEL_TEMP_DIRECTORY} and store that as the temp
+ * file root directory.
+ *
+ * Asynchronous errors through the callback may include any error not specific to the
+ * streaming use-case.
+ * @param request The request that specifies what should be downloaded.
+ */
+ public void download(@NonNull DownloadRequest request) {
+ IMbmsDownloadService downloadService = mService.get();
+ if (downloadService == null) {
+ throw new IllegalStateException("Middleware not yet bound");
+ }
+
+ // Check to see whether the app's set a temp root dir yet, and set it if not.
+ SharedPreferences prefs = mContext.getSharedPreferences(
+ MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_FILE_NAME, 0);
+ if (prefs.getString(MbmsTempFileProvider.TEMP_FILE_ROOT_PREF_NAME, null) == null) {
+ File tempRootDirectory = new File(mContext.getFilesDir(),
+ DEFAULT_TOP_LEVEL_TEMP_DIRECTORY);
+ tempRootDirectory.mkdirs();
+ setTempFileRootDirectory(tempRootDirectory);
+ }
+
+ writeDownloadRequestToken(request);
+ try {
+ downloadService.download(request);
+ } catch (RemoteException e) {
+ mService.set(null);
+ sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+ }
+ }
+
+ /**
+ * Returns a list of pending {@link DownloadRequest}s that originated from this application.
+ * A pending request is one that was issued via
+ * {@link #download(DownloadRequest)} but not cancelled through
+ * {@link #cancelDownload(DownloadRequest)}.
+ * @return A list, possibly empty, of {@link DownloadRequest}s
+ */
+ public @NonNull List<DownloadRequest> listPendingDownloads() {
+ IMbmsDownloadService downloadService = mService.get();
+ if (downloadService == null) {
+ throw new IllegalStateException("Middleware not yet bound");
+ }
+
+ try {
+ return downloadService.listPendingDownloads(mSubscriptionId);
+ } catch (RemoteException e) {
+ mService.set(null);
+ sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+ return Collections.emptyList();
+ }
+ }
+
+ /**
+ * Registers a callback for a {@link DownloadRequest} previously requested via
+ * {@link #download(DownloadRequest)}. This callback will only be called as long as both this
+ * app and the middleware are both running -- if either one stops, no further calls on the
+ * provided {@link DownloadStateCallback} will be enqueued.
+ *
+ * If the middleware is not aware of the specified download request,
+ * this method will throw an {@link IllegalArgumentException}.
+ *
+ * @param request The {@link DownloadRequest} that you want updates on.
+ * @param callback The callback that should be called when the middleware has information to
+ * share on the download.
+ * @param handler The {@link Handler} on which calls to {@code callback} should be enqueued on.
+ */
+ public void registerStateCallback(@NonNull DownloadRequest request,
+ @NonNull DownloadStateCallback callback,
+ @NonNull Handler handler) {
+ IMbmsDownloadService downloadService = mService.get();
+ if (downloadService == null) {
+ throw new IllegalStateException("Middleware not yet bound");
+ }
+
+ InternalDownloadStateCallback internalCallback =
+ new InternalDownloadStateCallback(callback, handler);
+
+ try {
+ int result = downloadService.registerStateCallback(request, internalCallback);
+ if (result != MbmsErrors.SUCCESS) {
+ if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
+ throw new IllegalArgumentException("Unknown download request.");
+ }
+ sendErrorToApp(result, null);
+ return;
+ }
+ } catch (RemoteException e) {
+ mService.set(null);
+ sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+ return;
+ }
+ mInternalDownloadCallbacks.put(callback, internalCallback);
+ }
+
+ /**
+ * Un-register a callback previously registered via
+ * {@link #registerStateCallback(DownloadRequest, DownloadStateCallback, Handler)}. After
+ * this method is called, no further callbacks will be enqueued on the {@link Handler}
+ * provided upon registration, even if this method throws an exception.
+ *
+ * If the middleware is not aware of the specified download request,
+ * this method will throw an {@link IllegalArgumentException}.
+ *
+ * @param request The {@link DownloadRequest} provided during registration
+ * @param callback The callback provided during registration.
+ */
+ public void unregisterStateCallback(@NonNull DownloadRequest request,
+ @NonNull DownloadStateCallback callback) {
+ try {
+ IMbmsDownloadService downloadService = mService.get();
+ if (downloadService == null) {
+ throw new IllegalStateException("Middleware not yet bound");
+ }
+
+ InternalDownloadStateCallback internalCallback =
+ mInternalDownloadCallbacks.get(callback);
+
+ try {
+ int result = downloadService.unregisterStateCallback(request, internalCallback);
+ if (result != MbmsErrors.SUCCESS) {
+ if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
+ throw new IllegalArgumentException("Unknown download request.");
+ }
+ sendErrorToApp(result, null);
+ }
+ } catch (RemoteException e) {
+ mService.set(null);
+ sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+ }
+ } finally {
+ InternalDownloadStateCallback internalCallback =
+ mInternalDownloadCallbacks.remove(callback);
+ if (internalCallback != null) {
+ internalCallback.stop();
+ }
+ }
+ }
+
+ /**
+ * Attempts to cancel the specified {@link DownloadRequest}.
+ *
+ * If the middleware is not aware of the specified download request,
+ * this method will throw an {@link IllegalArgumentException}.
+ *
+ * @param downloadRequest The download request that you wish to cancel.
+ */
+ public void cancelDownload(@NonNull DownloadRequest downloadRequest) {
+ IMbmsDownloadService downloadService = mService.get();
+ if (downloadService == null) {
+ throw new IllegalStateException("Middleware not yet bound");
+ }
+
+ try {
+ int result = downloadService.cancelDownload(downloadRequest);
+ if (result != MbmsErrors.SUCCESS) {
+ if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
+ throw new IllegalArgumentException("Unknown download request.");
+ }
+ sendErrorToApp(result, null);
+ return;
+ }
+ } catch (RemoteException e) {
+ mService.set(null);
+ sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+ return;
+ }
+ deleteDownloadRequestToken(downloadRequest);
+ }
+
+ /**
+ * Gets information about the status of a file pending download.
+ *
+ * If there was a problem communicating with the middleware or if it has no records of the
+ * file indicated by {@code fileInfo} being associated with {@code downloadRequest},
+ * {@link #STATUS_UNKNOWN} will be returned.
+ *
+ * @param downloadRequest The download request to query.
+ * @param fileInfo The particular file within the request to get information on.
+ * @return The status of the download.
+ */
+ @DownloadStatus
+ public int getDownloadStatus(DownloadRequest downloadRequest, FileInfo fileInfo) {
+ IMbmsDownloadService downloadService = mService.get();
+ if (downloadService == null) {
+ throw new IllegalStateException("Middleware not yet bound");
+ }
+
+ try {
+ return downloadService.getDownloadStatus(downloadRequest, fileInfo);
+ } catch (RemoteException e) {
+ mService.set(null);
+ sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+ return STATUS_UNKNOWN;
+ }
+ }
+
+ /**
+ * Resets the middleware's knowledge of previously-downloaded files in this download request.
+ *
+ * Normally, the middleware keeps track of the hashes of downloaded files and won't re-download
+ * files whose server-reported hash matches one of the already-downloaded files. This means
+ * that if the file is accidentally deleted by the user or by the app, the middleware will
+ * not try to download it again.
+ * This method will reset the middleware's cache of hashes for the provided
+ * {@link DownloadRequest}, so that previously downloaded content will be downloaded again
+ * when available.
+ * This will not interrupt in-progress downloads.
+ *
+ * This is distinct from cancelling and re-issuing the download request -- if you cancel and
+ * re-issue, the middleware will not clear its cache of download state information.
+ *
+ * If the middleware is not aware of the specified download request, an
+ * {@link IllegalArgumentException} will be thrown.
+ *
+ * @param downloadRequest The request to re-download files for.
+ */
+ public void resetDownloadKnowledge(DownloadRequest downloadRequest) {
+ IMbmsDownloadService downloadService = mService.get();
+ if (downloadService == null) {
+ throw new IllegalStateException("Middleware not yet bound");
+ }
+
+ try {
+ int result = downloadService.resetDownloadKnowledge(downloadRequest);
+ if (result != MbmsErrors.SUCCESS) {
+ if (result == MbmsErrors.DownloadErrors.ERROR_UNKNOWN_DOWNLOAD_REQUEST) {
+ throw new IllegalArgumentException("Unknown download request.");
+ }
+ sendErrorToApp(result, null);
+ }
+ } catch (RemoteException e) {
+ mService.set(null);
+ sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+ }
+ }
+
+ /**
+ * Terminates this instance.
+ *
+ * After this method returns,
+ * no further callbacks originating from the middleware will be enqueued on the provided
+ * instance of {@link MbmsDownloadSessionCallback}, but callbacks that have already been
+ * enqueued will still be delivered.
+ *
+ * It is safe to call {@link #create(Context, MbmsDownloadSessionCallback, int, Handler)} to
+ * obtain another instance of {@link MbmsDownloadSession} immediately after this method
+ * returns.
+ *
+ * May throw an {@link IllegalStateException}
+ */
+ @Override
+ public void close() {
+ try {
+ IMbmsDownloadService downloadService = mService.get();
+ if (downloadService == null) {
+ Log.i(LOG_TAG, "Service already dead");
+ return;
+ }
+ downloadService.dispose(mSubscriptionId);
+ } catch (RemoteException e) {
+ // Ignore
+ Log.i(LOG_TAG, "Remote exception while disposing of service");
+ } finally {
+ mService.set(null);
+ sIsInitialized.set(false);
+ mInternalCallback.stop();
+ }
+ }
+
+ private void writeDownloadRequestToken(DownloadRequest request) {
+ File token = getDownloadRequestTokenPath(request);
+ if (!token.getParentFile().exists()) {
+ token.getParentFile().mkdirs();
+ }
+ if (token.exists()) {
+ Log.w(LOG_TAG, "Download token " + token.getName() + " already exists");
+ return;
+ }
+ try {
+ if (!token.createNewFile()) {
+ throw new RuntimeException("Failed to create download token for request "
+ + request);
+ }
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to create download token for request " + request
+ + " due to IOException " + e);
+ }
+ }
+
+ private void deleteDownloadRequestToken(DownloadRequest request) {
+ File token = getDownloadRequestTokenPath(request);
+ if (!token.isFile()) {
+ Log.w(LOG_TAG, "Attempting to delete non-existent download token at " + token);
+ return;
+ }
+ if (!token.delete()) {
+ Log.w(LOG_TAG, "Couldn't delete download token at " + token);
+ }
+ }
+
+ private File getDownloadRequestTokenPath(DownloadRequest request) {
+ File tempFileLocation = MbmsUtils.getEmbmsTempFileDirForService(mContext,
+ request.getFileServiceId());
+ String downloadTokenFileName = request.getHash()
+ + MbmsDownloadReceiver.DOWNLOAD_TOKEN_SUFFIX;
+ return new File(tempFileLocation, downloadTokenFileName);
+ }
+
+ private void sendErrorToApp(int errorCode, String message) {
+ try {
+ mInternalCallback.onError(errorCode, message);
+ } catch (RemoteException e) {
+ // Ignore, should not happen locally.
+ }
+ }
+}
diff --git a/telephony/java/android/telephony/MbmsStreamingManager.java b/telephony/java/android/telephony/MbmsStreamingSession.java
index b6b253ecaddb..a8c46079148c 100644
--- a/telephony/java/android/telephony/MbmsStreamingManager.java
+++ b/telephony/java/android/telephony/MbmsStreamingSession.java
@@ -16,6 +16,8 @@
package android.telephony;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
import android.content.ComponentName;
@@ -25,18 +27,20 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
-import android.telephony.mbms.InternalStreamingManagerCallback;
+import android.telephony.mbms.InternalStreamingSessionCallback;
import android.telephony.mbms.InternalStreamingServiceCallback;
-import android.telephony.mbms.MbmsException;
-import android.telephony.mbms.MbmsStreamingManagerCallback;
+import android.telephony.mbms.MbmsErrors;
+import android.telephony.mbms.MbmsStreamingSessionCallback;
import android.telephony.mbms.MbmsUtils;
import android.telephony.mbms.StreamingService;
import android.telephony.mbms.StreamingServiceCallback;
import android.telephony.mbms.StreamingServiceInfo;
import android.telephony.mbms.vendor.IMbmsStreamingService;
+import android.util.ArraySet;
import android.util.Log;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
@@ -45,8 +49,8 @@ import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
/**
* This class provides functionality for streaming media over MBMS.
*/
-public class MbmsStreamingManager {
- private static final String LOG_TAG = "MbmsStreamingManager";
+public class MbmsStreamingSession implements AutoCloseable {
+ private static final String LOG_TAG = "MbmsStreamingSession";
/**
* Service action which must be handled by the middleware implementing the MBMS streaming
@@ -65,98 +69,98 @@ public class MbmsStreamingManager {
@Override
public void binderDied() {
sIsInitialized.set(false);
- sendErrorToApp(MbmsException.ERROR_MIDDLEWARE_LOST, "Received death notification");
+ sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, "Received death notification");
}
};
- private InternalStreamingManagerCallback mInternalCallback;
+ private InternalStreamingSessionCallback mInternalCallback;
+ private Set<StreamingService> mKnownActiveStreamingServices = new ArraySet<>();
private final Context mContext;
private int mSubscriptionId = INVALID_SUBSCRIPTION_ID;
/** @hide */
- private MbmsStreamingManager(Context context, MbmsStreamingManagerCallback callback,
+ private MbmsStreamingSession(Context context, MbmsStreamingSessionCallback callback,
int subscriptionId, Handler handler) {
mContext = context;
mSubscriptionId = subscriptionId;
if (handler == null) {
handler = new Handler(Looper.getMainLooper());
}
- mInternalCallback = new InternalStreamingManagerCallback(callback, handler);
+ mInternalCallback = new InternalStreamingSessionCallback(callback, handler);
}
/**
- * Create a new MbmsStreamingManager using the given subscription ID.
+ * Create a new {@link MbmsStreamingSession} using the given subscription ID.
*
* Note that this call will bind a remote service. You may not call this method on your app's
- * main thread. This may throw an {@link MbmsException}, indicating errors that may happen
- * during the initialization or binding process.
+ * main thread.
*
- *
- * You may only have one instance of {@link MbmsStreamingManager} per UID. If you call this
- * method while there is an active instance of {@link MbmsStreamingManager} in your process
- * (in other words, one that has not had {@link #dispose()} called on it), this method will
- * throw an {@link MbmsException}. If you call this method in a different process
+ * You may only have one instance of {@link MbmsStreamingSession} per UID. If you call this
+ * method while there is an active instance of {@link MbmsStreamingSession} in your process
+ * (in other words, one that has not had {@link #close()} called on it), this method will
+ * throw an {@link IllegalStateException}. If you call this method in a different process
* running under the same UID, an error will be indicated via
- * {@link MbmsStreamingManagerCallback#onError(int, String)}.
+ * {@link MbmsStreamingSessionCallback#onError(int, String)}.
*
* Note that initialization may fail asynchronously. If you wish to try again after you
- * receive such an asynchronous error, you must call dispose() on the instance of
- * {@link MbmsStreamingManager} that you received before calling this method again.
+ * receive such an asynchronous error, you must call {@link #close()} on the instance of
+ * {@link MbmsStreamingSession} that you received before calling this method again.
*
* @param context The {@link Context} to use.
* @param callback A callback object on which you wish to receive results of asynchronous
* operations.
* @param subscriptionId The subscription ID to use.
- * @param handler The handler you wish to receive callbacks on. If null, callbacks will be
- * processed on the main looper (in other words, the looper returned from
- * {@link Looper#getMainLooper()}).
+ * @param handler The handler you wish to receive callbacks on.
+ * @return An instance of {@link MbmsStreamingSession}, or null if an error occurred.
*/
- public static MbmsStreamingManager create(Context context,
- MbmsStreamingManagerCallback callback, int subscriptionId, Handler handler)
- throws MbmsException {
+ public static @Nullable MbmsStreamingSession create(@NonNull Context context,
+ final @NonNull MbmsStreamingSessionCallback callback, int subscriptionId,
+ @NonNull Handler handler) {
if (!sIsInitialized.compareAndSet(false, true)) {
- throw new MbmsException(MbmsException.InitializationErrors.ERROR_DUPLICATE_INITIALIZE);
+ throw new IllegalStateException("Cannot create two instances of MbmsStreamingSession");
}
- MbmsStreamingManager manager = new MbmsStreamingManager(context, callback,
+ MbmsStreamingSession session = new MbmsStreamingSession(context, callback,
subscriptionId, handler);
- try {
- manager.bindAndInitialize();
- } catch (MbmsException e) {
+
+ final int result = session.bindAndInitialize();
+ if (result != MbmsErrors.SUCCESS) {
sIsInitialized.set(false);
- throw e;
+ handler.post(new Runnable() {
+ @Override
+ public void run() {
+ callback.onError(result, null);
+ }
+ });
+ return null;
}
- return manager;
+ return session;
}
/**
- * Create a new MbmsStreamingManager using the system default data subscription ID.
- * See {@link #create(Context, MbmsStreamingManagerCallback, int, Handler)}.
+ * Create a new {@link MbmsStreamingSession} using the system default data subscription ID.
+ * See {@link #create(Context, MbmsStreamingSessionCallback, int, Handler)}.
*/
- public static MbmsStreamingManager create(Context context,
- MbmsStreamingManagerCallback callback, Handler handler)
- throws MbmsException {
+ public static MbmsStreamingSession create(@NonNull Context context,
+ @NonNull MbmsStreamingSessionCallback callback, @NonNull Handler handler) {
return create(context, callback, SubscriptionManager.getDefaultSubscriptionId(), handler);
}
/**
- * Create a new MbmsStreamingManager using the system default data subscription ID and
- * default {@link Handler}.
- * See {@link #create(Context, MbmsStreamingManagerCallback, int, Handler)}.
- */
- public static MbmsStreamingManager create(Context context,
- MbmsStreamingManagerCallback callback)
- throws MbmsException {
- return create(context, callback, SubscriptionManager.getDefaultSubscriptionId(), null);
- }
-
- /**
- * Terminates this instance, ending calls to the registered listener. Also terminates
- * any streaming services spawned from this instance.
+ * Terminates this instance. Also terminates
+ * any streaming services spawned from this instance as if
+ * {@link StreamingService#stopStreaming()} had been called on them. After this method returns,
+ * no further callbacks originating from the middleware will be enqueued on the provided
+ * instance of {@link MbmsStreamingSessionCallback}, but callbacks that have already been
+ * enqueued will still be delivered.
+ *
+ * It is safe to call {@link #create(Context, MbmsStreamingSessionCallback, int, Handler)} to
+ * obtain another instance of {@link MbmsStreamingSession} immediately after this method
+ * returns.
*
* May throw an {@link IllegalStateException}
*/
- public void dispose() {
+ public void close() {
try {
IMbmsStreamingService streamingService = mService.get();
if (streamingService == null) {
@@ -164,47 +168,49 @@ public class MbmsStreamingManager {
return;
}
streamingService.dispose(mSubscriptionId);
+ for (StreamingService s : mKnownActiveStreamingServices) {
+ s.getCallback().stop();
+ }
+ mKnownActiveStreamingServices.clear();
} catch (RemoteException e) {
// Ignore for now
} finally {
mService.set(null);
sIsInitialized.set(false);
+ mInternalCallback.stop();
}
}
/**
* An inspection API to retrieve the list of streaming media currently be advertised.
- * The results are returned asynchronously through the previously registered callback.
- * serviceClasses lets the app filter on types of programming and is opaque data between
- * the app and the carrier.
+ * The results are returned asynchronously via
+ * {@link MbmsStreamingSessionCallback#onStreamingServicesUpdated(List)} on the callback
+ * provided upon creation.
*
- * Multiple calls replace the list of serviceClasses of interest.
+ * Multiple calls replace the list of service classes of interest.
*
- * This may throw an {@link MbmsException} containing any error in
- * {@link android.telephony.mbms.MbmsException.GeneralErrors},
- * {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}, or
- * {@link MbmsException#ERROR_MIDDLEWARE_LOST}.
+ * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}.
*
- * May also throw an unchecked {@link IllegalArgumentException} or an
- * {@link IllegalStateException}
- *
- * @param classList A list of streaming service classes that the app would like updates on.
+ * @param serviceClassList A list of streaming service classes that the app would like updates
+ * on. The exact names of these classes should be negotiated with the
+ * wireless carrier separately.
*/
- public void getStreamingServices(List<String> classList) throws MbmsException {
+ public void requestUpdateStreamingServices(List<String> serviceClassList) {
IMbmsStreamingService streamingService = mService.get();
if (streamingService == null) {
- throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
+ throw new IllegalStateException("Middleware not yet bound");
}
try {
- int returnCode = streamingService.getStreamingServices(mSubscriptionId, classList);
- if (returnCode != MbmsException.SUCCESS) {
- throw new MbmsException(returnCode);
+ int returnCode = streamingService.requestUpdateStreamingServices(
+ mSubscriptionId, serviceClassList);
+ if (returnCode != MbmsErrors.SUCCESS) {
+ sendErrorToApp(returnCode, null);
}
} catch (RemoteException e) {
Log.w(LOG_TAG, "Remote process died");
mService.set(null);
sIsInitialized.set(false);
- throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
+ sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
}
}
@@ -215,56 +221,57 @@ public class MbmsStreamingManager {
* reported via
* {@link android.telephony.mbms.StreamingServiceCallback#onStreamStateUpdated(int, int)}
*
- * May throw an
- * {@link MbmsException} containing any of the error codes in
- * {@link android.telephony.mbms.MbmsException.GeneralErrors},
- * {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}, or
- * {@link MbmsException#ERROR_MIDDLEWARE_LOST}.
- *
- * May also throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
+ * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
*
* Asynchronous errors through the callback include any of the errors in
- * {@link android.telephony.mbms.MbmsException.GeneralErrors} or
- * {@link android.telephony.mbms.MbmsException.StreamingErrors}.
+ * {@link MbmsErrors.GeneralErrors} or
+ * {@link MbmsErrors.StreamingErrors}.
*
* @param serviceInfo The information about the service to stream.
* @param callback A callback that'll be called when something about the stream changes.
- * @param handler A handler that calls to {@code callback} should be called on. If null,
- * defaults to the handler provided via
- * {@link #create(Context, MbmsStreamingManagerCallback, int, Handler)}.
+ * @param handler A handler that calls to {@code callback} should be called on.
* @return An instance of {@link StreamingService} through which the stream can be controlled.
+ * May be {@code null} if an error occurred.
*/
- public StreamingService startStreaming(StreamingServiceInfo serviceInfo,
- StreamingServiceCallback callback, Handler handler) throws MbmsException {
+ public @Nullable StreamingService startStreaming(StreamingServiceInfo serviceInfo,
+ StreamingServiceCallback callback, @NonNull Handler handler) {
IMbmsStreamingService streamingService = mService.get();
if (streamingService == null) {
- throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_NOT_BOUND);
+ throw new IllegalStateException("Middleware not yet bound");
}
InternalStreamingServiceCallback serviceCallback = new InternalStreamingServiceCallback(
- callback, handler == null ? mInternalCallback.getHandler() : handler);
+ callback, handler);
StreamingService serviceForApp = new StreamingService(
- mSubscriptionId, streamingService, serviceInfo, serviceCallback);
+ mSubscriptionId, streamingService, this, serviceInfo, serviceCallback);
+ mKnownActiveStreamingServices.add(serviceForApp);
try {
int returnCode = streamingService.startStreaming(
mSubscriptionId, serviceInfo.getServiceId(), serviceCallback);
- if (returnCode != MbmsException.SUCCESS) {
- throw new MbmsException(returnCode);
+ if (returnCode != MbmsErrors.SUCCESS) {
+ sendErrorToApp(returnCode, null);
+ return null;
}
} catch (RemoteException e) {
Log.w(LOG_TAG, "Remote process died");
mService.set(null);
sIsInitialized.set(false);
- throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
+ sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+ return null;
}
return serviceForApp;
}
- private void bindAndInitialize() throws MbmsException {
- MbmsUtils.startBinding(mContext, MBMS_STREAMING_SERVICE_ACTION,
+ /** @hide */
+ public void onStreamingServiceStopped(StreamingService service) {
+ mKnownActiveStreamingServices.remove(service);
+ }
+
+ private int bindAndInitialize() {
+ return MbmsUtils.startBinding(mContext, MBMS_STREAMING_SERVICE_ACTION,
new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
@@ -277,19 +284,19 @@ public class MbmsStreamingManager {
} catch (RemoteException e) {
Log.e(LOG_TAG, "Service died before initialization");
sendErrorToApp(
- MbmsException.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+ MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
e.toString());
sIsInitialized.set(false);
return;
} catch (RuntimeException e) {
Log.e(LOG_TAG, "Runtime exception during initialization");
sendErrorToApp(
- MbmsException.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
+ MbmsErrors.InitializationErrors.ERROR_UNABLE_TO_INITIALIZE,
e.toString());
sIsInitialized.set(false);
return;
}
- if (result != MbmsException.SUCCESS) {
+ if (result != MbmsErrors.SUCCESS) {
sendErrorToApp(result, "Error returned during initialization");
sIsInitialized.set(false);
return;
@@ -297,7 +304,7 @@ public class MbmsStreamingManager {
try {
streamingService.asBinder().linkToDeath(mDeathRecipient, 0);
} catch (RemoteException e) {
- sendErrorToApp(MbmsException.ERROR_MIDDLEWARE_LOST,
+ sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST,
"Middleware lost during initialization");
sIsInitialized.set(false);
return;
@@ -315,7 +322,7 @@ public class MbmsStreamingManager {
private void sendErrorToApp(int errorCode, String message) {
try {
- mInternalCallback.error(errorCode, message);
+ mInternalCallback.onError(errorCode, message);
} catch (RemoteException e) {
// Ignore, should not happen locally.
}
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index 87054469c2f9..1b942de860be 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -77,9 +77,28 @@ public class PhoneNumberUtils
public static final int TOA_International = 0x91;
public static final int TOA_Unknown = 0x81;
+ /*
+ * The BCD extended type used to determine the extended char for the digit which is greater than
+ * 9.
+ *
+ * see TS 51.011 section 10.5.1 EF_ADN(Abbreviated dialling numbers)
+ */
+ public static final int BCD_EXTENDED_TYPE_EF_ADN = 1;
+
+ /*
+ * The BCD extended type used to determine the extended char for the digit which is greater than
+ * 9.
+ *
+ * see TS 24.008 section 10.5.4.7 Called party BCD number
+ */
+ public static final int BCD_EXTENDED_TYPE_CALLED_PARTY = 2;
+
static final String LOG_TAG = "PhoneNumberUtils";
private static final boolean DBG = false;
+ private static final String BCD_EF_ADN_EXTENDED = "*#,N;";
+ private static final String BCD_CALLED_PARTY_EXTENDED = "*#abc";
+
/*
* global-phone-number = ["+"] 1*( DIGIT / written-sep )
* written-sep = ("-"/".")
@@ -799,11 +818,33 @@ public class PhoneNumberUtils
*
* @return partial string on invalid decode
*
- * FIXME(mkf) support alphanumeric address type
- * currently implemented in SMSMessage.getAddress()
+ * @deprecated use {@link #calledPartyBCDToString(byte[], int, int, int)} instead. Calling this
+ * method is equivalent to calling {@link #calledPartyBCDToString(byte[], int, int)} with
+ * {@link #BCD_EXTENDED_TYPE_EF_ADN} as the extended type.
*/
- public static String
- calledPartyBCDToString (byte[] bytes, int offset, int length) {
+ @Deprecated
+ public static String calledPartyBCDToString(byte[] bytes, int offset, int length) {
+ return calledPartyBCDToString(bytes, offset, length, BCD_EXTENDED_TYPE_EF_ADN);
+ }
+
+ /**
+ * 3GPP TS 24.008 10.5.4.7
+ * Called Party BCD Number
+ *
+ * See Also TS 51.011 10.5.1 "dialing number/ssc string"
+ * and TS 11.11 "10.3.1 EF adn (Abbreviated dialing numbers)"
+ *
+ * @param bytes the data buffer
+ * @param offset should point to the TOA (aka. TON/NPI) octet after the length byte
+ * @param length is the number of bytes including TOA byte
+ * and must be at least 2
+ * @param bcdExtType used to determine the extended bcd coding
+ * @see #BCD_EXTENDED_TYPE_EF_ADN
+ * @see #BCD_EXTENDED_TYPE_CALLED_PARTY
+ *
+ */
+ public static String calledPartyBCDToString(
+ byte[] bytes, int offset, int length, int bcdExtType) {
boolean prependPlus = false;
StringBuilder ret = new StringBuilder(1 + length * 2);
@@ -817,7 +858,7 @@ public class PhoneNumberUtils
}
internalCalledPartyBCDFragmentToString(
- ret, bytes, offset + 1, length - 1);
+ ret, bytes, offset + 1, length - 1, bcdExtType);
if (prependPlus && ret.length() == 0) {
// If the only thing there is a prepended plus, return ""
@@ -902,14 +943,13 @@ public class PhoneNumberUtils
return ret.toString();
}
- private static void
- internalCalledPartyBCDFragmentToString(
- StringBuilder sb, byte [] bytes, int offset, int length) {
+ private static void internalCalledPartyBCDFragmentToString(
+ StringBuilder sb, byte [] bytes, int offset, int length, int bcdExtType) {
for (int i = offset ; i < length + offset ; i++) {
byte b;
char c;
- c = bcdToChar((byte)(bytes[i] & 0xf));
+ c = bcdToChar((byte)(bytes[i] & 0xf), bcdExtType);
if (c == 0) {
return;
@@ -930,7 +970,7 @@ public class PhoneNumberUtils
break;
}
- c = bcdToChar(b);
+ c = bcdToChar(b, bcdExtType);
if (c == 0) {
return;
}
@@ -943,49 +983,65 @@ public class PhoneNumberUtils
/**
* Like calledPartyBCDToString, but field does not start with a
* TOA byte. For example: SIM ADN extension fields
+ *
+ * @deprecated use {@link #calledPartyBCDFragmentToString(byte[], int, int, int)} instead.
+ * Calling this method is equivalent to calling
+ * {@link #calledPartyBCDFragmentToString(byte[], int, int, int)} with
+ * {@link #BCD_EXTENDED_TYPE_EF_ADN} as the extended type.
*/
+ @Deprecated
+ public static String calledPartyBCDFragmentToString(byte[] bytes, int offset, int length) {
+ return calledPartyBCDFragmentToString(bytes, offset, length, BCD_EXTENDED_TYPE_EF_ADN);
+ }
- public static String
- calledPartyBCDFragmentToString(byte [] bytes, int offset, int length) {
+ /**
+ * Like calledPartyBCDToString, but field does not start with a
+ * TOA byte. For example: SIM ADN extension fields
+ */
+ public static String calledPartyBCDFragmentToString(
+ byte[] bytes, int offset, int length, int bcdExtType) {
StringBuilder ret = new StringBuilder(length * 2);
-
- internalCalledPartyBCDFragmentToString(ret, bytes, offset, length);
-
+ internalCalledPartyBCDFragmentToString(ret, bytes, offset, length, bcdExtType);
return ret.toString();
}
- /** returns 0 on invalid value */
- private static char
- bcdToChar(byte b) {
+ /**
+ * Returns the correspond character for given {@code b} based on {@code bcdExtType}, or 0 on
+ * invalid code.
+ */
+ private static char bcdToChar(byte b, int bcdExtType) {
if (b < 0xa) {
- return (char)('0' + b);
- } else switch (b) {
- case 0xa: return '*';
- case 0xb: return '#';
- case 0xc: return PAUSE;
- case 0xd: return WILD;
+ return (char) ('0' + b);
+ }
- default: return 0;
+ String extended = null;
+ if (BCD_EXTENDED_TYPE_EF_ADN == bcdExtType) {
+ extended = BCD_EF_ADN_EXTENDED;
+ } else if (BCD_EXTENDED_TYPE_CALLED_PARTY == bcdExtType) {
+ extended = BCD_CALLED_PARTY_EXTENDED;
}
+ if (extended == null || b - 0xa >= extended.length()) {
+ return 0;
+ }
+
+ return extended.charAt(b - 0xa);
}
- private static int
- charToBCD(char c) {
- if (c >= '0' && c <= '9') {
+ private static int charToBCD(char c, int bcdExtType) {
+ if ('0' <= c && c <= '9') {
return c - '0';
- } else if (c == '*') {
- return 0xa;
- } else if (c == '#') {
- return 0xb;
- } else if (c == PAUSE) {
- return 0xc;
- } else if (c == WILD) {
- return 0xd;
- } else if (c == WAIT) {
- return 0xe;
- } else {
- throw new RuntimeException ("invalid char for BCD " + c);
}
+
+ String extended = null;
+ if (BCD_EXTENDED_TYPE_EF_ADN == bcdExtType) {
+ extended = BCD_EF_ADN_EXTENDED;
+ } else if (BCD_EXTENDED_TYPE_CALLED_PARTY == bcdExtType) {
+ extended = BCD_CALLED_PARTY_EXTENDED;
+ }
+ if (extended == null || extended.indexOf(c) == -1) {
+ throw new RuntimeException("invalid char for BCD " + c);
+ }
+ return 0xa + extended.indexOf(c);
}
/**
@@ -1034,40 +1090,60 @@ public class PhoneNumberUtils
*
* Returns null if network portion is empty.
*/
- public static byte[]
- networkPortionToCalledPartyBCD(String s) {
+ public static byte[] networkPortionToCalledPartyBCD(String s) {
String networkPortion = extractNetworkPortion(s);
- return numberToCalledPartyBCDHelper(networkPortion, false);
+ return numberToCalledPartyBCDHelper(
+ networkPortion, false, BCD_EXTENDED_TYPE_EF_ADN);
}
/**
* Same as {@link #networkPortionToCalledPartyBCD}, but includes a
* one-byte length prefix.
*/
- public static byte[]
- networkPortionToCalledPartyBCDWithLength(String s) {
+ public static byte[] networkPortionToCalledPartyBCDWithLength(String s) {
String networkPortion = extractNetworkPortion(s);
- return numberToCalledPartyBCDHelper(networkPortion, true);
+ return numberToCalledPartyBCDHelper(
+ networkPortion, true, BCD_EXTENDED_TYPE_EF_ADN);
+ }
+
+ /**
+ * Convert a dialing number to BCD byte array
+ *
+ * @param number dialing number string. If the dialing number starts with '+', set to
+ * international TOA
+ *
+ * @return BCD byte array
+ *
+ * @deprecated use {@link #numberToCalledPartyBCD(String, int)} instead. Calling this method
+ * is equivalent to calling {@link #numberToCalledPartyBCD(String, int)} with
+ * {@link #BCD_EXTENDED_TYPE_EF_ADN} as the extended type.
+ */
+ @Deprecated
+ public static byte[] numberToCalledPartyBCD(String number) {
+ return numberToCalledPartyBCD(number, BCD_EXTENDED_TYPE_EF_ADN);
}
/**
* Convert a dialing number to BCD byte array
*
- * @param number dialing number string
- * if the dialing number starts with '+', set to international TOA
+ * @param number dialing number string. If the dialing number starts with '+', set to
+ * international TOA
+ * @param bcdExtType used to determine the extended bcd coding
+ * @see #BCD_EXTENDED_TYPE_EF_ADN
+ * @see #BCD_EXTENDED_TYPE_CALLED_PARTY
+ *
* @return BCD byte array
*/
- public static byte[]
- numberToCalledPartyBCD(String number) {
- return numberToCalledPartyBCDHelper(number, false);
+ public static byte[] numberToCalledPartyBCD(String number, int bcdExtType) {
+ return numberToCalledPartyBCDHelper(number, false, bcdExtType);
}
/**
* If includeLength is true, prepend a one-byte length value to
* the return array.
*/
- private static byte[]
- numberToCalledPartyBCDHelper(String number, boolean includeLength) {
+ private static byte[] numberToCalledPartyBCDHelper(
+ String number, boolean includeLength, int bcdExtType) {
int numberLenReal = number.length();
int numberLenEffective = numberLenReal;
boolean hasPlus = number.indexOf('+') != -1;
@@ -1087,7 +1163,8 @@ public class PhoneNumberUtils
char c = number.charAt(i);
if (c == '+') continue;
int shift = ((digitCount & 0x01) == 1) ? 4 : 0;
- result[extraBytes + (digitCount >> 1)] |= (byte)((charToBCD(c) & 0x0F) << shift);
+ result[extraBytes + (digitCount >> 1)] |=
+ (byte)((charToBCD(c, bcdExtType) & 0x0F) << shift);
digitCount++;
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index ab679ff69d11..b7a7d8e3e639 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -1647,6 +1647,10 @@ public class TelephonyManager {
return getTelephonyProperty(phoneId, TelephonyProperties.PROPERTY_OPERATOR_ISO_COUNTRY, "");
}
+ /*
+ * When adding a network type to the list below, make sure to add the correct icon to
+ * MobileSignalController.mapIconSets().
+ */
/** Network type is unknown */
public static final int NETWORK_TYPE_UNKNOWN = 0;
/** Current network is GPRS */
@@ -5086,7 +5090,12 @@ public class TelephonyManager {
}
}
- /** @hide */
+ /**
+ * @deprecated Use {@link android.telecom.TelecomManager#placeCall(Uri address,
+ * Bundle extras)} instead.
+ * @hide
+ */
+ @Deprecated
@SystemApi
@RequiresPermission(android.Manifest.permission.CALL_PHONE)
public void call(String callingPackage, String number) {
@@ -5099,7 +5108,11 @@ public class TelephonyManager {
}
}
- /** @hide */
+ /**
+ * @deprecated Use {@link android.telecom.TelecomManager#endCall()} instead.
+ * @hide
+ */
+ @Deprecated
@SystemApi
@RequiresPermission(android.Manifest.permission.CALL_PHONE)
public boolean endCall() {
@@ -5113,7 +5126,11 @@ public class TelephonyManager {
return false;
}
- /** @hide */
+ /**
+ * @deprecated Use {@link android.telecom.TelecomManager#acceptRingingCall} instead
+ * @hide
+ */
+ @Deprecated
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void answerRingingCall() {
@@ -5126,7 +5143,11 @@ public class TelephonyManager {
}
}
- /** @hide */
+ /**
+ * @deprecated Use {@link android.telecom.TelecomManager#silenceRinger} instead
+ * @hide
+ */
+ @Deprecated
@SystemApi
@SuppressLint("Doclava125")
public void silenceRinger() {
@@ -5681,10 +5702,13 @@ public class TelephonyManager {
}
/**
+ * @deprecated Use {link@ android.telecom.TelecomManager#isTtySupported} instead
* Whether the phone supports TTY mode.
*
* @return {@code true} if the device supports TTY mode, and {@code false} otherwise.
+ *
*/
+ @Deprecated
public boolean isTtyModeSupported() {
try {
ITelephony telephony = getITelephony();
@@ -5776,6 +5800,25 @@ public class TelephonyManager {
}
/**
+ * Returns the IMS Registration Status for a particular Subscription ID
+ *
+ * @param subId Subscription ID
+ * @return true if IMS status is registered, false if the IMS status is not registered or a
+ * RemoteException occurred.
+ *
+ * @hide
+ */
+ public boolean isImsRegistered(int subId) {
+ try {
+ return getITelephony().isImsRegisteredForSubscriber(subId);
+ } catch (RemoteException ex) {
+ return false;
+ } catch (NullPointerException ex) {
+ return false;
+ }
+ }
+
+ /**
* Returns the Status of Volte
* @hide
*/
@@ -6740,7 +6783,6 @@ public class TelephonyManager {
* Get the most recent SignalStrength information reported by the modem. Due
* to power saving this information may not always be current.
* @return the most recent cached signal strength info from the modem
- * @hide
*/
@Nullable
public SignalStrength getSignalStrength() {
diff --git a/telephony/java/android/telephony/mbms/DownloadRequest.java b/telephony/java/android/telephony/mbms/DownloadRequest.java
index eae9011e42c8..5a57f3221d3a 100644
--- a/telephony/java/android/telephony/mbms/DownloadRequest.java
+++ b/telephony/java/android/telephony/mbms/DownloadRequest.java
@@ -16,6 +16,7 @@
package android.telephony.mbms;
+import android.annotation.SystemApi;
import android.content.Intent;
import android.net.Uri;
import android.os.Parcel;
@@ -37,34 +38,27 @@ import java.security.NoSuchAlgorithmException;
import java.util.Objects;
/**
- * A Parcelable class describing a pending Cell-Broadcast download request
- * @hide
+ * Describes a request to download files over cell-broadcast. Instances of this class should be
+ * created by the app when requesting a download, and instances of this class will be passed back
+ * to the app when the middleware updates the status of the download.
*/
-public class DownloadRequest implements Parcelable {
+public final class DownloadRequest implements Parcelable {
// Version code used to keep token calculation consistent.
private static final int CURRENT_VERSION = 1;
private static final String LOG_TAG = "MbmsDownloadRequest";
- /**
- * Maximum permissible length for the app's download-completion intent, when serialized via
- * {@link Intent#toUri(int)}.
- */
+ /** @hide */
public static final int MAX_APP_INTENT_SIZE = 50000;
- /**
- * Maximum permissible length for the app's destination path, when serialized via
- * {@link Uri#toString()}.
- */
+ /** @hide */
public static final int MAX_DESTINATION_URI_SIZE = 50000;
/** @hide */
private static class OpaqueDataContainer implements Serializable {
- private final String destinationUri;
private final String appIntent;
private final int version;
- public OpaqueDataContainer(String destinationUri, String appIntent, int version) {
- this.destinationUri = destinationUri;
+ public OpaqueDataContainer(String appIntent, int version) {
this.appIntent = appIntent;
this.version = version;
}
@@ -73,7 +67,6 @@ public class DownloadRequest implements Parcelable {
public static class Builder {
private String fileServiceId;
private Uri source;
- private Uri dest;
private int subscriptionId;
private String appIntent;
private int version = CURRENT_VERSION;
@@ -91,8 +84,8 @@ public class DownloadRequest implements Parcelable {
/**
* Set the service ID for the download request. For use by the middleware only.
* @hide
- * TODO: systemapi
*/
+ @SystemApi
public Builder setServiceId(String serviceId) {
fileServiceId = serviceId;
return this;
@@ -101,7 +94,6 @@ public class DownloadRequest implements Parcelable {
/**
* Sets the source URI for the download request to be built.
* @param source
- * @return
*/
public Builder setSource(Uri source) {
this.source = source;
@@ -109,25 +101,8 @@ public class DownloadRequest implements Parcelable {
}
/**
- * Sets the destination URI for the download request to be built. The middleware should
- * not set this directly.
- * @param dest A URI obtained from {@link Uri#fromFile(File)}, denoting the requested
- * final destination of the download.
- * @return
- */
- public Builder setDest(Uri dest) {
- if (dest.toString().length() > MAX_DESTINATION_URI_SIZE) {
- throw new IllegalArgumentException("Destination uri must not exceed length " +
- MAX_DESTINATION_URI_SIZE);
- }
- this.dest = dest;
- return this;
- }
-
- /**
* Set the subscription ID on which the file(s) should be downloaded.
* @param subscriptionId
- * @return
*/
public Builder setSubscriptionId(int subscriptionId) {
this.subscriptionId = subscriptionId;
@@ -141,7 +116,6 @@ public class DownloadRequest implements Parcelable {
*
* The middleware should not use this method.
* @param intent
- * @return
*/
public Builder setAppIntent(Intent intent) {
this.appIntent = intent.toUri(0);
@@ -158,17 +132,15 @@ public class DownloadRequest implements Parcelable {
* manager code, but is irrelevant to the middleware.
* @param data A byte array, the contents of which should have been originally obtained
* from {@link DownloadRequest#getOpaqueData()}.
- * @return
- * TODO: systemapi
* @hide
*/
+ @SystemApi
public Builder setOpaqueData(byte[] data) {
try {
ObjectInputStream stream = new ObjectInputStream(new ByteArrayInputStream(data));
OpaqueDataContainer dataContainer = (OpaqueDataContainer) stream.readObject();
version = dataContainer.version;
appIntent = dataContainer.appIntent;
- dest = Uri.parse(dataContainer.destinationUri);
} catch (IOException e) {
// Really should never happen
Log.e(LOG_TAG, "Got IOException trying to parse opaque data");
@@ -181,24 +153,21 @@ public class DownloadRequest implements Parcelable {
}
public DownloadRequest build() {
- return new DownloadRequest(fileServiceId, source, dest,
- subscriptionId, appIntent, version);
+ return new DownloadRequest(fileServiceId, source, subscriptionId, appIntent, version);
}
}
private final String fileServiceId;
private final Uri sourceUri;
- private final Uri destinationUri;
private final int subscriptionId;
private final String serializedResultIntentForApp;
private final int version;
private DownloadRequest(String fileServiceId,
- Uri source, Uri dest,
- int sub, String appIntent, int version) {
+ Uri source, int sub,
+ String appIntent, int version) {
this.fileServiceId = fileServiceId;
sourceUri = source;
- destinationUri = dest;
subscriptionId = sub;
serializedResultIntentForApp = appIntent;
this.version = version;
@@ -211,7 +180,6 @@ public class DownloadRequest implements Parcelable {
private DownloadRequest(DownloadRequest dr) {
fileServiceId = dr.fileServiceId;
sourceUri = dr.sourceUri;
- destinationUri = dr.destinationUri;
subscriptionId = dr.subscriptionId;
serializedResultIntentForApp = dr.serializedResultIntentForApp;
version = dr.version;
@@ -220,7 +188,6 @@ public class DownloadRequest implements Parcelable {
private DownloadRequest(Parcel in) {
fileServiceId = in.readString();
sourceUri = in.readParcelable(getClass().getClassLoader());
- destinationUri = in.readParcelable(getClass().getClassLoader());
subscriptionId = in.readInt();
serializedResultIntentForApp = in.readString();
version = in.readInt();
@@ -233,7 +200,6 @@ public class DownloadRequest implements Parcelable {
public void writeToParcel(Parcel out, int flags) {
out.writeString(fileServiceId);
out.writeParcelable(sourceUri, flags);
- out.writeParcelable(destinationUri, flags);
out.writeInt(subscriptionId);
out.writeString(serializedResultIntentForApp);
out.writeInt(version);
@@ -254,14 +220,6 @@ public class DownloadRequest implements Parcelable {
}
/**
- * For use by the client app only.
- * @return The URI of the final destination of the download.
- */
- public Uri getDestinationUri() {
- return destinationUri;
- }
-
- /**
* @return The subscription ID on which to perform MBMS operations.
*/
public int getSubscriptionId() {
@@ -287,14 +245,14 @@ public class DownloadRequest implements Parcelable {
* {@link Builder#setOpaqueData(byte[])}.
* @return A byte array of opaque data to persist.
* @hide
- * TODO: systemapi
*/
+ @SystemApi
public byte[] getOpaqueData() {
try {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ObjectOutputStream stream = new ObjectOutputStream(byteArrayOutputStream);
OpaqueDataContainer container = new OpaqueDataContainer(
- destinationUri.toString(), serializedResultIntentForApp, version);
+ serializedResultIntentForApp, version);
stream.writeObject(container);
stream.flush();
return byteArrayOutputStream.toByteArray();
@@ -321,6 +279,22 @@ public class DownloadRequest implements Parcelable {
};
/**
+ * Maximum permissible length for the app's destination path, when serialized via
+ * {@link Uri#toString()}.
+ */
+ public static int getMaxAppIntentSize() {
+ return MAX_APP_INTENT_SIZE;
+ }
+
+ /**
+ * Maximum permissible length for the app's download-completion intent, when serialized via
+ * {@link Intent#toUri(int)}.
+ */
+ public static int getMaxDestinationUriSize() {
+ return MAX_DESTINATION_URI_SIZE;
+ }
+
+ /**
* @hide
*/
public boolean isMultipartDownload() {
@@ -344,7 +318,6 @@ public class DownloadRequest implements Parcelable {
if (version >= 1) {
// Hash the source URI, destination URI, and the app intent
digest.update(sourceUri.toString().getBytes(StandardCharsets.UTF_8));
- digest.update(destinationUri.toString().getBytes(StandardCharsets.UTF_8));
digest.update(serializedResultIntentForApp.getBytes(StandardCharsets.UTF_8));
}
// Add updates for future versions here
@@ -365,13 +338,12 @@ public class DownloadRequest implements Parcelable {
version == request.version &&
Objects.equals(fileServiceId, request.fileServiceId) &&
Objects.equals(sourceUri, request.sourceUri) &&
- Objects.equals(destinationUri, request.destinationUri) &&
Objects.equals(serializedResultIntentForApp, request.serializedResultIntentForApp);
}
@Override
public int hashCode() {
- return Objects.hash(fileServiceId, sourceUri, destinationUri,
+ return Objects.hash(fileServiceId, sourceUri,
subscriptionId, serializedResultIntentForApp, version);
}
}
diff --git a/telephony/java/android/telephony/mbms/DownloadProgressListener.java b/telephony/java/android/telephony/mbms/DownloadStateCallback.java
index d91e9ad24a9a..86920bd3b205 100644
--- a/telephony/java/android/telephony/mbms/DownloadProgressListener.java
+++ b/telephony/java/android/telephony/mbms/DownloadStateCallback.java
@@ -16,18 +16,20 @@
package android.telephony.mbms;
-import android.os.RemoteException;
+import android.telephony.MbmsDownloadSession;
/**
- * A optional listener class used by download clients to track progress.
- * @hide
+ * A optional listener class used by download clients to track progress. Apps should extend this
+ * class and pass an instance into
+ * {@link MbmsDownloadSession#download(DownloadRequest)}
+ *
+ * This is optionally specified when requesting a download and will only be called while the app
+ * is running.
*/
-public class DownloadProgressListener extends IDownloadProgressListener.Stub {
+public class DownloadStateCallback {
+
/**
- * Gives process callbacks for a given DownloadRequest.
- * This is optionally specified when requesting a download and
- * only lives while the app is running - it's unlikely to be useful for
- * downloads far in the future.
+ * Called when the middleware wants to report progress for a file in a {@link DownloadRequest}.
*
* @param request a {@link DownloadRequest}, indicating which download is being referenced.
* @param fileInfo a {@link FileInfo} specifying the file to report progress on. Note that
@@ -40,9 +42,21 @@ public class DownloadProgressListener extends IDownloadProgressListener.Stub {
* @param currentDecodedSize is the number of bytes that have been decoded.
* @param fullDecodedSize is the total number of bytes that make up the final decoded content.
*/
- @Override
- public void progress(DownloadRequest request, FileInfo fileInfo,
+ public void onProgressUpdated(DownloadRequest request, FileInfo fileInfo,
int currentDownloadSize, int fullDownloadSize,
- int currentDecodedSize, int fullDecodedSize) throws RemoteException {
+ int currentDecodedSize, int fullDecodedSize) {
+ }
+
+ /**
+ * Gives download state callbacks for a file in a {@link DownloadRequest}.
+ *
+ * @param request a {@link DownloadRequest}, indicating which download is being referenced.
+ * @param fileInfo a {@link FileInfo} specifying the file to report progress on. Note that
+ * the request may result in many files being downloaded and the client
+ * may not have been able to get a list of them in advance.
+ * @param state The current state of the download.
+ */
+ public void onStateUpdated(DownloadRequest request, FileInfo fileInfo,
+ @MbmsDownloadSession.DownloadStatus int state) {
}
}
diff --git a/telephony/java/android/telephony/mbms/FileInfo.java b/telephony/java/android/telephony/mbms/FileInfo.java
index f97131dda427..0d737b589d06 100644
--- a/telephony/java/android/telephony/mbms/FileInfo.java
+++ b/telephony/java/android/telephony/mbms/FileInfo.java
@@ -16,26 +16,18 @@
package android.telephony.mbms;
+import android.annotation.SystemApi;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
/**
- * A Parcelable class Cell-Broadcast downloadable file information.
- * @hide
+ * Describes a single file that is available over MBMS.
*/
-public class FileInfo implements Parcelable {
+public final class FileInfo implements Parcelable {
- /**
- * The URI into the carriers infrastructure which points to this file.
- * This is used internally but is also one of the few pieces of data about the content that is
- * exposed and may be needed for disambiguation by the application.
- */
private final Uri uri;
- /**
- * The mime type of the content.
- */
private final String mimeType;
public static final Parcelable.Creator<FileInfo> CREATOR =
@@ -53,8 +45,8 @@ public class FileInfo implements Parcelable {
/**
* @hide
- * TODO: systemapi
*/
+ @SystemApi
public FileInfo(Uri uri, String mimeType) {
this.uri = uri;
this.mimeType = mimeType;
@@ -76,10 +68,17 @@ public class FileInfo implements Parcelable {
return 0;
}
+ /**
+ * @return The URI in the carrier's infrastructure which points to this file. Apps should
+ * negotiate the contents of this URI separately with the carrier.
+ */
public Uri getUri() {
return uri;
}
+ /**
+ * @return The MIME type of the file.
+ */
public String getMimeType() {
return mimeType;
}
diff --git a/telephony/java/android/telephony/mbms/FileServiceInfo.java b/telephony/java/android/telephony/mbms/FileServiceInfo.java
index 8afe4d3c5230..d8d7f48a56be 100644
--- a/telephony/java/android/telephony/mbms/FileServiceInfo.java
+++ b/telephony/java/android/telephony/mbms/FileServiceInfo.java
@@ -16,6 +16,7 @@
package android.telephony.mbms;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
@@ -26,13 +27,14 @@ import java.util.Locale;
import java.util.Map;
/**
- * A Parcelable class Cell-Broadcast downloadable file information.
- * @hide
+ * Describes a file service available from the carrier from which files can be downloaded via
+ * cell-broadcast.
*/
-public class FileServiceInfo extends ServiceInfo implements Parcelable {
+public final class FileServiceInfo extends ServiceInfo implements Parcelable {
private final List<FileInfo> files;
- /** @hide TODO: systemapi */
+ /** @hide */
+ @SystemApi
public FileServiceInfo(Map<Locale, String> newNames, String newClassName,
List<Locale> newLocales, String newServiceId, Date start, Date end,
List<FileInfo> newFiles) {
@@ -56,7 +58,7 @@ public class FileServiceInfo extends ServiceInfo implements Parcelable {
FileServiceInfo(Parcel in) {
super(in);
files = new ArrayList<FileInfo>();
- in.readList(files, null);
+ in.readList(files, FileInfo.class.getClassLoader());
}
@Override
@@ -70,8 +72,12 @@ public class FileServiceInfo extends ServiceInfo implements Parcelable {
return 0;
}
+ /**
+ * @return A list of files available from this service. Note that this list may not be
+ * exhaustive -- the middleware may not have information on all files that are available.
+ * Consult the carrier for an authoritative and exhaustive list.
+ */
public List<FileInfo> getFiles() {
return files;
}
-
}
diff --git a/telephony/java/android/telephony/mbms/IDownloadProgressListener.aidl b/telephony/java/android/telephony/mbms/IDownloadStateCallback.aidl
index bb9dc6cfea9f..cebc70d3e884 100755
--- a/telephony/java/android/telephony/mbms/IDownloadProgressListener.aidl
+++ b/telephony/java/android/telephony/mbms/IDownloadStateCallback.aidl
@@ -23,12 +23,15 @@ import android.telephony.mbms.FileInfo;
* The optional interface used by download clients to track progress.
* @hide
*/
-interface IDownloadProgressListener
+interface IDownloadStateCallback
{
/**
* Gives progress callbacks for a given DownloadRequest. Includes a FileInfo
* as the list of files may not have been known at request-time.
*/
- void progress(in DownloadRequest request, in FileInfo fileInfo, int currentDownloadSize,
- int fullDownloadSize, int currentDecodedSize, int fullDecodedSize);
+ void onProgressUpdated(in DownloadRequest request, in FileInfo fileInfo,
+ int currentDownloadSize, int fullDownloadSize,
+ int currentDecodedSize, int fullDecodedSize);
+
+ void onStateUpdated(in DownloadRequest request, in FileInfo fileInfo, int state);
}
diff --git a/telephony/java/android/telephony/mbms/IMbmsDownloadManagerCallback.aidl b/telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl
index ac2f20243896..0d813a7ceea0 100755
--- a/telephony/java/android/telephony/mbms/IMbmsDownloadManagerCallback.aidl
+++ b/telephony/java/android/telephony/mbms/IMbmsDownloadSessionCallback.aidl
@@ -24,11 +24,11 @@ import java.util.List;
* The interface the clients top-level file download listener will satisfy.
* @hide
*/
-oneway interface IMbmsDownloadManagerCallback
+oneway interface IMbmsDownloadSessionCallback
{
- void error(int errorCode, String message);
+ void onError(int errorCode, String message);
- void fileServicesUpdated(in List<FileServiceInfo> services);
+ void onFileServicesUpdated(in List<FileServiceInfo> services);
- void middlewareReady();
+ void onMiddlewareReady();
}
diff --git a/telephony/java/android/telephony/mbms/IMbmsStreamingManagerCallback.aidl b/telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl
index 007aee7cf3f2..0bf0ebc484ea 100755
--- a/telephony/java/android/telephony/mbms/IMbmsStreamingManagerCallback.aidl
+++ b/telephony/java/android/telephony/mbms/IMbmsStreamingSessionCallback.aidl
@@ -24,11 +24,11 @@ import java.util.List;
* The interface the clients top-level streaming listener will satisfy.
* @hide
*/
-oneway interface IMbmsStreamingManagerCallback
+oneway interface IMbmsStreamingSessionCallback
{
- void error(int errorCode, String message);
+ void onError(int errorCode, String message);
- void streamingServicesUpdated(in List<StreamingServiceInfo> services);
+ void onStreamingServicesUpdated(in List<StreamingServiceInfo> services);
- void middlewareReady();
+ void onMiddlewareReady();
}
diff --git a/telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl b/telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl
index 0952fbea881d..164cefb2d5ef 100755
--- a/telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl
+++ b/telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl
@@ -20,9 +20,9 @@ package android.telephony.mbms;
* @hide
*/
oneway interface IStreamingServiceCallback {
- void error(int errorCode, String message);
- void streamStateUpdated(int state, int reason);
- void mediaDescriptionUpdated();
- void broadcastSignalStrengthUpdated(int signalStrength);
- void streamMethodUpdated(int methodType);
+ void onError(int errorCode, String message);
+ void onStreamStateUpdated(int state, int reason);
+ void onMediaDescriptionUpdated();
+ void onBroadcastSignalStrengthUpdated(int signalStrength);
+ void onStreamMethodUpdated(int methodType);
}
diff --git a/telephony/java/android/telephony/mbms/InternalDownloadSessionCallback.java b/telephony/java/android/telephony/mbms/InternalDownloadSessionCallback.java
new file mode 100644
index 000000000000..a7a5958fff56
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/InternalDownloadSessionCallback.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.mbms;
+
+import android.os.Handler;
+import android.os.RemoteException;
+
+import java.util.List;
+
+/** @hide */
+public class InternalDownloadSessionCallback extends IMbmsDownloadSessionCallback.Stub {
+
+ private final Handler mHandler;
+ private final MbmsDownloadSessionCallback mAppCallback;
+ private volatile boolean mIsStopped = false;
+
+ public InternalDownloadSessionCallback(MbmsDownloadSessionCallback appCallback,
+ Handler handler) {
+ mAppCallback = appCallback;
+ mHandler = handler;
+ }
+
+ @Override
+ public void onError(final int errorCode, final String message) throws RemoteException {
+ if (mIsStopped) {
+ return;
+ }
+
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mAppCallback.onError(errorCode, message);
+ }
+ });
+ }
+
+ @Override
+ public void onFileServicesUpdated(final List<FileServiceInfo> services) throws RemoteException {
+ if (mIsStopped) {
+ return;
+ }
+
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mAppCallback.onFileServicesUpdated(services);
+ }
+ });
+ }
+
+ @Override
+ public void onMiddlewareReady() throws RemoteException {
+ if (mIsStopped) {
+ return;
+ }
+
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mAppCallback.onMiddlewareReady();
+ }
+ });
+ }
+
+ public Handler getHandler() {
+ return mHandler;
+ }
+
+ public void stop() {
+ mIsStopped = true;
+ }
+}
diff --git a/telephony/java/android/telephony/mbms/InternalDownloadStateCallback.java b/telephony/java/android/telephony/mbms/InternalDownloadStateCallback.java
new file mode 100644
index 000000000000..8702952cf06b
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/InternalDownloadStateCallback.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.telephony.mbms;
+
+import android.os.Handler;
+import android.os.RemoteException;
+
+/**
+ * @hide
+ */
+public class InternalDownloadStateCallback extends IDownloadStateCallback.Stub {
+ private final Handler mHandler;
+ private final DownloadStateCallback mAppCallback;
+ private volatile boolean mIsStopped = false;
+
+ public InternalDownloadStateCallback(DownloadStateCallback appCallback, Handler handler) {
+ mAppCallback = appCallback;
+ mHandler = handler;
+ }
+
+ @Override
+ public void onProgressUpdated(final DownloadRequest request, final FileInfo fileInfo,
+ final int currentDownloadSize, final int fullDownloadSize, final int currentDecodedSize,
+ final int fullDecodedSize) throws RemoteException {
+ if (mIsStopped) {
+ return;
+ }
+
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mAppCallback.onProgressUpdated(request, fileInfo, currentDownloadSize,
+ fullDownloadSize, currentDecodedSize, fullDecodedSize);
+ }
+ });
+ }
+
+ @Override
+ public void onStateUpdated(final DownloadRequest request, final FileInfo fileInfo,
+ final int state) throws RemoteException {
+ if (mIsStopped) {
+ return;
+ }
+
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mAppCallback.onStateUpdated(request, fileInfo, state);
+ }
+ });
+ }
+
+ public void stop() {
+ mIsStopped = true;
+ }
+}
diff --git a/telephony/java/android/telephony/mbms/InternalStreamingServiceCallback.java b/telephony/java/android/telephony/mbms/InternalStreamingServiceCallback.java
index bb337b271cf0..eb6579cec471 100644
--- a/telephony/java/android/telephony/mbms/InternalStreamingServiceCallback.java
+++ b/telephony/java/android/telephony/mbms/InternalStreamingServiceCallback.java
@@ -23,6 +23,7 @@ import android.os.RemoteException;
public class InternalStreamingServiceCallback extends IStreamingServiceCallback.Stub {
private final StreamingServiceCallback mAppCallback;
private final Handler mHandler;
+ private volatile boolean mIsStopped = false;
public InternalStreamingServiceCallback(StreamingServiceCallback appCallback, Handler handler) {
mAppCallback = appCallback;
@@ -30,7 +31,11 @@ public class InternalStreamingServiceCallback extends IStreamingServiceCallback.
}
@Override
- public void error(int errorCode, String message) throws RemoteException {
+ public void onError(final int errorCode, final String message) throws RemoteException {
+ if (mIsStopped) {
+ return;
+ }
+
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -40,7 +45,11 @@ public class InternalStreamingServiceCallback extends IStreamingServiceCallback.
}
@Override
- public void streamStateUpdated(int state, int reason) throws RemoteException {
+ public void onStreamStateUpdated(final int state, final int reason) throws RemoteException {
+ if (mIsStopped) {
+ return;
+ }
+
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -50,7 +59,11 @@ public class InternalStreamingServiceCallback extends IStreamingServiceCallback.
}
@Override
- public void mediaDescriptionUpdated() throws RemoteException {
+ public void onMediaDescriptionUpdated() throws RemoteException {
+ if (mIsStopped) {
+ return;
+ }
+
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -60,7 +73,11 @@ public class InternalStreamingServiceCallback extends IStreamingServiceCallback.
}
@Override
- public void broadcastSignalStrengthUpdated(int signalStrength) throws RemoteException {
+ public void onBroadcastSignalStrengthUpdated(final int signalStrength) throws RemoteException {
+ if (mIsStopped) {
+ return;
+ }
+
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -70,7 +87,11 @@ public class InternalStreamingServiceCallback extends IStreamingServiceCallback.
}
@Override
- public void streamMethodUpdated(int methodType) throws RemoteException {
+ public void onStreamMethodUpdated(final int methodType) throws RemoteException {
+ if (mIsStopped) {
+ return;
+ }
+
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -78,4 +99,8 @@ public class InternalStreamingServiceCallback extends IStreamingServiceCallback.
}
});
}
+
+ public void stop() {
+ mIsStopped = true;
+ }
}
diff --git a/telephony/java/android/telephony/mbms/InternalStreamingManagerCallback.java b/telephony/java/android/telephony/mbms/InternalStreamingSessionCallback.java
index b52df8c0dd84..d782d12c00d6 100644
--- a/telephony/java/android/telephony/mbms/InternalStreamingManagerCallback.java
+++ b/telephony/java/android/telephony/mbms/InternalStreamingSessionCallback.java
@@ -18,25 +18,27 @@ package android.telephony.mbms;
import android.os.Handler;
import android.os.RemoteException;
-import android.telephony.mbms.IMbmsStreamingManagerCallback;
-import android.telephony.mbms.MbmsStreamingManagerCallback;
-import android.telephony.mbms.StreamingServiceInfo;
import java.util.List;
/** @hide */
-public class InternalStreamingManagerCallback extends IMbmsStreamingManagerCallback.Stub {
+public class InternalStreamingSessionCallback extends IMbmsStreamingSessionCallback.Stub {
private final Handler mHandler;
- private final MbmsStreamingManagerCallback mAppCallback;
+ private final MbmsStreamingSessionCallback mAppCallback;
+ private volatile boolean mIsStopped = false;
- public InternalStreamingManagerCallback(MbmsStreamingManagerCallback appCallback,
+ public InternalStreamingSessionCallback(MbmsStreamingSessionCallback appCallback,
Handler handler) {
mAppCallback = appCallback;
mHandler = handler;
}
@Override
- public void error(int errorCode, String message) throws RemoteException {
+ public void onError(final int errorCode, final String message) throws RemoteException {
+ if (mIsStopped) {
+ return;
+ }
+
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -46,8 +48,12 @@ public class InternalStreamingManagerCallback extends IMbmsStreamingManagerCallb
}
@Override
- public void streamingServicesUpdated(List<StreamingServiceInfo> services)
+ public void onStreamingServicesUpdated(final List<StreamingServiceInfo> services)
throws RemoteException {
+ if (mIsStopped) {
+ return;
+ }
+
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -57,7 +63,11 @@ public class InternalStreamingManagerCallback extends IMbmsStreamingManagerCallb
}
@Override
- public void middlewareReady() throws RemoteException {
+ public void onMiddlewareReady() throws RemoteException {
+ if (mIsStopped) {
+ return;
+ }
+
mHandler.post(new Runnable() {
@Override
public void run() {
@@ -69,4 +79,8 @@ public class InternalStreamingManagerCallback extends IMbmsStreamingManagerCallb
public Handler getHandler() {
return mHandler;
}
+
+ public void stop() {
+ mIsStopped = true;
+ }
}
diff --git a/telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java b/telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java
deleted file mode 100644
index 17291d09215d..000000000000
--- a/telephony/java/android/telephony/mbms/MbmsDownloadManagerCallback.java
+++ /dev/null
@@ -1,64 +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.telephony.mbms;
-
-import android.os.RemoteException;
-import android.telephony.MbmsDownloadManager;
-
-import java.util.List;
-
-/**
- * A Parcelable class with Cell-Broadcast service information.
- * @hide
- */
-public class MbmsDownloadManagerCallback extends IMbmsDownloadManagerCallback.Stub {
-
- @Override
- public void error(int errorCode, String message) throws RemoteException {
- // default implementation empty
- }
-
- /**
- * Called to indicate published File Services have changed.
- *
- * This will only be called after the application has requested
- * a list of file services and specified a service class list
- * of interest AND the results of a subsequent getFileServices
- * call with the same service class list would return different
- * results.
- *
- * @param services a List of FileServiceInfos
- *
- */
- @Override
- public void fileServicesUpdated(List<FileServiceInfo> services) throws RemoteException {
- // default implementation empty
- }
-
- /**
- * Called to indicate that the middleware has been initialized and is ready.
- *
- * Before this method is called, calling any method on an instance of
- * {@link android.telephony.MbmsDownloadManager} will result in an {@link MbmsException}
- * being thrown with error code {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}
- * or {@link MbmsException.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}
- */
- @Override
- public void middlewareReady() throws RemoteException {
- // default implementation empty
- }
-}
diff --git a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
index ba7d120a3b7c..61415b50b2d2 100644
--- a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
+++ b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
@@ -16,6 +16,7 @@
package android.telephony.mbms;
+import android.annotation.SystemApi;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
@@ -24,8 +25,8 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
-import android.telephony.MbmsDownloadManager;
-import android.telephony.mbms.vendor.VendorIntents;
+import android.telephony.MbmsDownloadSession;
+import android.telephony.mbms.vendor.VendorUtils;
import android.util.Log;
import java.io.File;
@@ -35,52 +36,74 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
import java.util.ArrayList;
-import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
/**
- * @hide
+ * The {@link BroadcastReceiver} responsible for handling intents sent from the middleware. Apps
+ * that wish to download using MBMS APIs should declare this class in their AndroidManifest.xml as
+ * follows:
+<pre>{@code
+<receiver
+ android:name="android.telephony.mbms.MbmsDownloadReceiver"
+ android:permission="android.permission.SEND_EMBMS_INTENTS"
+ android:enabled="true"
+ android:exported="true">
+</receiver>}</pre>
*/
public class MbmsDownloadReceiver extends BroadcastReceiver {
+ /** @hide */
public static final String DOWNLOAD_TOKEN_SUFFIX = ".download_token";
+ /** @hide */
public static final String MBMS_FILE_PROVIDER_META_DATA_KEY = "mbms-file-provider-authority";
/**
- * TODO: @SystemApi all these result codes
* Indicates that the requested operation completed without error.
+ * @hide
*/
+ @SystemApi
public static final int RESULT_OK = 0;
/**
* Indicates that the intent sent had an invalid action. This will be the result if
* {@link Intent#getAction()} returns anything other than
- * {@link VendorIntents#ACTION_DOWNLOAD_RESULT_INTERNAL},
- * {@link VendorIntents#ACTION_FILE_DESCRIPTOR_REQUEST}, or
- * {@link VendorIntents#ACTION_CLEANUP}.
+ * {@link VendorUtils#ACTION_DOWNLOAD_RESULT_INTERNAL},
+ * {@link VendorUtils#ACTION_FILE_DESCRIPTOR_REQUEST}, or
+ * {@link VendorUtils#ACTION_CLEANUP}.
* This is a fatal result code and no result extras should be expected.
+ * @hide
*/
+ @SystemApi
public static final int RESULT_INVALID_ACTION = 1;
/**
* Indicates that the intent was missing some required extras.
* This is a fatal result code and no result extras should be expected.
+ * @hide
*/
+ @SystemApi
public static final int RESULT_MALFORMED_INTENT = 2;
/**
- * Indicates that the supplied value for {@link VendorIntents#EXTRA_TEMP_FILE_ROOT}
+ * Indicates that the supplied value for {@link VendorUtils#EXTRA_TEMP_FILE_ROOT}
* does not match what the app has stored.
* This is a fatal result code and no result extras should be expected.
+ * @hide
*/
+ @SystemApi
public static final int RESULT_BAD_TEMP_FILE_ROOT = 3;
/**
* Indicates that the manager was unable to move the completed download to its final location.
* This is a fatal result code and no result extras should be expected.
+ * @hide
*/
+ @SystemApi
public static final int RESULT_DOWNLOAD_FINALIZATION_ERROR = 4;
/**
@@ -88,35 +111,48 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
* descriptors.
* This is a non-fatal result code -- some file descriptors may still be generated, but there
* is no guarantee that they will be the same number as requested.
+ * @hide
*/
+ @SystemApi
public static final int RESULT_TEMP_FILE_GENERATION_ERROR = 5;
+ /**
+ * Indicates that the manager was unable to notify the app of the completed download.
+ * This is a fatal result code and no result extras should be expected.
+ * @hide
+ */
+ @SystemApi
+ public static final int RESULT_APP_NOTIFICATION_ERROR = 6;
+
+
private static final String LOG_TAG = "MbmsDownloadReceiver";
private static final String TEMP_FILE_SUFFIX = ".embms.temp";
- private static final int MAX_TEMP_FILE_RETRIES = 5;
+ private static final String TEMP_FILE_STAGING_LOCATION = "staged_completed_files";
+ private static final int MAX_TEMP_FILE_RETRIES = 5;
private String mFileProviderAuthorityCache = null;
private String mMiddlewarePackageNameCache = null;
+ /** @hide */
@Override
public void onReceive(Context context, Intent intent) {
if (!verifyIntentContents(context, intent)) {
setResultCode(RESULT_MALFORMED_INTENT);
return;
}
- if (!Objects.equals(intent.getStringExtra(VendorIntents.EXTRA_TEMP_FILE_ROOT),
+ if (!Objects.equals(intent.getStringExtra(VendorUtils.EXTRA_TEMP_FILE_ROOT),
MbmsTempFileProvider.getEmbmsTempFileDir(context).getPath())) {
setResultCode(RESULT_BAD_TEMP_FILE_ROOT);
return;
}
- if (VendorIntents.ACTION_DOWNLOAD_RESULT_INTERNAL.equals(intent.getAction())) {
+ if (VendorUtils.ACTION_DOWNLOAD_RESULT_INTERNAL.equals(intent.getAction())) {
moveDownloadedFile(context, intent);
cleanupPostMove(context, intent);
- } else if (VendorIntents.ACTION_FILE_DESCRIPTOR_REQUEST.equals(intent.getAction())) {
+ } else if (VendorUtils.ACTION_FILE_DESCRIPTOR_REQUEST.equals(intent.getAction())) {
generateTempFiles(context, intent);
- } else if (VendorIntents.ACTION_CLEANUP.equals(intent.getAction())) {
+ } else if (VendorUtils.ACTION_CLEANUP.equals(intent.getAction())) {
cleanupTempFiles(context, intent);
} else {
setResultCode(RESULT_INVALID_ACTION);
@@ -124,30 +160,31 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
}
private boolean verifyIntentContents(Context context, Intent intent) {
- if (VendorIntents.ACTION_DOWNLOAD_RESULT_INTERNAL.equals(intent.getAction())) {
- if (!intent.hasExtra(MbmsDownloadManager.EXTRA_RESULT)) {
+ if (VendorUtils.ACTION_DOWNLOAD_RESULT_INTERNAL.equals(intent.getAction())) {
+ if (!intent.hasExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_RESULT)) {
Log.w(LOG_TAG, "Download result did not include a result code. Ignoring.");
return false;
}
- if (!intent.hasExtra(VendorIntents.EXTRA_REQUEST)) {
+ if (!intent.hasExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_REQUEST)) {
Log.w(LOG_TAG, "Download result did not include the associated request. Ignoring.");
return false;
}
- if (!intent.hasExtra(VendorIntents.EXTRA_TEMP_FILE_ROOT)) {
+ if (!intent.hasExtra(VendorUtils.EXTRA_TEMP_FILE_ROOT)) {
Log.w(LOG_TAG, "Download result did not include the temp file root. Ignoring.");
return false;
}
- if (!intent.hasExtra(MbmsDownloadManager.EXTRA_FILE_INFO)) {
+ if (!intent.hasExtra(MbmsDownloadSession.EXTRA_MBMS_FILE_INFO)) {
Log.w(LOG_TAG, "Download result did not include the associated file info. " +
"Ignoring.");
return false;
}
- if (!intent.hasExtra(VendorIntents.EXTRA_FINAL_URI)) {
+ if (!intent.hasExtra(VendorUtils.EXTRA_FINAL_URI)) {
Log.w(LOG_TAG, "Download result did not include the path to the final " +
"temp file. Ignoring.");
return false;
}
- DownloadRequest request = intent.getParcelableExtra(VendorIntents.EXTRA_REQUEST);
+ DownloadRequest request = intent.getParcelableExtra(
+ MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_REQUEST);
String expectedTokenFileName = request.getHash() + DOWNLOAD_TOKEN_SUFFIX;
File expectedTokenFile = new File(
MbmsUtils.getEmbmsTempFileDirForService(context, request.getFileServiceId()),
@@ -157,27 +194,27 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
"Expected " + expectedTokenFile);
return false;
}
- } else if (VendorIntents.ACTION_FILE_DESCRIPTOR_REQUEST.equals(intent.getAction())) {
- if (!intent.hasExtra(VendorIntents.EXTRA_SERVICE_INFO)) {
- Log.w(LOG_TAG, "Temp file request did not include the associated service info." +
+ } else if (VendorUtils.ACTION_FILE_DESCRIPTOR_REQUEST.equals(intent.getAction())) {
+ if (!intent.hasExtra(VendorUtils.EXTRA_SERVICE_ID)) {
+ Log.w(LOG_TAG, "Temp file request did not include the associated service id." +
" Ignoring.");
return false;
}
- if (!intent.hasExtra(VendorIntents.EXTRA_TEMP_FILE_ROOT)) {
+ if (!intent.hasExtra(VendorUtils.EXTRA_TEMP_FILE_ROOT)) {
Log.w(LOG_TAG, "Download result did not include the temp file root. Ignoring.");
return false;
}
- } else if (VendorIntents.ACTION_CLEANUP.equals(intent.getAction())) {
- if (!intent.hasExtra(VendorIntents.EXTRA_SERVICE_INFO)) {
- Log.w(LOG_TAG, "Cleanup request did not include the associated service info." +
+ } else if (VendorUtils.ACTION_CLEANUP.equals(intent.getAction())) {
+ if (!intent.hasExtra(VendorUtils.EXTRA_SERVICE_ID)) {
+ Log.w(LOG_TAG, "Cleanup request did not include the associated service id." +
" Ignoring.");
return false;
}
- if (!intent.hasExtra(VendorIntents.EXTRA_TEMP_FILE_ROOT)) {
+ if (!intent.hasExtra(VendorUtils.EXTRA_TEMP_FILE_ROOT)) {
Log.w(LOG_TAG, "Cleanup request did not include the temp file root. Ignoring.");
return false;
}
- if (!intent.hasExtra(VendorIntents.EXTRA_TEMP_FILES_IN_USE)) {
+ if (!intent.hasExtra(VendorUtils.EXTRA_TEMP_FILES_IN_USE)) {
Log.w(LOG_TAG, "Cleanup request did not include the list of temp files in use. " +
"Ignoring.");
return false;
@@ -187,21 +224,26 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
}
private void moveDownloadedFile(Context context, Intent intent) {
- DownloadRequest request = intent.getParcelableExtra(VendorIntents.EXTRA_REQUEST);
+ DownloadRequest request = intent.getParcelableExtra(
+ MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_REQUEST);
Intent intentForApp = request.getIntentForApp();
+ if (intentForApp == null) {
+ Log.i(LOG_TAG, "Malformed app notification intent");
+ setResultCode(RESULT_APP_NOTIFICATION_ERROR);
+ return;
+ }
- int result = intent.getIntExtra(MbmsDownloadManager.EXTRA_RESULT,
- MbmsDownloadManager.RESULT_CANCELLED);
- intentForApp.putExtra(MbmsDownloadManager.EXTRA_RESULT, result);
+ int result = intent.getIntExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_RESULT,
+ MbmsDownloadSession.RESULT_CANCELLED);
+ intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_RESULT, result);
- if (result != MbmsDownloadManager.RESULT_SUCCESSFUL) {
+ if (result != MbmsDownloadSession.RESULT_SUCCESSFUL) {
Log.i(LOG_TAG, "Download request indicated a failed download. Aborting.");
context.sendBroadcast(intentForApp);
return;
}
- Uri destinationUri = request.getDestinationUri();
- Uri finalTempFile = intent.getParcelableExtra(VendorIntents.EXTRA_FINAL_URI);
+ Uri finalTempFile = intent.getParcelableExtra(VendorUtils.EXTRA_FINAL_URI);
if (!verifyTempFilePath(context, request.getFileServiceId(), finalTempFile)) {
Log.w(LOG_TAG, "Download result specified an invalid temp file " + finalTempFile);
setResultCode(RESULT_DOWNLOAD_FINALIZATION_ERROR);
@@ -209,30 +251,37 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
}
FileInfo completedFileInfo =
- (FileInfo) intent.getParcelableExtra(MbmsDownloadManager.EXTRA_FILE_INFO);
- String relativePath = calculateDestinationFileRelativePath(request, completedFileInfo);
+ (FileInfo) intent.getParcelableExtra(MbmsDownloadSession.EXTRA_MBMS_FILE_INFO);
+ Path stagingDirectory = FileSystems.getDefault().getPath(
+ MbmsTempFileProvider.getEmbmsTempFileDir(context).getPath(),
+ TEMP_FILE_STAGING_LOCATION);
- Uri finalFileLocation = moveTempFile(finalTempFile, destinationUri, relativePath);
- if (finalFileLocation == null) {
+ Uri stagedFileLocation;
+ try {
+ stagedFileLocation = stageTempFile(finalTempFile, stagingDirectory);
+ } catch (IOException e) {
Log.w(LOG_TAG, "Failed to move temp file to final destination");
setResultCode(RESULT_DOWNLOAD_FINALIZATION_ERROR);
return;
}
- intentForApp.putExtra(MbmsDownloadManager.EXTRA_COMPLETED_FILE_URI, finalFileLocation);
- intentForApp.putExtra(MbmsDownloadManager.EXTRA_FILE_INFO, completedFileInfo);
+ intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_COMPLETED_FILE_URI,
+ stagedFileLocation);
+ intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_FILE_INFO, completedFileInfo);
+ intentForApp.putExtra(MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_REQUEST, request);
context.sendBroadcast(intentForApp);
setResultCode(RESULT_OK);
}
private void cleanupPostMove(Context context, Intent intent) {
- DownloadRequest request = intent.getParcelableExtra(VendorIntents.EXTRA_REQUEST);
+ DownloadRequest request = intent.getParcelableExtra(
+ MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_REQUEST);
if (request == null) {
Log.w(LOG_TAG, "Intent does not include a DownloadRequest. Ignoring.");
return;
}
- List<Uri> tempFiles = intent.getParcelableExtra(VendorIntents.EXTRA_TEMP_LIST);
+ List<Uri> tempFiles = intent.getParcelableExtra(VendorUtils.EXTRA_TEMP_LIST);
if (tempFiles == null) {
return;
}
@@ -246,16 +295,15 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
}
private void generateTempFiles(Context context, Intent intent) {
- FileServiceInfo serviceInfo =
- intent.getParcelableExtra(VendorIntents.EXTRA_SERVICE_INFO);
- if (serviceInfo == null) {
- Log.w(LOG_TAG, "Temp file request did not include the associated service info. " +
+ String serviceId = intent.getStringExtra(VendorUtils.EXTRA_SERVICE_ID);
+ if (serviceId == null) {
+ Log.w(LOG_TAG, "Temp file request did not include the associated service id. " +
"Ignoring.");
setResultCode(RESULT_MALFORMED_INTENT);
return;
}
- int fdCount = intent.getIntExtra(VendorIntents.EXTRA_FD_COUNT, 0);
- List<Uri> pausedList = intent.getParcelableExtra(VendorIntents.EXTRA_PAUSED_LIST);
+ int fdCount = intent.getIntExtra(VendorUtils.EXTRA_FD_COUNT, 0);
+ List<Uri> pausedList = intent.getParcelableExtra(VendorUtils.EXTRA_PAUSED_LIST);
if (fdCount == 0 && (pausedList == null || pausedList.size() == 0)) {
Log.i(LOG_TAG, "No temp files actually requested. Ending.");
@@ -265,22 +313,20 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
}
ArrayList<UriPathPair> freshTempFiles =
- generateFreshTempFiles(context, serviceInfo, fdCount);
+ generateFreshTempFiles(context, serviceId, fdCount);
ArrayList<UriPathPair> pausedFiles =
- generateUrisForPausedFiles(context, serviceInfo, pausedList);
+ generateUrisForPausedFiles(context, serviceId, pausedList);
Bundle result = new Bundle();
- result.putParcelableArrayList(VendorIntents.EXTRA_FREE_URI_LIST, freshTempFiles);
- result.putParcelableArrayList(VendorIntents.EXTRA_PAUSED_URI_LIST, pausedFiles);
+ result.putParcelableArrayList(VendorUtils.EXTRA_FREE_URI_LIST, freshTempFiles);
+ result.putParcelableArrayList(VendorUtils.EXTRA_PAUSED_URI_LIST, pausedFiles);
setResultCode(RESULT_OK);
setResultExtras(result);
}
- private ArrayList<UriPathPair> generateFreshTempFiles(Context context,
- FileServiceInfo serviceInfo,
+ private ArrayList<UriPathPair> generateFreshTempFiles(Context context, String serviceId,
int freshFdCount) {
- File tempFileDir = MbmsUtils.getEmbmsTempFileDirForService(context,
- serviceInfo.getServiceId());
+ File tempFileDir = MbmsUtils.getEmbmsTempFileDirForService(context, serviceId);
if (!tempFileDir.exists()) {
tempFileDir.mkdirs();
}
@@ -324,14 +370,14 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
}
private ArrayList<UriPathPair> generateUrisForPausedFiles(Context context,
- FileServiceInfo serviceInfo, List<Uri> pausedFiles) {
+ String serviceId, List<Uri> pausedFiles) {
if (pausedFiles == null) {
return new ArrayList<>(0);
}
ArrayList<UriPathPair> result = new ArrayList<>(pausedFiles.size());
for (Uri fileUri : pausedFiles) {
- if (!verifyTempFilePath(context, serviceInfo.getServiceId(), fileUri)) {
+ if (!verifyTempFilePath(context, serviceId, fileUri)) {
Log.w(LOG_TAG, "Supplied file " + fileUri + " is not a valid temp file to resume");
setResultCode(RESULT_TEMP_FILE_GENERATION_ERROR);
continue;
@@ -353,12 +399,10 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
}
private void cleanupTempFiles(Context context, Intent intent) {
- FileServiceInfo serviceInfo =
- intent.getParcelableExtra(VendorIntents.EXTRA_SERVICE_INFO);
- File tempFileDir = MbmsUtils.getEmbmsTempFileDirForService(context,
- serviceInfo.getServiceId());
+ String serviceId = intent.getStringExtra(VendorUtils.EXTRA_SERVICE_ID);
+ File tempFileDir = MbmsUtils.getEmbmsTempFileDirForService(context, serviceId);
final List<Uri> filesInUse =
- intent.getParcelableArrayListExtra(VendorIntents.EXTRA_TEMP_FILES_IN_USE);
+ intent.getParcelableArrayListExtra(VendorUtils.EXTRA_TEMP_FILES_IN_USE);
File[] filesToDelete = tempFileDir.listFiles(new FileFilter() {
@Override
public boolean accept(File file) {
@@ -385,63 +429,22 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
}
}
- private static String calculateDestinationFileRelativePath(DownloadRequest request,
- FileInfo info) {
- List<String> filePathComponents = info.getUri().getPathSegments();
- List<String> requestPathComponents = request.getSourceUri().getPathSegments();
- Iterator<String> filePathIter = filePathComponents.iterator();
- Iterator<String> requestPathIter = requestPathComponents.iterator();
-
- StringBuilder pathBuilder = new StringBuilder();
- // Iterate through the segments of the carrier's URI to the file, along with the segments
- // of the source URI specified in the download request. The relative path is calculated
- // as the tail of the file's URI that does not match the path segments in the source URI.
- while (filePathIter.hasNext()) {
- String currFilePathComponent = filePathIter.next();
- if (requestPathIter.hasNext()) {
- String requestFilePathComponent = requestPathIter.next();
- if (requestFilePathComponent.equals(currFilePathComponent)) {
- continue;
- }
- }
- pathBuilder.append(currFilePathComponent);
- pathBuilder.append('/');
- }
- // remove the trailing slash
- if (pathBuilder.length() > 0) {
- pathBuilder.deleteCharAt(pathBuilder.length() - 1);
- }
- return pathBuilder.toString();
- }
-
/*
- * Moves a tempfile located at fromPath to a new location at toPath. If
- * toPath is a directory, the destination file will be located at relativePath
- * underneath toPath.
+ * Moves a tempfile located at fromPath to a new location in the staging directory.
*/
- private static Uri moveTempFile(Uri fromPath, Uri toPath, String relativePath) {
+ private static Uri stageTempFile(Uri fromPath, Path stagingDirectory) throws IOException {
if (!ContentResolver.SCHEME_FILE.equals(fromPath.getScheme())) {
Log.w(LOG_TAG, "Moving source uri " + fromPath+ " does not have a file scheme");
return null;
}
- if (!ContentResolver.SCHEME_FILE.equals(toPath.getScheme())) {
- Log.w(LOG_TAG, "Moving destination uri " + toPath + " does not have a file scheme");
- return null;
- }
- File fromFile = new File(fromPath.getSchemeSpecificPart());
- File toFile = new File(toPath.getSchemeSpecificPart());
- if (toFile.isDirectory()) {
- toFile = new File(toFile, relativePath);
+ Path fromFile = FileSystems.getDefault().getPath(fromPath.getPath());
+ if (!Files.isDirectory(stagingDirectory)) {
+ Files.createDirectory(stagingDirectory);
}
- toFile.getParentFile().mkdirs();
+ Path result = Files.move(fromFile, stagingDirectory.resolve(fromFile.getFileName()));
- if (fromFile.renameTo(toFile)) {
- return Uri.fromFile(toFile);
- } else if (manualMove(fromFile, toFile)) {
- return Uri.fromFile(toFile);
- }
- return null;
+ return Uri.fromFile(result.toFile());
}
private static boolean verifyTempFilePath(Context context, String serviceId,
@@ -493,7 +496,7 @@ public class MbmsDownloadReceiver extends BroadcastReceiver {
private String getMiddlewarePackageCached(Context context) {
if (mMiddlewarePackageNameCache == null) {
mMiddlewarePackageNameCache = MbmsUtils.getMiddlewareServiceInfo(context,
- MbmsDownloadManager.MBMS_DOWNLOAD_SERVICE_ACTION).packageName;
+ MbmsDownloadSession.MBMS_DOWNLOAD_SERVICE_ACTION).packageName;
}
return mMiddlewarePackageNameCache;
}
diff --git a/telephony/java/android/telephony/mbms/MbmsDownloadSessionCallback.java b/telephony/java/android/telephony/mbms/MbmsDownloadSessionCallback.java
new file mode 100644
index 000000000000..77dea6f309e5
--- /dev/null
+++ b/telephony/java/android/telephony/mbms/MbmsDownloadSessionCallback.java
@@ -0,0 +1,66 @@
+/*
+ * 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.telephony.mbms;
+
+import android.telephony.MbmsDownloadSession;
+
+import java.util.List;
+
+/**
+ * A callback class that apps should use to receive information on file downloads over
+ * cell-broadcast.
+ */
+public class MbmsDownloadSessionCallback {
+
+ /**
+ * Indicates that the middleware has encountered an asynchronous error.
+ * @param errorCode Any error code listed in {@link MbmsErrors}
+ * @param message A message, intended for debugging purposes, describing the error in further
+ * detail.
+ */
+ public void onError(int errorCode, String message) {
+ // default implementation empty
+ }
+
+ /**
+ * Called to indicate published File Services have changed.
+ *
+ * This will only be called after the application has requested a list of file services and
+ * specified a service class list of interest via
+ * {@link MbmsDownloadSession#requestUpdateFileServices(List)}. If there are subsequent calls to
+ * {@link MbmsDownloadSession#requestUpdateFileServices(List)},
+ * this method may not be called again if
+ * the list of service classes would remain the same.
+ *
+ * @param services The most recently updated list of available file services.
+ */
+ public void onFileServicesUpdated(List<FileServiceInfo> services) {
+ // default implementation empty
+ }
+
+ /**
+ * Called to indicate that the middleware has been initialized and is ready.
+ *
+ * Before this method is called, calling any method on an instance of
+ * {@link MbmsDownloadSession} will result in an {@link IllegalStateException}
+ * being thrown or {@link #onError(int, String)} being called with error code
+ * {@link MbmsErrors.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}
+ */
+ public void onMiddlewareReady() {
+ // default implementation empty
+ }
+}
diff --git a/telephony/java/android/telephony/mbms/MbmsException.java b/telephony/java/android/telephony/mbms/MbmsErrors.java
index 6de5a18ebaf5..af0af24c3619 100644
--- a/telephony/java/android/telephony/mbms/MbmsException.java
+++ b/telephony/java/android/telephony/mbms/MbmsErrors.java
@@ -16,7 +16,9 @@
package android.telephony.mbms;
-public class MbmsException extends Exception {
+import android.telephony.MbmsStreamingSession;
+
+public class MbmsErrors {
/** Indicates that the operation was successful. */
public static final int SUCCESS = 0;
@@ -30,8 +32,8 @@ public class MbmsException extends Exception {
/**
* Indicates that the app attempted to perform an operation on an instance of
- * TODO: link android.telephony.MbmsDownloadManager or
- * {@link android.telephony.MbmsStreamingManager} without being bound to the middleware.
+ * {@link android.telephony.MbmsDownloadSession} or
+ * {@link MbmsStreamingSession} without being bound to the middleware.
*/
public static final int ERROR_MIDDLEWARE_NOT_BOUND = 2;
@@ -46,8 +48,7 @@ public class MbmsException extends Exception {
private InitializationErrors() {}
/**
* Indicates that the app tried to create more than one instance each of
- * {@link android.telephony.MbmsStreamingManager} or
- * TODO: link android.telephony.MbmsDownloadManager
+ * {@link MbmsStreamingSession} or {@link android.telephony.MbmsDownloadSession}.
*/
public static final int ERROR_DUPLICATE_INITIALIZE = 101;
/** Indicates that the app is not authorized to access media via MBMS.*/
@@ -64,8 +65,8 @@ public class MbmsException extends Exception {
private GeneralErrors() {}
/**
* Indicates that the app attempted to perform an operation before receiving notification
- * that the middleware is ready via {@link MbmsStreamingManagerCallback#onMiddlewareReady()}
- * or TODO: link MbmsDownloadManagerCallback#middlewareReady
+ * that the middleware is ready via {@link MbmsStreamingSessionCallback#onMiddlewareReady()}
+ * or {@link MbmsDownloadSessionCallback#onMiddlewareReady()}.
*/
public static final int ERROR_MIDDLEWARE_NOT_YET_READY = 201;
/**
@@ -107,7 +108,7 @@ public class MbmsException extends Exception {
/**
* Indicates that the app called
- * {@link android.telephony.MbmsStreamingManager#startStreaming(
+ * {@link MbmsStreamingSession#startStreaming(
* StreamingServiceInfo, StreamingServiceCallback, android.os.Handler)}
* more than once for the same {@link StreamingServiceInfo}.
*/
@@ -116,10 +117,9 @@ public class MbmsException extends Exception {
/**
* Indicates the errors that are applicable only to the file-download use-case
- * TODO: unhide
- * @hide
*/
public static class DownloadErrors {
+ private DownloadErrors() { }
/**
* Indicates that the app is not allowed to change the temp file root at this time due to
* outstanding download requests.
@@ -130,15 +130,5 @@ public class MbmsException extends Exception {
public static final int ERROR_UNKNOWN_DOWNLOAD_REQUEST = 402;
}
- private final int mErrorCode;
-
- /** @hide */
- public MbmsException(int errorCode) {
- super();
- mErrorCode = errorCode;
- }
-
- public int getErrorCode() {
- return mErrorCode;
- }
+ private MbmsErrors() {}
}
diff --git a/telephony/java/android/telephony/mbms/MbmsStreamingManagerCallback.java b/telephony/java/android/telephony/mbms/MbmsStreamingSessionCallback.java
index b31ffa72781f..5c130a09773e 100644
--- a/telephony/java/android/telephony/mbms/MbmsStreamingManagerCallback.java
+++ b/telephony/java/android/telephony/mbms/MbmsStreamingSessionCallback.java
@@ -16,25 +16,26 @@
package android.telephony.mbms;
+import android.annotation.Nullable;
import android.content.Context;
-import android.os.RemoteException;
-import android.telephony.MbmsStreamingManager;
+import android.os.Handler;
+import android.telephony.MbmsStreamingSession;
import java.util.List;
/**
* A callback class that is used to receive information from the middleware on MBMS streaming
* services. An instance of this object should be passed into
- * {@link android.telephony.MbmsStreamingManager#create(Context, MbmsStreamingManagerCallback)}.
+ * {@link MbmsStreamingSession#create(Context, MbmsStreamingSessionCallback, int, Handler)}.
*/
-public class MbmsStreamingManagerCallback {
+public class MbmsStreamingSessionCallback {
/**
* Called by the middleware when it has detected an error condition. The possible error codes
- * are listed in {@link MbmsException}.
+ * are listed in {@link MbmsErrors}.
* @param errorCode The error code.
* @param message A human-readable message generated by the middleware for debugging purposes.
*/
- public void onError(int errorCode, String message) {
+ public void onError(int errorCode, @Nullable String message) {
// default implementation empty
}
@@ -47,8 +48,7 @@ public class MbmsStreamingManagerCallback {
* call with the same service class list would return different
* results.
*
- * @param services a List of StreamingServiceInfos
- *
+ * @param services The list of available services.
*/
public void onStreamingServicesUpdated(List<StreamingServiceInfo> services) {
// default implementation empty
@@ -58,9 +58,9 @@ public class MbmsStreamingManagerCallback {
* Called to indicate that the middleware has been initialized and is ready.
*
* Before this method is called, calling any method on an instance of
- * {@link android.telephony.MbmsStreamingManager} will result in an {@link MbmsException}
- * being thrown with error code {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}
- * or {@link MbmsException.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}
+ * {@link MbmsStreamingSession} will result in an {@link IllegalStateException} or an error
+ * delivered via {@link #onError(int, String)} with error code
+ * {@link MbmsErrors.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}.
*/
public void onMiddlewareReady() {
// default implementation empty
diff --git a/telephony/java/android/telephony/mbms/MbmsTempFileProvider.java b/telephony/java/android/telephony/mbms/MbmsTempFileProvider.java
index c4d033bf2886..689becd7169a 100644
--- a/telephony/java/android/telephony/mbms/MbmsTempFileProvider.java
+++ b/telephony/java/android/telephony/mbms/MbmsTempFileProvider.java
@@ -23,12 +23,11 @@ import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.SharedPreferences;
-import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.database.Cursor;
import android.net.Uri;
-import android.os.Bundle;
import android.os.ParcelFileDescriptor;
+import android.telephony.MbmsDownloadSession;
import java.io.File;
import java.io.FileNotFoundException;
@@ -39,7 +38,6 @@ import java.util.Objects;
* @hide
*/
public class MbmsTempFileProvider extends ContentProvider {
- public static final String DEFAULT_TOP_LEVEL_TEMP_DIRECTORY = "androidMbmsTempFileRoot";
public static final String TEMP_FILE_ROOT_PREF_FILE_NAME = "MbmsTempFileRootPrefs";
public static final String TEMP_FILE_ROOT_PREF_NAME = "mbms_temp_file_root";
@@ -182,8 +180,8 @@ public class MbmsTempFileProvider extends ContentProvider {
if (storedTempFileRoot != null) {
return new File(storedTempFileRoot).getCanonicalFile();
} else {
- return new File(context.getFilesDir(), DEFAULT_TOP_LEVEL_TEMP_DIRECTORY)
- .getCanonicalFile();
+ return new File(context.getFilesDir(),
+ MbmsDownloadSession.DEFAULT_TOP_LEVEL_TEMP_DIRECTORY).getCanonicalFile();
}
} catch (IOException e) {
throw new RuntimeException("Unable to canonicalize temp file root path " + e);
diff --git a/telephony/java/android/telephony/mbms/MbmsUtils.java b/telephony/java/android/telephony/mbms/MbmsUtils.java
index 4b913f825231..d38d8a712c73 100644
--- a/telephony/java/android/telephony/mbms/MbmsUtils.java
+++ b/telephony/java/android/telephony/mbms/MbmsUtils.java
@@ -68,19 +68,20 @@ public class MbmsUtils {
return downloadServices.get(0).serviceInfo;
}
- public static void startBinding(Context context, String serviceAction,
- ServiceConnection serviceConnection) throws MbmsException {
+ public static int startBinding(Context context, String serviceAction,
+ ServiceConnection serviceConnection) {
Intent bindIntent = new Intent();
ServiceInfo mbmsServiceInfo =
MbmsUtils.getMiddlewareServiceInfo(context, serviceAction);
if (mbmsServiceInfo == null) {
- throw new MbmsException(MbmsException.ERROR_NO_UNIQUE_MIDDLEWARE);
+ return MbmsErrors.ERROR_NO_UNIQUE_MIDDLEWARE;
}
bindIntent.setComponent(MbmsUtils.toComponentName(mbmsServiceInfo));
context.bindService(bindIntent, serviceConnection, Context.BIND_AUTO_CREATE);
+ return MbmsErrors.SUCCESS;
}
/**
diff --git a/telephony/java/android/telephony/mbms/ServiceInfo.java b/telephony/java/android/telephony/mbms/ServiceInfo.java
index c01604b0a1bf..9a01ed00e0cd 100644
--- a/telephony/java/android/telephony/mbms/ServiceInfo.java
+++ b/telephony/java/android/telephony/mbms/ServiceInfo.java
@@ -16,6 +16,8 @@
package android.telephony.mbms;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -26,12 +28,13 @@ import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
/**
* Describes a cell-broadcast service. This class should not be instantiated directly -- use
- * {@link StreamingServiceInfo} or FileServiceInfo TODO: add link once that's unhidden
+ * {@link StreamingServiceInfo} or {@link FileServiceInfo}
*/
public class ServiceInfo {
// arbitrary limit on the number of locale -> name pairs we support
@@ -58,6 +61,13 @@ public class ServiceInfo {
if (newLocales.size() > MAP_LIMIT) {
throw new RuntimeException("bad locales length " + newLocales.size());
}
+
+ for (Locale l : newLocales) {
+ if (!newNames.containsKey(l)) {
+ throw new IllegalArgumentException("A name must be provided for each locale");
+ }
+ }
+
names = new HashMap(newNames.size());
names.putAll(newNames);
className = newClassName;
@@ -114,16 +124,25 @@ public class ServiceInfo {
}
/**
- * User displayable names listed by language. Do not modify the map returned from this method.
+ * Get the user-displayable name for this cell-broadcast service corresponding to the
+ * provided {@link Locale}.
+ * @param locale The {@link Locale} in which you want the name of the service. This must be a
+ * value from the list returned by {@link #getLocales()} -- an
+ * {@link java.util.NoSuchElementException} may be thrown otherwise.
+ * @return The {@link CharSequence} providing the name of the service in the given
+ * {@link Locale}
*/
- public Map<Locale, String> getNames() {
- return names;
+ public @NonNull CharSequence getNameForLocale(@NonNull Locale locale) {
+ if (!names.containsKey(locale)) {
+ throw new NoSuchElementException("Locale not supported");
+ }
+ return names.get(locale);
}
/**
* The class name for this service - used to categorize and filter
*/
- public String getClassName() {
+ public String getServiceClassName() {
return className;
}
diff --git a/telephony/java/android/telephony/mbms/StreamingService.java b/telephony/java/android/telephony/mbms/StreamingService.java
index 1d66bac0d06c..ec9134a4b855 100644
--- a/telephony/java/android/telephony/mbms/StreamingService.java
+++ b/telephony/java/android/telephony/mbms/StreamingService.java
@@ -17,8 +17,10 @@
package android.telephony.mbms;
import android.annotation.IntDef;
+import android.annotation.Nullable;
import android.net.Uri;
import android.os.RemoteException;
+import android.telephony.MbmsStreamingSession;
import android.telephony.mbms.vendor.IMbmsStreamingService;
import android.util.Log;
@@ -27,7 +29,7 @@ import java.lang.annotation.RetentionPolicy;
/**
* Class used to represent a single MBMS stream. After a stream has been started with
- * {@link android.telephony.MbmsStreamingManager#startStreaming(StreamingServiceInfo,
+ * {@link MbmsStreamingSession#startStreaming(StreamingServiceInfo,
* StreamingServiceCallback, android.os.Handler)},
* this class is used to hold information about the stream and control it.
*/
@@ -63,7 +65,7 @@ public class StreamingService {
/**
* State changed due to a call to {@link #stopStreaming()} or
- * {@link android.telephony.MbmsStreamingManager#startStreaming(StreamingServiceInfo,
+ * {@link MbmsStreamingSession#startStreaming(StreamingServiceInfo,
* StreamingServiceCallback, android.os.Handler)}
*/
public static final int REASON_BY_USER_REQUEST = 1;
@@ -101,6 +103,7 @@ public class StreamingService {
public final static int UNICAST_METHOD = 2;
private final int mSubscriptionId;
+ private final MbmsStreamingSession mParentSession;
private final StreamingServiceInfo mServiceInfo;
private final InternalStreamingServiceCallback mCallback;
@@ -111,25 +114,25 @@ public class StreamingService {
*/
public StreamingService(int subscriptionId,
IMbmsStreamingService service,
+ MbmsStreamingSession session,
StreamingServiceInfo streamingServiceInfo,
InternalStreamingServiceCallback callback) {
mSubscriptionId = subscriptionId;
+ mParentSession = session;
mService = service;
mServiceInfo = streamingServiceInfo;
mCallback = callback;
}
/**
- * Retreive the Uri used to play this stream.
+ * Retrieve the Uri used to play this stream.
*
- * This may throw a {@link MbmsException} with the error code
- * {@link MbmsException#ERROR_MIDDLEWARE_LOST}
+ * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}.
*
- * May also throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
- *
- * @return The {@link Uri} to pass to the streaming client.
+ * @return The {@link Uri} to pass to the streaming client, or {@code null} if an error
+ * occurred.
*/
- public Uri getPlaybackUri() throws MbmsException {
+ public @Nullable Uri getPlaybackUri() {
if (mService == null) {
throw new IllegalStateException("No streaming service attached");
}
@@ -139,25 +142,26 @@ public class StreamingService {
} catch (RemoteException e) {
Log.w(LOG_TAG, "Remote process died");
mService = null;
- throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
+ mParentSession.onStreamingServiceStopped(this);
+ sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+ return null;
}
}
/**
- * Retreive the info for this StreamingService.
+ * Retrieve the {@link StreamingServiceInfo} corresponding to this stream.
*/
public StreamingServiceInfo getInfo() {
return mServiceInfo;
}
/**
- * Stop streaming this service.
- * This may throw a {@link MbmsException} with the error code
- * {@link MbmsException#ERROR_MIDDLEWARE_LOST}
+ * Stop streaming this service. Further operations on this object will fail with an
+ * {@link IllegalStateException}.
*
- * May also throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
+ * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
*/
- public void stopStreaming() throws MbmsException {
+ public void stopStreaming() {
if (mService == null) {
throw new IllegalStateException("No streaming service attached");
}
@@ -167,32 +171,22 @@ public class StreamingService {
} catch (RemoteException e) {
Log.w(LOG_TAG, "Remote process died");
mService = null;
- throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
+ sendErrorToApp(MbmsErrors.ERROR_MIDDLEWARE_LOST, null);
+ } finally {
+ mParentSession.onStreamingServiceStopped(this);
}
}
- /**
- * Disposes of this stream. Further operations on this object will fail with an
- * {@link IllegalStateException}.
- *
- * This may throw a {@link MbmsException} with the error code
- * {@link MbmsException#ERROR_MIDDLEWARE_LOST}
- * May also throw an {@link IllegalStateException}
- */
- public void dispose() throws MbmsException {
- if (mService == null) {
- throw new IllegalStateException("No streaming service attached");
- }
+ /** @hide */
+ public InternalStreamingServiceCallback getCallback() {
+ return mCallback;
+ }
+ private void sendErrorToApp(int errorCode, String message) {
try {
- mService.disposeStream(mSubscriptionId, mServiceInfo.getServiceId());
+ mCallback.onError(errorCode, message);
} catch (RemoteException e) {
- Log.w(LOG_TAG, "Remote process died");
- throw new MbmsException(MbmsException.ERROR_MIDDLEWARE_LOST);
- } catch (IllegalArgumentException e) {
- throw new IllegalStateException("StreamingService state inconsistent with middleware");
- } finally {
- mService = null;
+ // Ignore, should not happen locally.
}
}
}
diff --git a/telephony/java/android/telephony/mbms/StreamingServiceCallback.java b/telephony/java/android/telephony/mbms/StreamingServiceCallback.java
index b72c71572b60..09038244a16a 100644
--- a/telephony/java/android/telephony/mbms/StreamingServiceCallback.java
+++ b/telephony/java/android/telephony/mbms/StreamingServiceCallback.java
@@ -16,6 +16,8 @@
package android.telephony.mbms;
+import android.annotation.Nullable;
+
/**
* A callback class for use when the application is actively streaming content. The middleware
* will provide updates on the status of the stream via this callback.
@@ -33,11 +35,11 @@ public class StreamingServiceCallback {
/**
* Called by the middleware when it has detected an error condition in this stream. The
- * possible error codes are listed in {@link MbmsException}.
+ * possible error codes are listed in {@link MbmsErrors}.
* @param errorCode The error code.
* @param message A human-readable message generated by the middleware for debugging purposes.
*/
- public void onError(int errorCode, String message) {
+ public void onError(int errorCode, @Nullable String message) {
// default implementation empty
}
diff --git a/telephony/java/android/telephony/mbms/UriPathPair.java b/telephony/java/android/telephony/mbms/UriPathPair.java
index 7acc270ed729..187e9eedab48 100644
--- a/telephony/java/android/telephony/mbms/UriPathPair.java
+++ b/telephony/java/android/telephony/mbms/UriPathPair.java
@@ -16,13 +16,20 @@
package android.telephony.mbms;
+import android.annotation.SystemApi;
import android.content.ContentResolver;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
+import android.telephony.mbms.vendor.VendorUtils;
-/** @hide */
-public class UriPathPair implements Parcelable {
+/**
+ * Wrapper for a pair of {@link Uri}s that describe a temp file used by the middleware to
+ * download files via cell-broadcast.
+ * @hide
+ */
+@SystemApi
+public final class UriPathPair implements Parcelable {
private final Uri mFilePathUri;
private final Uri mContentUri;
@@ -40,7 +47,7 @@ public class UriPathPair implements Parcelable {
}
/** @hide */
- protected UriPathPair(Parcel in) {
+ private UriPathPair(Parcel in) {
mFilePathUri = in.readParcelable(Uri.class.getClassLoader());
mContentUri = in.readParcelable(Uri.class.getClassLoader());
}
@@ -57,12 +64,23 @@ public class UriPathPair implements Parcelable {
}
};
- /** future systemapi */
+ /**
+ * Returns the file-path {@link Uri}. This has scheme {@code file} and points to the actual
+ * location on disk where the temp file resides. Use this when sending {@link Uri}s back to the
+ * app in the intents in {@link VendorUtils}.
+ * @return A {@code file} {@link Uri}.
+ */
public Uri getFilePathUri() {
return mFilePathUri;
}
- /** future systemapi */
+ /**
+ * Returns the content {@link Uri} that may be used with
+ * {@link ContentResolver#openFileDescriptor(Uri, String)} to obtain a
+ * {@link android.os.ParcelFileDescriptor} to a temp file to write to. This {@link Uri} will
+ * expire if the middleware process dies.
+ * @return A {@code content} {@link Uri}
+ */
public Uri getContentUri() {
return mContentUri;
}
diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
index dfcc5f7c8793..ed5e8268fc77 100755
--- a/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
+++ b/telephony/java/android/telephony/mbms/vendor/IMbmsDownloadService.aidl
@@ -20,21 +20,26 @@ import android.app.PendingIntent;
import android.net.Uri;
import android.telephony.mbms.DownloadRequest;
import android.telephony.mbms.FileInfo;
-import android.telephony.mbms.IMbmsDownloadManagerCallback;
-import android.telephony.mbms.IDownloadProgressListener;
+import android.telephony.mbms.IMbmsDownloadSessionCallback;
+import android.telephony.mbms.IDownloadStateCallback;
/**
* @hide
*/
interface IMbmsDownloadService
{
- int initialize(int subId, IMbmsDownloadManagerCallback listener);
+ int initialize(int subId, IMbmsDownloadSessionCallback listener);
- int getFileServices(int subId, in List<String> serviceClasses);
+ int requestUpdateFileServices(int subId, in List<String> serviceClasses);
int setTempFileRootDirectory(int subId, String rootDirectoryPath);
- int download(in DownloadRequest downloadRequest, IDownloadProgressListener listener);
+ int download(in DownloadRequest downloadRequest);
+
+ int registerStateCallback(in DownloadRequest downloadRequest, IDownloadStateCallback listener);
+
+ int unregisterStateCallback(in DownloadRequest downloadRequest,
+ IDownloadStateCallback listener);
List<DownloadRequest> listPendingDownloads(int subscriptionId);
diff --git a/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl b/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl
index 4dd42924ab05..c90ffc7726e4 100755
--- a/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl
+++ b/telephony/java/android/telephony/mbms/vendor/IMbmsStreamingService.aidl
@@ -17,7 +17,7 @@
package android.telephony.mbms.vendor;
import android.net.Uri;
-import android.telephony.mbms.IMbmsStreamingManagerCallback;
+import android.telephony.mbms.IMbmsStreamingSessionCallback;
import android.telephony.mbms.IStreamingServiceCallback;
import android.telephony.mbms.StreamingServiceInfo;
@@ -26,18 +26,16 @@ import android.telephony.mbms.StreamingServiceInfo;
*/
interface IMbmsStreamingService
{
- int initialize(IMbmsStreamingManagerCallback listener, int subId);
+ int initialize(IMbmsStreamingSessionCallback callback, int subId);
- int getStreamingServices(int subId, in List<String> serviceClasses);
+ int requestUpdateStreamingServices(int subId, in List<String> serviceClasses);
int startStreaming(int subId, String serviceId,
- IStreamingServiceCallback listener);
+ IStreamingServiceCallback callback);
Uri getPlaybackUri(int subId, String serviceId);
void stopStreaming(int subId, String serviceId);
- void disposeStream(int subId, String serviceId);
-
void dispose(int subId);
}
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
index 71713d013f97..d845a57b946a 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsDownloadServiceBase.java
@@ -17,40 +17,50 @@
package android.telephony.mbms.vendor;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.IBinder;
import android.os.RemoteException;
-import android.telephony.mbms.DownloadProgressListener;
+import android.telephony.MbmsDownloadSession;
import android.telephony.mbms.DownloadRequest;
+import android.telephony.mbms.DownloadStateCallback;
import android.telephony.mbms.FileInfo;
import android.telephony.mbms.FileServiceInfo;
-import android.telephony.mbms.IDownloadProgressListener;
-import android.telephony.mbms.IMbmsDownloadManagerCallback;
-import android.telephony.mbms.MbmsDownloadManagerCallback;
-import android.telephony.mbms.MbmsException;
+import android.telephony.mbms.IDownloadStateCallback;
+import android.telephony.mbms.IMbmsDownloadSessionCallback;
+import android.telephony.mbms.MbmsDownloadSessionCallback;
+import android.telephony.mbms.MbmsErrors;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
/**
- * Base class for MbmsDownloadService. The middleware should extend this base class rather than
- * the aidl stub for compatibility
+ * Base class for MbmsDownloadService. The middleware should return an instance of this object from
+ * its {@link android.app.Service#onBind(Intent)} method.
* @hide
- * TODO: future systemapi
*/
+@SystemApi
public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
+ private final Map<IBinder, DownloadStateCallback> mDownloadCallbackBinderMap = new HashMap<>();
+ private final Map<IBinder, DeathRecipient> mDownloadCallbackDeathRecipients = new HashMap<>();
+
/**
* Initialize the download service for this app and subId, registering the listener.
*
* May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}, which
* will be intercepted and passed to the app as
- * {@link android.telephony.mbms.MbmsException.InitializationErrors#ERROR_UNABLE_TO_INITIALIZE}
+ * {@link MbmsErrors.InitializationErrors#ERROR_UNABLE_TO_INITIALIZE}
*
- * May return any value from {@link android.telephony.mbms.MbmsException.InitializationErrors}
- * or {@link MbmsException#SUCCESS}. Non-successful error codes will be passed to the app via
- * {@link IMbmsDownloadManagerCallback#error(int, String)}.
+ * May return any value from {@link MbmsErrors.InitializationErrors}
+ * or {@link MbmsErrors#SUCCESS}. Non-successful error codes will be passed to the app via
+ * {@link IMbmsDownloadSessionCallback#onError(int, String)}.
*
* @param callback The callback to use to communicate with the app.
* @param subscriptionId The subscription ID to use.
*/
- public int initialize(int subscriptionId, MbmsDownloadManagerCallback callback)
+ public int initialize(int subscriptionId, MbmsDownloadSessionCallback callback)
throws RemoteException {
return 0;
}
@@ -60,22 +70,42 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
* @hide
*/
@Override
- public final int initialize(int subscriptionId,
- final IMbmsDownloadManagerCallback callback) throws RemoteException {
- return initialize(subscriptionId, new MbmsDownloadManagerCallback() {
+ public final int initialize(final int subscriptionId,
+ final IMbmsDownloadSessionCallback callback) throws RemoteException {
+ final int uid = Binder.getCallingUid();
+ callback.asBinder().linkToDeath(new DeathRecipient() {
+ @Override
+ public void binderDied() {
+ onAppCallbackDied(uid, subscriptionId);
+ }
+ }, 0);
+
+ return initialize(subscriptionId, new MbmsDownloadSessionCallback() {
@Override
- public void error(int errorCode, String message) throws RemoteException {
- callback.error(errorCode, message);
+ public void onError(int errorCode, String message) {
+ try {
+ callback.onError(errorCode, message);
+ } catch (RemoteException e) {
+ onAppCallbackDied(uid, subscriptionId);
+ }
}
@Override
- public void fileServicesUpdated(List<FileServiceInfo> services) throws RemoteException {
- callback.fileServicesUpdated(services);
+ public void onFileServicesUpdated(List<FileServiceInfo> services) {
+ try {
+ callback.onFileServicesUpdated(services);
+ } catch (RemoteException e) {
+ onAppCallbackDied(uid, subscriptionId);
+ }
}
@Override
- public void middlewareReady() throws RemoteException {
- callback.middlewareReady();
+ public void onMiddlewareReady() {
+ try {
+ callback.onMiddlewareReady();
+ } catch (RemoteException e) {
+ onAppCallbackDied(uid, subscriptionId);
+ }
}
});
}
@@ -83,7 +113,7 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
/**
* Registers serviceClasses of interest with the appName/subId key.
* Starts async fetching data on streaming services of matching classes to be reported
- * later via {@link IMbmsDownloadManagerCallback#fileServicesUpdated(List)}
+ * later via {@link IMbmsDownloadSessionCallback#onFileServicesUpdated(List)}
*
* Note that subsequent calls with the same uid and subId will replace
* the service class list.
@@ -94,11 +124,11 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
* @param serviceClasses The service classes that the app wishes to get info on. The strings
* may contain arbitrary data as negotiated between the app and the
* carrier.
- * @return One of {@link MbmsException#SUCCESS} or
- * {@link MbmsException.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY},
+ * @return One of {@link MbmsErrors#SUCCESS} or
+ * {@link MbmsErrors.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY},
*/
@Override
- public int getFileServices(int subscriptionId, List<String> serviceClasses)
+ public int requestUpdateFileServices(int subscriptionId, List<String> serviceClasses)
throws RemoteException {
return 0;
}
@@ -110,13 +140,13 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
*
* If the calling app (as identified by the calling UID) currently has any pending download
* requests that have not been canceled, the middleware must return
- * {@link MbmsException.DownloadErrors#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT} here.
+ * {@link MbmsErrors.DownloadErrors#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT} here.
*
* @param subscriptionId The subscription id the download is operating under.
* @param rootDirectoryPath The path to the app's temp file root directory.
- * @return {@link MbmsException#SUCCESS},
- * {@link MbmsException.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY} or
- * {@link MbmsException.DownloadErrors#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT}
+ * @return {@link MbmsErrors#SUCCESS},
+ * {@link MbmsErrors.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY} or
+ * {@link MbmsErrors.DownloadErrors#ERROR_CANNOT_CHANGE_TEMP_FILE_ROOT}
*/
@Override
public int setTempFileRootDirectory(int subscriptionId,
@@ -132,12 +162,32 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
* this is not the case, an {@link IllegalStateException} may be thrown.
*
* @param downloadRequest An object describing the set of files to be downloaded.
- * @param listener A listener through which the middleware can provide progress updates to
- * the app while both are still running.
- * @return Any error from {@link android.telephony.mbms.MbmsException.GeneralErrors}
- * or {@link MbmsException#SUCCESS}
+ * @return Any error from {@link MbmsErrors.GeneralErrors}
+ * or {@link MbmsErrors#SUCCESS}
*/
- public int download(DownloadRequest downloadRequest, DownloadProgressListener listener) {
+ @Override
+ public int download(DownloadRequest downloadRequest) throws RemoteException {
+ return 0;
+ }
+
+ /**
+ * Registers a download state callbacks for the provided {@link DownloadRequest}.
+ *
+ * This method is called by the app when it wants to request updates on the progress or
+ * status of the download.
+ *
+ * If the middleware is not aware of a download having been requested with the provided
+ *
+ * {@link DownloadRequest} in the past,
+ * {@link MbmsErrors.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}
+ * must be returned.
+ *
+ * @param downloadRequest The {@link DownloadRequest} that was used to initiate the download
+ * for which progress updates are being requested.
+ * @param callback The callback object to use.
+ */
+ public int registerStateCallback(DownloadRequest downloadRequest,
+ DownloadStateCallback callback) throws RemoteException {
return 0;
}
@@ -146,24 +196,101 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
* @hide
*/
@Override
- public final int download(DownloadRequest downloadRequest, IDownloadProgressListener listener)
+ public final int registerStateCallback(
+ final DownloadRequest downloadRequest, final IDownloadStateCallback callback)
throws RemoteException {
- return download(downloadRequest, new DownloadProgressListener() {
+ final int uid = Binder.getCallingUid();
+ DeathRecipient deathRecipient = new DeathRecipient() {
+ @Override
+ public void binderDied() {
+ onAppCallbackDied(uid, downloadRequest.getSubscriptionId());
+ mDownloadCallbackBinderMap.remove(callback.asBinder());
+ mDownloadCallbackDeathRecipients.remove(callback.asBinder());
+ }
+ };
+ mDownloadCallbackDeathRecipients.put(callback.asBinder(), deathRecipient);
+ callback.asBinder().linkToDeath(deathRecipient, 0);
+
+ DownloadStateCallback exposedCallback = new DownloadStateCallback() {
@Override
- public void progress(DownloadRequest request, FileInfo fileInfo, int
+ public void onProgressUpdated(DownloadRequest request, FileInfo fileInfo, int
currentDownloadSize, int fullDownloadSize, int currentDecodedSize, int
- fullDecodedSize) throws RemoteException {
- listener.progress(request, fileInfo, currentDownloadSize, fullDownloadSize,
- currentDecodedSize, fullDecodedSize);
+ fullDecodedSize) {
+ try {
+ callback.onProgressUpdated(request, fileInfo, currentDownloadSize,
+ fullDownloadSize,
+ currentDecodedSize, fullDecodedSize);
+ } catch (RemoteException e) {
+ onAppCallbackDied(uid, downloadRequest.getSubscriptionId());
+ }
}
- });
+
+ @Override
+ public void onStateUpdated(DownloadRequest request, FileInfo fileInfo,
+ @MbmsDownloadSession.DownloadStatus int state) {
+ try {
+ callback.onStateUpdated(request, fileInfo, state);
+ } catch (RemoteException e) {
+ onAppCallbackDied(uid, downloadRequest.getSubscriptionId());
+ }
+ }
+ };
+
+ mDownloadCallbackBinderMap.put(callback.asBinder(), exposedCallback);
+
+ return registerStateCallback(downloadRequest, exposedCallback);
}
+ /**
+ * Un-registers a download state callbacks for the provided {@link DownloadRequest}.
+ *
+ * This method is called by the app when it no longer wants to request updates on the
+ * download.
+ *
+ * If the middleware is not aware of a download having been requested with the provided
+ * {@link DownloadRequest} in the past,
+ * {@link MbmsErrors.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}
+ * must be returned.
+ *
+ * @param downloadRequest The {@link DownloadRequest} that was used to register the callback
+ * @param callback The callback object that
+ * {@link #registerStateCallback(DownloadRequest, DownloadStateCallback)}
+ * was called with.
+ */
+ public int unregisterStateCallback(DownloadRequest downloadRequest,
+ DownloadStateCallback callback) throws RemoteException {
+ return 0;
+ }
+
+ /**
+ * Actual AIDL implementation -- hides the callback AIDL from the API.
+ * @hide
+ */
+ @Override
+ public final int unregisterStateCallback(
+ final DownloadRequest downloadRequest, final IDownloadStateCallback callback)
+ throws RemoteException {
+ DeathRecipient deathRecipient =
+ mDownloadCallbackDeathRecipients.remove(callback.asBinder());
+ if (deathRecipient == null) {
+ throw new IllegalArgumentException("Unknown callback");
+ }
+
+ callback.asBinder().unlinkToDeath(deathRecipient, 0);
+
+ DownloadStateCallback exposedCallback =
+ mDownloadCallbackBinderMap.remove(callback.asBinder());
+ if (exposedCallback == null) {
+ throw new IllegalArgumentException("Unknown callback");
+ }
+
+ return unregisterStateCallback(downloadRequest, exposedCallback);
+ }
/**
* Returns a list of pending {@link DownloadRequest}s that originated from the calling
* application, identified by its uid. A pending request is one that was issued via
- * {@link #download(DownloadRequest, IDownloadProgressListener)} but not cancelled through
+ * {@link #download(DownloadRequest)} but not cancelled through
* {@link #cancelDownload(DownloadRequest)}.
* The middleware must return a non-null result synchronously or throw an exception
* inheriting from {@link RuntimeException}.
@@ -179,13 +306,13 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
* Issues a request to cancel the specified download request.
*
* If the middleware is unable to cancel the request for whatever reason, it should return
- * synchronously with an error. If this method returns {@link MbmsException#SUCCESS}, the app
+ * synchronously with an error. If this method returns {@link MbmsErrors#SUCCESS}, the app
* will no longer be expecting any more file-completed intents from the middleware for this
* {@link DownloadRequest}.
* @param downloadRequest The request to cancel
- * @return {@link MbmsException#SUCCESS},
- * {@link MbmsException.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST},
- * {@link MbmsException.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}
+ * @return {@link MbmsErrors#SUCCESS},
+ * {@link MbmsErrors.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST},
+ * {@link MbmsErrors.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}
*/
@Override
public int cancelDownload(DownloadRequest downloadRequest) throws RemoteException {
@@ -197,7 +324,7 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
*
* If the middleware has not yet been properly initialized or if it has no records of the
* file indicated by {@code fileInfo} being associated with {@code downloadRequest},
- * {@link android.telephony.MbmsDownloadManager#STATUS_UNKNOWN} must be returned.
+ * {@link MbmsDownloadSession#STATUS_UNKNOWN} must be returned.
*
* @param downloadRequest The download request to query.
* @param fileInfo The particular file within the request to get information on.
@@ -217,7 +344,7 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
* In addition, current in-progress downloads must not be interrupted.
*
* If the middleware is not aware of the specified download request, return
- * {@link MbmsException.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}.
+ * {@link MbmsErrors.DownloadErrors#ERROR_UNKNOWN_DOWNLOAD_REQUEST}.
*
* @param downloadRequest The request to re-download files for.
*/
@@ -231,7 +358,7 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
* Signals that the app wishes to dispose of the session identified by the
* {@code subscriptionId} argument and the caller's uid. No notification back to the
* app is required for this operation, and the corresponding callback provided via
- * {@link #initialize(int, IMbmsDownloadManagerCallback)} should no longer be used
+ * {@link #initialize(int, IMbmsDownloadSessionCallback)} should no longer be used
* after this method has been called by the app.
*
* Any download requests issued by the app should remain in effect until the app calls
@@ -244,4 +371,12 @@ public class MbmsDownloadServiceBase extends IMbmsDownloadService.Stub {
@Override
public void dispose(int subscriptionId) throws RemoteException {
}
+
+ /**
+ * Indicates that the app identified by the given UID and subscription ID has died.
+ * @param uid the UID of the app, as returned by {@link Binder#getCallingUid()}.
+ * @param subscriptionId The subscription ID the app is using.
+ */
+ public void onAppCallbackDied(int uid, int subscriptionId) {
+ }
}
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
index 843e0482ee1b..f8f370a5fe8d 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
@@ -18,13 +18,14 @@ package android.telephony.mbms.vendor;
import android.annotation.Nullable;
import android.annotation.SystemApi;
+import android.content.Intent;
import android.net.Uri;
import android.os.Binder;
import android.os.RemoteException;
-import android.telephony.mbms.IMbmsStreamingManagerCallback;
+import android.telephony.mbms.IMbmsStreamingSessionCallback;
import android.telephony.mbms.IStreamingServiceCallback;
-import android.telephony.mbms.MbmsException;
-import android.telephony.mbms.MbmsStreamingManagerCallback;
+import android.telephony.mbms.MbmsErrors;
+import android.telephony.mbms.MbmsStreamingSessionCallback;
import android.telephony.mbms.StreamingService;
import android.telephony.mbms.StreamingServiceCallback;
import android.telephony.mbms.StreamingServiceInfo;
@@ -32,6 +33,8 @@ import android.telephony.mbms.StreamingServiceInfo;
import java.util.List;
/**
+ * Base class for MBMS streaming services. The middleware should return an instance of this
+ * object from its {@link android.app.Service#onBind(Intent)} method.
* @hide
*/
@SystemApi
@@ -41,16 +44,16 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
*
* May throw an {@link IllegalArgumentException} or a {@link SecurityException}, which
* will be intercepted and passed to the app as
- * {@link android.telephony.mbms.MbmsException.InitializationErrors#ERROR_UNABLE_TO_INITIALIZE}
+ * {@link MbmsErrors.InitializationErrors#ERROR_UNABLE_TO_INITIALIZE}
*
- * May return any value from {@link android.telephony.mbms.MbmsException.InitializationErrors}
- * or {@link MbmsException#SUCCESS}. Non-successful error codes will be passed to the app via
- * {@link IMbmsStreamingManagerCallback#error(int, String)}.
+ * May return any value from {@link MbmsErrors.InitializationErrors}
+ * or {@link MbmsErrors#SUCCESS}. Non-successful error codes will be passed to the app via
+ * {@link IMbmsStreamingSessionCallback#onError(int, String)}.
*
* @param callback The callback to use to communicate with the app.
* @param subscriptionId The subscription ID to use.
*/
- public int initialize(MbmsStreamingManagerCallback callback, int subscriptionId)
+ public int initialize(MbmsStreamingSessionCallback callback, int subscriptionId)
throws RemoteException {
return 0;
}
@@ -60,7 +63,7 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
* @hide
*/
@Override
- public final int initialize(final IMbmsStreamingManagerCallback callback,
+ public final int initialize(final IMbmsStreamingSessionCallback callback,
final int subscriptionId) throws RemoteException {
final int uid = Binder.getCallingUid();
callback.asBinder().linkToDeath(new DeathRecipient() {
@@ -70,20 +73,20 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
}
}, 0);
- return initialize(new MbmsStreamingManagerCallback() {
+ return initialize(new MbmsStreamingSessionCallback() {
@Override
- public void onError(int errorCode, String message) {
+ public void onError(final int errorCode, final String message) {
try {
- callback.error(errorCode, message);
+ callback.onError(errorCode, message);
} catch (RemoteException e) {
onAppCallbackDied(uid, subscriptionId);
}
}
@Override
- public void onStreamingServicesUpdated(List<StreamingServiceInfo> services) {
+ public void onStreamingServicesUpdated(final List<StreamingServiceInfo> services) {
try {
- callback.streamingServicesUpdated(services);
+ callback.onStreamingServicesUpdated(services);
} catch (RemoteException e) {
onAppCallbackDied(uid, subscriptionId);
}
@@ -92,7 +95,7 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
@Override
public void onMiddlewareReady() {
try {
- callback.middlewareReady();
+ callback.onMiddlewareReady();
} catch (RemoteException e) {
onAppCallbackDied(uid, subscriptionId);
}
@@ -104,7 +107,7 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
/**
* Registers serviceClasses of interest with the appName/subId key.
* Starts async fetching data on streaming services of matching classes to be reported
- * later via {@link IMbmsStreamingManagerCallback#streamingServicesUpdated(List)}
+ * later via {@link IMbmsStreamingSessionCallback#onStreamingServicesUpdated(List)}
*
* Note that subsequent calls with the same uid and subId will replace
* the service class list.
@@ -115,11 +118,11 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
* @param serviceClasses The service classes that the app wishes to get info on. The strings
* may contain arbitrary data as negotiated between the app and the
* carrier.
- * @return {@link MbmsException#SUCCESS} or any of the errors in
- * {@link android.telephony.mbms.MbmsException.GeneralErrors}
+ * @return {@link MbmsErrors#SUCCESS} or any of the errors in
+ * {@link MbmsErrors.GeneralErrors}
*/
@Override
- public int getStreamingServices(int subscriptionId,
+ public int requestUpdateStreamingServices(int subscriptionId,
List<String> serviceClasses) throws RemoteException {
return 0;
}
@@ -127,14 +130,14 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
/**
* Starts streaming on a particular service. This method may perform asynchronous work. When
* the middleware is ready to send bits to the frontend, it should inform the app via
- * {@link IStreamingServiceCallback#streamStateUpdated(int, int)}.
+ * {@link IStreamingServiceCallback#onStreamStateUpdated(int, int)}.
*
* May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
*
* @param subscriptionId The subscription id to use.
* @param serviceId The ID of the streaming service that the app has requested.
* @param callback The callback object on which the app wishes to receive updates.
- * @return Any error in {@link android.telephony.mbms.MbmsException.GeneralErrors}
+ * @return Any error in {@link MbmsErrors.GeneralErrors}
*/
public int startStreaming(int subscriptionId, String serviceId,
StreamingServiceCallback callback) throws RemoteException {
@@ -147,8 +150,8 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
* @hide
*/
@Override
- public int startStreaming(int subscriptionId, String serviceId,
- IStreamingServiceCallback callback) throws RemoteException {
+ public int startStreaming(final int subscriptionId, String serviceId,
+ final IStreamingServiceCallback callback) throws RemoteException {
final int uid = Binder.getCallingUid();
callback.asBinder().linkToDeath(new DeathRecipient() {
@Override
@@ -159,19 +162,19 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
return startStreaming(subscriptionId, serviceId, new StreamingServiceCallback() {
@Override
- public void onError(int errorCode, String message) {
+ public void onError(final int errorCode, final String message) {
try {
- callback.error(errorCode, message);
+ callback.onError(errorCode, message);
} catch (RemoteException e) {
onAppCallbackDied(uid, subscriptionId);
}
}
@Override
- public void onStreamStateUpdated(@StreamingService.StreamingState int state,
- @StreamingService.StreamingStateChangeReason int reason) {
+ public void onStreamStateUpdated(@StreamingService.StreamingState final int state,
+ @StreamingService.StreamingStateChangeReason final int reason) {
try {
- callback.streamStateUpdated(state, reason);
+ callback.onStreamStateUpdated(state, reason);
} catch (RemoteException e) {
onAppCallbackDied(uid, subscriptionId);
}
@@ -180,25 +183,25 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
@Override
public void onMediaDescriptionUpdated() {
try {
- callback.mediaDescriptionUpdated();
+ callback.onMediaDescriptionUpdated();
} catch (RemoteException e) {
onAppCallbackDied(uid, subscriptionId);
}
}
@Override
- public void onBroadcastSignalStrengthUpdated(int signalStrength) {
+ public void onBroadcastSignalStrengthUpdated(final int signalStrength) {
try {
- callback.broadcastSignalStrengthUpdated(signalStrength);
+ callback.onBroadcastSignalStrengthUpdated(signalStrength);
} catch (RemoteException e) {
onAppCallbackDied(uid, subscriptionId);
}
}
@Override
- public void onStreamMethodUpdated(int methodType) {
+ public void onStreamMethodUpdated(final int methodType) {
try {
- callback.streamMethodUpdated(methodType);
+ callback.onStreamMethodUpdated(methodType);
} catch (RemoteException e) {
onAppCallbackDied(uid, subscriptionId);
}
@@ -225,32 +228,19 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
/**
* Stop streaming the stream identified by {@code serviceId}. Notification of the resulting
* stream state change should be reported to the app via
- * {@link IStreamingServiceCallback#streamStateUpdated(int, int)}.
- *
- * May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
+ * {@link IStreamingServiceCallback#onStreamStateUpdated(int, int)}.
*
- * @param subscriptionId The subscription id to use.
- * @param serviceId The ID of the streaming service that the app wishes to stop.
- */
- @Override
- public void stopStreaming(int subscriptionId, String serviceId)
- throws RemoteException {
- }
-
- /**
- * Dispose of the stream identified by {@code serviceId} for the app identified by the
- * {@code appName} and {@code subscriptionId} arguments along with the caller's uid.
- * No notification back to the app is required for this operation, and the callback provided via
+ * In addition, the callback provided via
* {@link #startStreaming(int, String, IStreamingServiceCallback)} should no longer be
* used after this method has called by the app.
*
* May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
*
* @param subscriptionId The subscription id to use.
- * @param serviceId The ID of the streaming service that the app wishes to dispose of.
+ * @param serviceId The ID of the streaming service that the app wishes to stop.
*/
@Override
- public void disposeStream(int subscriptionId, String serviceId)
+ public void stopStreaming(int subscriptionId, String serviceId)
throws RemoteException {
}
@@ -258,7 +248,7 @@ public class MbmsStreamingServiceBase extends IMbmsStreamingService.Stub {
* Signals that the app wishes to dispose of the session identified by the
* {@code subscriptionId} argument and the caller's uid. No notification back to the
* app is required for this operation, and the corresponding callback provided via
- * {@link #initialize(IMbmsStreamingManagerCallback, int)} should no longer be used
+ * {@link #initialize(IMbmsStreamingSessionCallback, int)} should no longer be used
* after this method has been called by the app.
*
* May throw an {@link IllegalStateException}
diff --git a/telephony/java/android/telephony/mbms/vendor/VendorIntents.java b/telephony/java/android/telephony/mbms/vendor/VendorUtils.java
index 367c995c6f7d..8fb27b2953e6 100644
--- a/telephony/java/android/telephony/mbms/vendor/VendorIntents.java
+++ b/telephony/java/android/telephony/mbms/vendor/VendorUtils.java
@@ -16,29 +16,32 @@
package android.telephony.mbms.vendor;
+import android.annotation.SystemApi;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.net.Uri;
-import android.telephony.mbms.DownloadRequest;
+import android.telephony.MbmsDownloadSession;
import android.telephony.mbms.MbmsDownloadReceiver;
import java.io.File;
import java.util.List;
/**
+ * Contains constants and utility methods for MBMS Download middleware apps to communicate with
+ * frontend apps.
* @hide
- * TODO: future systemapi
*/
-public class VendorIntents {
+@SystemApi
+public class VendorUtils {
/**
* The MBMS middleware should send this when a download of single file has completed or
* failed. Mandatory extras are
- * {@link android.telephony.MbmsDownloadManager#EXTRA_RESULT}
- * {@link android.telephony.MbmsDownloadManager#EXTRA_FILE_INFO}
- * {@link #EXTRA_REQUEST}
+ * {@link MbmsDownloadSession#EXTRA_MBMS_DOWNLOAD_RESULT}
+ * {@link MbmsDownloadSession#EXTRA_MBMS_FILE_INFO}
+ * {@link MbmsDownloadSession#EXTRA_MBMS_DOWNLOAD_REQUEST}
* {@link #EXTRA_TEMP_LIST}
* {@link #EXTRA_FINAL_URI}
*/
@@ -49,7 +52,7 @@ public class VendorIntents {
* The MBMS middleware should send this when it wishes to request {@code content://} URIs to
* serve as temp files for downloads or when it wishes to resume paused downloads. Mandatory
* extras are
- * {@link #EXTRA_REQUEST}
+ * {@link #EXTRA_SERVICE_ID}
*
* Optional extras are
* {@link #EXTRA_FD_COUNT} (0 if not present)
@@ -118,48 +121,35 @@ public class VendorIntents {
"android.telephony.mbms.extra.TEMP_FILES_IN_USE";
/**
- * Extra containing the {@link DownloadRequest} for which the download result or file
- * descriptor request is for. Must not be null.
- */
- public static final String EXTRA_REQUEST = "android.telephony.mbms.extra.REQUEST";
-
- /**
* Extra containing a single {@link Uri} indicating the path to the temp file in which the
* decoded downloaded file resides. Must not be null.
*/
public static final String EXTRA_FINAL_URI = "android.telephony.mbms.extra.FINAL_URI";
/**
- * Extra containing an instance of {@link android.telephony.mbms.ServiceInfo}, used by
+ * Extra containing a String representing a service ID, used by
* file-descriptor requests and cleanup requests to specify which service they want to
* request temp files or clean up temp files for, respectively.
*/
- public static final String EXTRA_SERVICE_INFO =
- "android.telephony.mbms.extra.SERVICE_INFO";
+ public static final String EXTRA_SERVICE_ID =
+ "android.telephony.mbms.extra.SERVICE_ID";
/**
* Retrieves the {@link ComponentName} for the {@link android.content.BroadcastReceiver} that
* the various intents from the middleware should be targeted towards.
- * @param uid The uid of the frontend app.
+ * @param packageName The package name of the app.
* @return The component name of the receiver that the middleware should send its intents to,
* or null if the app didn't declare it in the manifest.
*/
- public static ComponentName getAppReceiverFromUid(Context context, int uid) {
- String[] packageNames = context.getPackageManager().getPackagesForUid(uid);
- if (packageNames == null) {
- return null;
- }
-
- for (String packageName : packageNames) {
- ComponentName candidate = new ComponentName(packageName,
- MbmsDownloadReceiver.class.getCanonicalName());
- Intent queryIntent = new Intent();
- queryIntent.setComponent(candidate);
- List<ResolveInfo> receivers =
- context.getPackageManager().queryBroadcastReceivers(queryIntent, 0);
- if (receivers != null && receivers.size() > 0) {
- return candidate;
- }
+ public static ComponentName getAppReceiverFromPackageName(Context context, String packageName) {
+ ComponentName candidate = new ComponentName(packageName,
+ MbmsDownloadReceiver.class.getCanonicalName());
+ Intent queryIntent = new Intent();
+ queryIntent.setComponent(candidate);
+ List<ResolveInfo> receivers =
+ context.getPackageManager().queryBroadcastReceivers(queryIntent, 0);
+ if (receivers != null && receivers.size() > 0) {
+ return candidate;
}
return null;
}
diff --git a/telephony/java/com/android/ims/ImsConfig.java b/telephony/java/com/android/ims/ImsConfig.java
index e7b22bdfadcc..cf4c47bf541f 100644
--- a/telephony/java/com/android/ims/ImsConfig.java
+++ b/telephony/java/com/android/ims/ImsConfig.java
@@ -524,6 +524,7 @@ public class ImsConfig {
* Defines IMS feature value.
*/
public static class FeatureValueConstants {
+ public static final int ERROR = -1;
public static final int OFF = 0;
public static final int ON = 1;
}
diff --git a/telephony/java/com/android/internal/telephony/ExponentialBackoff.java b/telephony/java/com/android/internal/telephony/ExponentialBackoff.java
new file mode 100644
index 000000000000..80958c077d6a
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/ExponentialBackoff.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.telephony;
+
+import android.annotation.NonNull;
+import android.os.Handler;
+import android.os.Looper;
+
+/** The implementation of exponential backoff with jitter applied. */
+public class ExponentialBackoff {
+ private int mRetryCounter;
+ private long mStartDelayMs;
+ private long mMaximumDelayMs;
+ private long mCurrentDelayMs;
+ private int mMultiplier;
+ private Runnable mRunnable;
+ private Handler mHandler;
+
+ public ExponentialBackoff(
+ long initialDelayMs,
+ long maximumDelayMs,
+ int multiplier,
+ @NonNull Looper looper,
+ @NonNull Runnable runnable) {
+ this(initialDelayMs, maximumDelayMs, multiplier, new Handler(looper), runnable);
+ }
+
+ public ExponentialBackoff(
+ long initialDelayMs,
+ long maximumDelayMs,
+ int multiplier,
+ @NonNull Handler handler,
+ @NonNull Runnable runnable) {
+ mRetryCounter = 0;
+ mStartDelayMs = initialDelayMs;
+ mMaximumDelayMs = maximumDelayMs;
+ mMultiplier = multiplier;
+ mHandler = handler;
+ mRunnable = runnable;
+ }
+
+ /** Starts the backoff, the runnable will be executed after {@link #mStartDelayMs}. */
+ public void start() {
+ mRetryCounter = 0;
+ mCurrentDelayMs = mStartDelayMs;
+ mHandler.removeCallbacks(mRunnable);
+ mHandler.postDelayed(mRunnable, mCurrentDelayMs);
+ }
+
+ /** Stops the backoff, all pending messages will be removed from the message queue. */
+ public void stop() {
+ mRetryCounter = 0;
+ mHandler.removeCallbacks(mRunnable);
+ }
+
+ /** Should call when the retry action has failed and we want to retry after a longer delay. */
+ public void notifyFailed() {
+ mRetryCounter++;
+ long temp = Math.min(
+ mMaximumDelayMs, (long) (mStartDelayMs * Math.pow(mMultiplier, mRetryCounter)));
+ mCurrentDelayMs = (long) (((1 + Math.random()) / 2) * temp);
+ mHandler.removeCallbacks(mRunnable);
+ mHandler.postDelayed(mRunnable, mCurrentDelayMs);
+ }
+
+ /** Returns the delay for the most recently posted message. */
+ public long getCurrentDelay() {
+ return mCurrentDelayMs;
+ }
+}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index ab7c5e722c24..0dd846e31841 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1080,6 +1080,15 @@ interface ITelephony {
boolean isImsRegistered();
/**
+ * Get IMS Registration Status on a particular subid.
+ *
+ * @param subId user preferred subId.
+ *
+ * @return {@code true} if the IMS status is registered.
+ */
+ boolean isImsRegisteredForSubscriber(int subId);
+
+ /**
* Returns the Status of Wi-Fi Calling
*/
boolean isWifiCallingAvailable();
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSmsAddress.java b/telephony/java/com/android/internal/telephony/gsm/GsmSmsAddress.java
index 2fbf7ed5648c..bd8c83e63055 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmSmsAddress.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmSmsAddress.java
@@ -17,6 +17,7 @@
package com.android.internal.telephony.gsm;
import android.telephony.PhoneNumberUtils;
+
import java.text.ParseException;
import com.android.internal.telephony.GsmAlphabet;
import com.android.internal.telephony.SmsAddress;
@@ -71,8 +72,11 @@ public class GsmSmsAddress extends SmsAddress {
// Make sure the final unused BCD digit is 0xf
origBytes[length - 1] |= 0xf0;
}
- address = PhoneNumberUtils.calledPartyBCDToString(origBytes,
- OFFSET_TOA, length - OFFSET_TOA);
+ address = PhoneNumberUtils.calledPartyBCDToString(
+ origBytes,
+ OFFSET_TOA,
+ length - OFFSET_TOA,
+ PhoneNumberUtils.BCD_EXTENDED_TYPE_CALLED_PARTY);
// And restore origBytes
origBytes[length - 1] = lastByte;
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index d4098d94ba6c..1ca19e01d6c8 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -535,8 +535,8 @@ public class SmsMessage extends SmsMessageBase {
} else {
// SC address
try {
- ret = PhoneNumberUtils
- .calledPartyBCDToString(mPdu, mCur, len);
+ ret = PhoneNumberUtils.calledPartyBCDToString(
+ mPdu, mCur, len, PhoneNumberUtils.BCD_EXTENDED_TYPE_CALLED_PARTY);
} catch (RuntimeException tr) {
Rlog.d(LOG_TAG, "invalid SC address: ", tr);
ret = null;
diff --git a/test-runner/Android.mk b/test-runner/Android.mk
index 0752661b3a68..a642fdfb58a0 100644
--- a/test-runner/Android.mk
+++ b/test-runner/Android.mk
@@ -70,11 +70,10 @@ LOCAL_MODULE := android.test.mock.sdk
LOCAL_SOURCE_FILES_ALL_GENERATED := true
-include $(BUILD_STATIC_JAVA_LIBRARY)
-
# Make sure to run droiddoc first to generate the stub source files.
-$(full_classes_compiled_jar) : $(android_test_mock_gen_stamp)
-$(full_classes_jack) : $(android_test_mock_gen_stamp)
+LOCAL_ADDITIONAL_DEPENDENCIES := $(android_test_mock_gen_stamp)
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
# Archive a copy of the classes.jar in SDK build.
$(call dist-for-goals,sdk win_sdk,$(full_classes_jar):android.test.mock.jar)
diff --git a/tests/net/Android.mk b/tests/net/Android.mk
index 3af0adc36bde..677585cc0c0f 100644
--- a/tests/net/Android.mk
+++ b/tests/net/Android.mk
@@ -42,6 +42,7 @@ LOCAL_JNI_SHARED_LIBRARIES := libframeworksnettestsjni \
libui \
libunwind \
libutils \
+ libvndksupport \
libcrypto \
libhidl-gen-utils \
libhidlbase \
@@ -52,6 +53,7 @@ LOCAL_JNI_SHARED_LIBRARIES := libframeworksnettestsjni \
libtinyxml2 \
libvintf \
libhwbinder \
+ libunwindstack \
android.hidl.token@1.0
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
diff --git a/tests/net/java/android/net/apf/ApfTest.java b/tests/net/java/android/net/apf/ApfTest.java
index bfbb8cc541d7..5008a4188905 100644
--- a/tests/net/java/android/net/apf/ApfTest.java
+++ b/tests/net/java/android/net/apf/ApfTest.java
@@ -614,9 +614,10 @@ public class ApfTest extends AndroidTestCase {
private final long mFixedTimeMs = SystemClock.elapsedRealtime();
public TestApfFilter(IpManager.Callback ipManagerCallback, boolean multicastFilter,
- boolean ieee802_3Filter, IpConnectivityLog log) throws Exception {
+ boolean ieee802_3Filter, int[] ethTypeBlackList,
+ IpConnectivityLog log) throws Exception {
super(new ApfCapabilities(2, 1700, ARPHRD_ETHER), NetworkInterface.getByName("lo"),
- ipManagerCallback, multicastFilter, ieee802_3Filter, log);
+ ipManagerCallback, multicastFilter, ieee802_3Filter, ethTypeBlackList, log);
}
// Pretend an RA packet has been received and show it to ApfFilter.
@@ -744,9 +745,10 @@ public class ApfTest extends AndroidTestCase {
LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19);
LinkProperties lp = new LinkProperties();
lp.addLinkAddress(link);
+ final int[] ethTypeBlackList = {};
ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, DROP_MULTICAST,
- ALLOW_802_3_FRAMES, mLog);
+ ALLOW_802_3_FRAMES, ethTypeBlackList, mLog);
apfFilter.setLinkProperties(lp);
byte[] program = ipManagerCallback.getApfProgram();
@@ -796,9 +798,10 @@ public class ApfTest extends AndroidTestCase {
@SmallTest
public void testApfFilterIPv6() throws Exception {
+ final int[] ethTypeBlackList = {};
MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST,
- ALLOW_802_3_FRAMES, mLog);
+ ALLOW_802_3_FRAMES, ethTypeBlackList, mLog);
byte[] program = ipManagerCallback.getApfProgram();
// Verify empty IPv6 packet is passed
@@ -833,6 +836,7 @@ public class ApfTest extends AndroidTestCase {
final byte[] broadcastIpv4Addr = {(byte)192,0,2,(byte)255};
final byte[] multicastIpv4Addr = {(byte)224,0,0,1};
final byte[] multicastIpv6Addr = {(byte)0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,(byte)0xfb};
+ final int[] ethTypeBlackList = {};
MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
LinkAddress link = new LinkAddress(InetAddress.getByAddress(unicastIpv4Addr), 24);
@@ -840,7 +844,7 @@ public class ApfTest extends AndroidTestCase {
lp.addLinkAddress(link);
ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST,
- DROP_802_3_FRAMES, mLog);
+ DROP_802_3_FRAMES, ethTypeBlackList, mLog);
apfFilter.setLinkProperties(lp);
byte[] program = ipManagerCallback.getApfProgram();
@@ -903,7 +907,7 @@ public class ApfTest extends AndroidTestCase {
ipManagerCallback.resetApfProgramWait();
apfFilter.shutdown();
apfFilter = new TestApfFilter(ipManagerCallback, DROP_MULTICAST,
- DROP_802_3_FRAMES, mLog);
+ DROP_802_3_FRAMES, ethTypeBlackList, mLog);
apfFilter.setLinkProperties(lp);
program = ipManagerCallback.getApfProgram();
assertDrop(program, mcastv4packet.array());
@@ -924,9 +928,10 @@ public class ApfTest extends AndroidTestCase {
LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19);
LinkProperties lp = new LinkProperties();
lp.addLinkAddress(link);
+ final int[] ethTypeBlackList = {};
ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST,
- ALLOW_802_3_FRAMES, mLog);
+ ALLOW_802_3_FRAMES, ethTypeBlackList, mLog);
apfFilter.setLinkProperties(lp);
byte[] program = ipManagerCallback.getApfProgram();
@@ -948,7 +953,7 @@ public class ApfTest extends AndroidTestCase {
ipManagerCallback.resetApfProgramWait();
apfFilter.shutdown();
apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST,
- DROP_802_3_FRAMES, mLog);
+ DROP_802_3_FRAMES, ethTypeBlackList, mLog);
apfFilter.setLinkProperties(lp);
program = ipManagerCallback.getApfProgram();
@@ -968,6 +973,70 @@ public class ApfTest extends AndroidTestCase {
apfFilter.shutdown();
}
+ @SmallTest
+ public void testApfFilterEthTypeBL() throws Exception {
+ MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
+ LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19);
+ LinkProperties lp = new LinkProperties();
+ lp.addLinkAddress(link);
+ final int[] emptyBlackList = {};
+ final int[] ipv4BlackList = {ETH_P_IP};
+ final int[] ipv4Ipv6BlackList = {ETH_P_IP, ETH_P_IPV6};
+
+ ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST,
+ ALLOW_802_3_FRAMES, emptyBlackList, mLog);
+ apfFilter.setLinkProperties(lp);
+
+ byte[] program = ipManagerCallback.getApfProgram();
+
+ // Verify empty packet of 100 zero bytes is passed
+ // Note that eth-type = 0 makes it an IEEE802.3 frame
+ ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
+ assertPass(program, packet.array());
+
+ // Verify empty packet with IPv4 is passed
+ packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
+ assertPass(program, packet.array());
+
+ // Verify empty IPv6 packet is passed
+ packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
+ assertPass(program, packet.array());
+
+ // Now add IPv4 to the black list
+ ipManagerCallback.resetApfProgramWait();
+ apfFilter.shutdown();
+ apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST,
+ ALLOW_802_3_FRAMES, ipv4BlackList, mLog);
+ apfFilter.setLinkProperties(lp);
+ program = ipManagerCallback.getApfProgram();
+
+ // Verify that IPv4 frame will be dropped
+ packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
+ assertDrop(program, packet.array());
+
+ // Verify that IPv6 frame will pass
+ packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
+ assertPass(program, packet.array());
+
+ // Now let us have both IPv4 and IPv6 in the black list
+ ipManagerCallback.resetApfProgramWait();
+ apfFilter.shutdown();
+ apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST,
+ ALLOW_802_3_FRAMES, ipv4Ipv6BlackList, mLog);
+ apfFilter.setLinkProperties(lp);
+ program = ipManagerCallback.getApfProgram();
+
+ // Verify that IPv4 frame will be dropped
+ packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
+ assertDrop(program, packet.array());
+
+ // Verify that IPv6 frame will be dropped
+ packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IPV6);
+ assertDrop(program, packet.array());
+
+ apfFilter.shutdown();
+ }
+
private byte[] getProgram(MockIpManagerCallback cb, ApfFilter filter, LinkProperties lp) {
cb.resetApfProgramWait();
filter.setLinkProperties(lp);
@@ -991,9 +1060,10 @@ public class ApfTest extends AndroidTestCase {
@SmallTest
public void testApfFilterArp() throws Exception {
+ final int[] ethTypeBlackList = {};
MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST,
- DROP_802_3_FRAMES, mLog);
+ DROP_802_3_FRAMES, ethTypeBlackList, mLog);
// Verify initially ARP request filter is off, and GARP filter is on.
verifyArpFilter(ipManagerCallback.getApfProgram(), PASS);
@@ -1114,8 +1184,9 @@ public class ApfTest extends AndroidTestCase {
@SmallTest
public void testApfFilterRa() throws Exception {
MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
+ final int[] ethTypeBlackList = {};
TestApfFilter apfFilter = new TestApfFilter(ipManagerCallback, DROP_MULTICAST,
- DROP_802_3_FRAMES, mLog);
+ DROP_802_3_FRAMES, ethTypeBlackList, mLog);
byte[] program = ipManagerCallback.getApfProgram();
final int ROUTER_LIFETIME = 1000;
@@ -1256,9 +1327,10 @@ public class ApfTest extends AndroidTestCase {
public void testRaParsing() throws Exception {
final int maxRandomPacketSize = 512;
final Random r = new Random();
+ final int[] ethTypeBlackList = {};
MockIpManagerCallback cb = new MockIpManagerCallback();
TestApfFilter apfFilter = new TestApfFilter(cb, DROP_MULTICAST,
- DROP_802_3_FRAMES, mLog);
+ DROP_802_3_FRAMES, ethTypeBlackList, mLog);
for (int i = 0; i < 1000; i++) {
byte[] packet = new byte[r.nextInt(maxRandomPacketSize + 1)];
r.nextBytes(packet);
@@ -1275,9 +1347,10 @@ public class ApfTest extends AndroidTestCase {
public void testRaProcessing() throws Exception {
final int maxRandomPacketSize = 512;
final Random r = new Random();
+ final int[] ethTypeBlackList = {};
MockIpManagerCallback cb = new MockIpManagerCallback();
TestApfFilter apfFilter = new TestApfFilter(cb, DROP_MULTICAST,
- DROP_802_3_FRAMES, mLog);
+ DROP_802_3_FRAMES, ethTypeBlackList, mLog);
for (int i = 0; i < 1000; i++) {
byte[] packet = new byte[r.nextInt(maxRandomPacketSize + 1)];
r.nextBytes(packet);
diff --git a/tests/net/java/android/net/ip/IpManagerTest.java b/tests/net/java/android/net/ip/IpManagerTest.java
index 541f91adf747..22d88fb70697 100644
--- a/tests/net/java/android/net/ip/IpManagerTest.java
+++ b/tests/net/java/android/net/ip/IpManagerTest.java
@@ -180,7 +180,8 @@ public class IpManagerTest {
// Add N - 1 addresses
for (int i = 0; i < lastAddr; i++) {
mObserver.addressUpdated(iface, new LinkAddress(addresses[i]));
- verify(mCb, timeout(100).times(1)).onLinkPropertiesChange(any());
+ verify(mCb, timeout(100)).onLinkPropertiesChange(any());
+ reset(mCb);
}
// Add Nth address
diff --git a/tests/net/java/android/net/netlink/ConntrackMessageTest.java b/tests/net/java/android/net/netlink/ConntrackMessageTest.java
new file mode 100644
index 000000000000..3aab9426bc3f
--- /dev/null
+++ b/tests/net/java/android/net/netlink/ConntrackMessageTest.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.netlink;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assume.assumeTrue;
+
+import android.system.OsConstants;
+import libcore.util.HexEncoding;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Arrays;
+
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class ConntrackMessageTest {
+ private static final boolean USING_LE = (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN);
+
+ // Example 1: TCP (192.168.43.209, 44333) -> (23.211.13.26, 443)
+ public static final String CT_V4UPDATE_TCP_HEX =
+ // struct nlmsghdr
+ "50000000" + // length = 80
+ "0001" + // type = (1 << 8) | 0
+ "0501" + // flags
+ "01000000" + // seqno = 1
+ "00000000" + // pid = 0
+ // struct nfgenmsg
+ "02" + // nfgen_family = AF_INET
+ "00" + // version = NFNETLINK_V0
+ "0000" + // res_id
+ // struct nlattr
+ "3400" + // nla_len = 52
+ "0180" + // nla_type = nested CTA_TUPLE_ORIG
+ // struct nlattr
+ "1400" + // nla_len = 20
+ "0180" + // nla_type = nested CTA_TUPLE_IP
+ "0800 0100 C0A82BD1" + // nla_type=CTA_IP_V4_SRC, ip=192.168.43.209
+ "0800 0200 17D30D1A" + // nla_type=CTA_IP_V4_DST, ip=23.211.13.26
+ // struct nlattr
+ "1C00" + // nla_len = 28
+ "0280" + // nla_type = nested CTA_TUPLE_PROTO
+ "0500 0100 06 000000" + // nla_type=CTA_PROTO_NUM, proto=6
+ "0600 0200 AD2D 0000" + // nla_type=CTA_PROTO_SRC_PORT, port=44333 (big endian)
+ "0600 0300 01BB 0000" + // nla_type=CTA_PROTO_DST_PORT, port=443 (big endian)
+ // struct nlattr
+ "0800" + // nla_len = 8
+ "0700" + // nla_type = CTA_TIMEOUT
+ "00069780"; // nla_value = 432000 (big endian)
+ public static final byte[] CT_V4UPDATE_TCP_BYTES =
+ HexEncoding.decode(CT_V4UPDATE_TCP_HEX.replaceAll(" ", "").toCharArray(), false);
+
+ // Example 2: UDP (100.96.167.146, 37069) -> (216.58.197.10, 443)
+ public static final String CT_V4UPDATE_UDP_HEX =
+ // struct nlmsghdr
+ "50000000" + // length = 80
+ "0001" + // type = (1 << 8) | 0
+ "0501" + // flags
+ "01000000" + // seqno = 1
+ "00000000" + // pid = 0
+ // struct nfgenmsg
+ "02" + // nfgen_family = AF_INET
+ "00" + // version = NFNETLINK_V0
+ "0000" + // res_id
+ // struct nlattr
+ "3400" + // nla_len = 52
+ "0180" + // nla_type = nested CTA_TUPLE_ORIG
+ // struct nlattr
+ "1400" + // nla_len = 20
+ "0180" + // nla_type = nested CTA_TUPLE_IP
+ "0800 0100 6460A792" + // nla_type=CTA_IP_V4_SRC, ip=100.96.167.146
+ "0800 0200 D83AC50A" + // nla_type=CTA_IP_V4_DST, ip=216.58.197.10
+ // struct nlattr
+ "1C00" + // nla_len = 28
+ "0280" + // nla_type = nested CTA_TUPLE_PROTO
+ "0500 0100 11 000000" + // nla_type=CTA_PROTO_NUM, proto=17
+ "0600 0200 90CD 0000" + // nla_type=CTA_PROTO_SRC_PORT, port=37069 (big endian)
+ "0600 0300 01BB 0000" + // nla_type=CTA_PROTO_DST_PORT, port=443 (big endian)
+ // struct nlattr
+ "0800" + // nla_len = 8
+ "0700" + // nla_type = CTA_TIMEOUT
+ "000000B4"; // nla_value = 180 (big endian)
+ public static final byte[] CT_V4UPDATE_UDP_BYTES =
+ HexEncoding.decode(CT_V4UPDATE_UDP_HEX.replaceAll(" ", "").toCharArray(), false);
+
+ @Test
+ public void testConntrackIPv4TcpTimeoutUpdate() throws Exception {
+ assumeTrue(USING_LE);
+
+ final byte[] tcp = ConntrackMessage.newIPv4TimeoutUpdateRequest(
+ OsConstants.IPPROTO_TCP,
+ (Inet4Address) InetAddress.getByName("192.168.43.209"), 44333,
+ (Inet4Address) InetAddress.getByName("23.211.13.26"), 443,
+ 432000);
+ assertArrayEquals(CT_V4UPDATE_TCP_BYTES, tcp);
+ }
+
+ @Test
+ public void testConntrackIPv4UdpTimeoutUpdate() throws Exception {
+ assumeTrue(USING_LE);
+
+ final byte[] udp = ConntrackMessage.newIPv4TimeoutUpdateRequest(
+ OsConstants.IPPROTO_UDP,
+ (Inet4Address) InetAddress.getByName("100.96.167.146"), 37069,
+ (Inet4Address) InetAddress.getByName("216.58.197.10"), 443,
+ 180);
+ assertArrayEquals(CT_V4UPDATE_UDP_BYTES, udp);
+ }
+}
diff --git a/tests/net/java/android/net/nsd/NsdManagerTest.java b/tests/net/java/android/net/nsd/NsdManagerTest.java
index f77608f95b3e..0a5a6aae6e9c 100644
--- a/tests/net/java/android/net/nsd/NsdManagerTest.java
+++ b/tests/net/java/android/net/nsd/NsdManagerTest.java
@@ -28,6 +28,7 @@ import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static com.android.internal.util.TestUtils.waitForIdleHandler;
import android.os.HandlerThread;
import android.os.Handler;
@@ -38,6 +39,7 @@ import android.support.test.runner.AndroidJUnit4;
import android.os.Message;
import android.os.Messenger;
import com.android.internal.util.AsyncChannel;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -56,7 +58,9 @@ public class NsdManagerTest {
@Mock INsdManager mService;
MockServiceHandler mServiceHandler;
- long mTimeoutMs = 100; // non-final so that tests can adjust the value.
+ NsdManager mManager;
+
+ long mTimeoutMs = 200; // non-final so that tests can adjust the value.
@Before
public void setUp() throws Exception {
@@ -64,11 +68,23 @@ public class NsdManagerTest {
mServiceHandler = spy(MockServiceHandler.create(mContext));
when(mService.getMessenger()).thenReturn(new Messenger(mServiceHandler));
+
+ mManager = makeManager();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ mServiceHandler.waitForIdle(mTimeoutMs);
+ mServiceHandler.chan.disconnect();
+ mServiceHandler.stop();
+ if (mManager != null) {
+ mManager.disconnect();
+ }
}
@Test
public void testResolveService() {
- NsdManager manager = makeManager();
+ NsdManager manager = mManager;
NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type");
NsdServiceInfo reply = new NsdServiceInfo("resolved_name", "resolved_type");
@@ -88,7 +104,7 @@ public class NsdManagerTest {
@Test
public void testParallelResolveService() {
- NsdManager manager = makeManager();
+ NsdManager manager = mManager;
NsdServiceInfo request = new NsdServiceInfo("a_name", "a_type");
NsdServiceInfo reply = new NsdServiceInfo("resolved_name", "resolved_type");
@@ -111,7 +127,7 @@ public class NsdManagerTest {
@Test
public void testRegisterService() {
- NsdManager manager = makeManager();
+ NsdManager manager = mManager;
NsdServiceInfo request1 = new NsdServiceInfo("a_name", "a_type");
NsdServiceInfo request2 = new NsdServiceInfo("another_name", "another_type");
@@ -170,7 +186,7 @@ public class NsdManagerTest {
@Test
public void testDiscoverService() {
- NsdManager manager = makeManager();
+ NsdManager manager = mManager;
NsdServiceInfo reply1 = new NsdServiceInfo("a_name", "a_type");
NsdServiceInfo reply2 = new NsdServiceInfo("another_name", "a_type");
@@ -248,7 +264,7 @@ public class NsdManagerTest {
@Test
public void testInvalidCalls() {
- NsdManager manager = new NsdManager(mContext, mService);
+ NsdManager manager = mManager;
NsdManager.RegistrationListener listener1 = mock(NsdManager.RegistrationListener.class);
NsdManager.DiscoveryListener listener2 = mock(NsdManager.DiscoveryListener.class);
@@ -318,9 +334,10 @@ public class NsdManagerTest {
}
int verifyRequest(int expectedMessageType) {
+ mServiceHandler.waitForIdle(mTimeoutMs);
verify(mServiceHandler, timeout(mTimeoutMs)).handleMessage(any());
reset(mServiceHandler);
- Message received = mServiceHandler.lastMessage;
+ Message received = mServiceHandler.getLastMessage();
assertEquals(NsdManager.nameOf(expectedMessageType), NsdManager.nameOf(received.what));
return received.arg2;
}
@@ -331,27 +348,43 @@ public class NsdManagerTest {
// Implements the server side of AsyncChannel connection protocol
public static class MockServiceHandler extends Handler {
- public Context mContext;
+ public final Context context;
public AsyncChannel chan;
- public volatile Message lastMessage;
+ public Message lastMessage;
- MockServiceHandler(Looper looper, Context context) {
- super(looper);
- mContext = context;
+ MockServiceHandler(Looper l, Context c) {
+ super(l);
+ context = c;
}
- @Override
- public void handleMessage(Message msg) {
+ synchronized Message getLastMessage() {
+ return lastMessage;
+ }
+
+ synchronized void setLastMessage(Message msg) {
lastMessage = obtainMessage();
lastMessage.copyFrom(msg);
+ }
+
+ void waitForIdle(long timeoutMs) {
+ waitForIdleHandler(this, timeoutMs);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ setLastMessage(msg);
if (msg.what == AsyncChannel.CMD_CHANNEL_FULL_CONNECTION) {
chan = new AsyncChannel();
- chan.connect(mContext, this, msg.replyTo);
+ chan.connect(context, this, msg.replyTo);
chan.sendMessage(AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED);
}
}
- public static MockServiceHandler create(Context context) {
+ void stop() {
+ getLooper().quitSafely();
+ }
+
+ static MockServiceHandler create(Context context) {
HandlerThread t = new HandlerThread("mock-service-handler");
t.start();
return new MockServiceHandler(t.getLooper(), context);
diff --git a/tests/net/java/com/android/internal/util/TestUtils.java b/tests/net/java/com/android/internal/util/TestUtils.java
index c9fa340dc8b5..6db01d343756 100644
--- a/tests/net/java/com/android/internal/util/TestUtils.java
+++ b/tests/net/java/com/android/internal/util/TestUtils.java
@@ -30,8 +30,7 @@ public final class TestUtils {
* Block until the given Handler thread becomes idle, or until timeoutMs has passed.
*/
public static void waitForIdleHandler(HandlerThread handlerThread, long timeoutMs) {
- // TODO: convert to getThreadHandler once it is available on aosp
- waitForIdleLooper(handlerThread.getLooper(), timeoutMs);
+ waitForIdleHandler(handlerThread.getThreadHandler(), timeoutMs);
}
/**
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index f6481cf59140..a8147380386f 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -64,6 +64,7 @@ import android.net.NetworkInfo.DetailedState;
import android.net.NetworkMisc;
import android.net.NetworkRequest;
import android.net.NetworkSpecifier;
+import android.net.NetworkUtils;
import android.net.RouteInfo;
import android.net.StringNetworkSpecifier;
import android.net.metrics.IpConnectivityLog;
@@ -88,6 +89,7 @@ import android.test.AndroidTestCase;
import android.test.mock.MockContentResolver;
import android.test.suitebuilder.annotation.SmallTest;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.Log;
import android.util.LogPrinter;
@@ -109,7 +111,10 @@ import org.mockito.Spy;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
@@ -304,6 +309,10 @@ public class ConnectivityServiceTest extends AndroidTestCase {
private String mRedirectUrl;
MockNetworkAgent(int transport) {
+ this(transport, new LinkProperties());
+ }
+
+ MockNetworkAgent(int transport, LinkProperties linkProperties) {
final int type = transportToLegacyType(transport);
final String typeName = ConnectivityManager.getNetworkTypeName(type);
mNetworkInfo = new NetworkInfo(type, 0, typeName, "Mock");
@@ -329,7 +338,7 @@ public class ConnectivityServiceTest extends AndroidTestCase {
mHandlerThread.start();
mNetworkAgent = new NetworkAgent(mHandlerThread.getLooper(), mServiceContext,
"Mock-" + typeName, mNetworkInfo, mNetworkCapabilities,
- new LinkProperties(), mScore, new NetworkMisc()) {
+ linkProperties, mScore, new NetworkMisc()) {
@Override
public void unwanted() { mDisconnected.open(); }
@@ -674,7 +683,8 @@ public class ConnectivityServiceTest extends AndroidTestCase {
public WrappedNetworkMonitor(Context context, Handler handler,
NetworkAgentInfo networkAgentInfo, NetworkRequest defaultRequest,
IpConnectivityLog log) {
- super(context, handler, networkAgentInfo, defaultRequest, log);
+ super(context, handler, networkAgentInfo, defaultRequest, log,
+ NetworkMonitor.NetworkMonitorSettings.DEFAULT);
}
@Override
@@ -3338,6 +3348,68 @@ public class ConnectivityServiceTest extends AndroidTestCase {
assertException(() -> { mCm.requestRouteToHostAddress(TYPE_NONE, null); }, unsupported);
}
+ @SmallTest
+ public void testLinkPropertiesEnsuresDirectlyConnectedRoutes() {
+ final NetworkRequest networkRequest = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_WIFI).build();
+ final TestNetworkCallback networkCallback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(networkRequest, networkCallback);
+
+ LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName("wlan0");
+ LinkAddress myIpv4Address = new LinkAddress("192.168.12.3/24");
+ RouteInfo myIpv4DefaultRoute = new RouteInfo((IpPrefix) null,
+ NetworkUtils.numericToInetAddress("192.168.12.1"), lp.getInterfaceName());
+ lp.addLinkAddress(myIpv4Address);
+ lp.addRoute(myIpv4DefaultRoute);
+
+ // Verify direct routes are added when network agent is first registered in
+ // ConnectivityService.
+ MockNetworkAgent networkAgent = new MockNetworkAgent(TRANSPORT_WIFI, lp);
+ networkAgent.connect(true);
+ networkCallback.expectCallback(CallbackState.AVAILABLE, networkAgent);
+ networkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES, networkAgent);
+ CallbackInfo cbi = networkCallback.expectCallback(CallbackState.LINK_PROPERTIES,
+ networkAgent);
+ networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, networkAgent);
+ networkCallback.assertNoCallback();
+ checkDirectlyConnectedRoutes(cbi.arg, Arrays.asList(myIpv4Address),
+ Arrays.asList(myIpv4DefaultRoute));
+ checkDirectlyConnectedRoutes(mCm.getLinkProperties(networkAgent.getNetwork()),
+ Arrays.asList(myIpv4Address), Arrays.asList(myIpv4DefaultRoute));
+
+ // Verify direct routes are added during subsequent link properties updates.
+ LinkProperties newLp = new LinkProperties(lp);
+ LinkAddress myIpv6Address1 = new LinkAddress("fe80::cafe/64");
+ LinkAddress myIpv6Address2 = new LinkAddress("2001:db8::2/64");
+ newLp.addLinkAddress(myIpv6Address1);
+ newLp.addLinkAddress(myIpv6Address2);
+ networkAgent.sendLinkProperties(newLp);
+ cbi = networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, networkAgent);
+ networkCallback.assertNoCallback();
+ checkDirectlyConnectedRoutes(cbi.arg,
+ Arrays.asList(myIpv4Address, myIpv6Address1, myIpv6Address2),
+ Arrays.asList(myIpv4DefaultRoute));
+ mCm.unregisterNetworkCallback(networkCallback);
+ }
+
+ private void checkDirectlyConnectedRoutes(Object callbackObj,
+ Collection<LinkAddress> linkAddresses, Collection<RouteInfo> otherRoutes) {
+ assertTrue(callbackObj instanceof LinkProperties);
+ LinkProperties lp = (LinkProperties) callbackObj;
+
+ Set<RouteInfo> expectedRoutes = new ArraySet<>();
+ expectedRoutes.addAll(otherRoutes);
+ for (LinkAddress address : linkAddresses) {
+ RouteInfo localRoute = new RouteInfo(address, null, lp.getInterfaceName());
+ // Duplicates in linkAddresses are considered failures
+ assertTrue(expectedRoutes.add(localRoute));
+ }
+ List<RouteInfo> observedRoutes = lp.getRoutes();
+ assertEquals(expectedRoutes.size(), observedRoutes.size());
+ assertTrue(observedRoutes.containsAll(expectedRoutes));
+ }
+
private static <T> void assertEmpty(T[] ts) {
int length = ts.length;
assertEquals("expected empty array, but length was " + length, 0, length);
diff --git a/tests/net/java/com/android/server/NsdServiceTest.java b/tests/net/java/com/android/server/NsdServiceTest.java
index 68cb251cf87e..2e49c98ca9b6 100644
--- a/tests/net/java/com/android/server/NsdServiceTest.java
+++ b/tests/net/java/com/android/server/NsdServiceTest.java
@@ -95,6 +95,9 @@ public class NsdServiceTest {
client2.disconnect();
verify(mDaemon, timeout(mTimeoutMs).times(1)).stop();
+
+ client1.disconnect();
+ client2.disconnect();
}
@Test
@@ -131,6 +134,8 @@ public class NsdServiceTest {
// checks that request are cleaned
verifyDaemonCommands("stop-register 2", "stop-discover 3", "stop-resolve 4");
+
+ client.disconnect();
}
NsdService makeService() {
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
index d11565abb917..f72a1c638ed5 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
@@ -16,6 +16,8 @@
package com.android.server.connectivity;
+import static android.net.metrics.INetdEventListener.EVENT_GETADDRINFO;
+import static android.net.metrics.INetdEventListener.EVENT_GETHOSTBYNAME;
import static com.android.server.connectivity.MetricsTestUtil.aBool;
import static com.android.server.connectivity.MetricsTestUtil.aByteArray;
import static com.android.server.connectivity.MetricsTestUtil.aLong;
@@ -31,29 +33,41 @@ import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClas
import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.ETHERNET;
import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.MULTIPLE;
import static com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.WIFI;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
import android.net.ConnectivityMetricsEvent;
import android.net.metrics.ApfProgramEvent;
import android.net.metrics.ApfStats;
+import android.net.metrics.ConnectStats;
import android.net.metrics.DefaultNetworkEvent;
import android.net.metrics.DhcpClientEvent;
import android.net.metrics.DhcpErrorEvent;
import android.net.metrics.DnsEvent;
+import android.net.metrics.DnsEvent;
import android.net.metrics.IpManagerEvent;
import android.net.metrics.IpReachabilityEvent;
import android.net.metrics.NetworkEvent;
import android.net.metrics.RaEvent;
import android.net.metrics.ValidationProbeEvent;
+import android.net.metrics.WakeupStats;
+import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
+
import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
+
import java.util.Arrays;
import java.util.List;
-import junit.framework.TestCase;
+
+import org.junit.runner.RunWith;
+import org.junit.Test;
// TODO: instead of comparing textpb to textpb, parse textpb and compare proto to proto.
-public class IpConnectivityEventBuilderTest extends TestCase {
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class IpConnectivityEventBuilderTest {
- @SmallTest
+ @Test
public void testLinkLayerInferrence() {
ConnectivityMetricsEvent ev = describeIpEvent(
aType(IpReachabilityEvent.class),
@@ -182,7 +196,7 @@ public class IpConnectivityEventBuilderTest extends TestCase {
verifySerialization(want, ev);
}
- @SmallTest
+ @Test
public void testDefaultNetworkEventSerialization() {
ConnectivityMetricsEvent ev = describeIpEvent(
aType(DefaultNetworkEvent.class),
@@ -201,9 +215,14 @@ public class IpConnectivityEventBuilderTest extends TestCase {
" time_ms: 1",
" transports: 0",
" default_network_event <",
+ " default_network_duration_ms: 0",
+ " final_score: 0",
+ " initial_score: 0",
+ " ip_support: 0",
" network_id <",
" network_id: 102",
" >",
+ " no_default_network_duration_ms: 0",
" previous_network_id <",
" network_id: 101",
" >",
@@ -218,7 +237,7 @@ public class IpConnectivityEventBuilderTest extends TestCase {
verifySerialization(want, ev);
}
- @SmallTest
+ @Test
public void testDhcpClientEventSerialization() {
ConnectivityMetricsEvent ev = describeIpEvent(
aType(DhcpClientEvent.class),
@@ -244,7 +263,7 @@ public class IpConnectivityEventBuilderTest extends TestCase {
verifySerialization(want, ev);
}
- @SmallTest
+ @Test
public void testDhcpErrorEventSerialization() {
ConnectivityMetricsEvent ev = describeIpEvent(
aType(DhcpErrorEvent.class),
@@ -269,7 +288,7 @@ public class IpConnectivityEventBuilderTest extends TestCase {
verifySerialization(want, ev);
}
- @SmallTest
+ @Test
public void testIpManagerEventSerialization() {
ConnectivityMetricsEvent ev = describeIpEvent(
aType(IpManagerEvent.class),
@@ -295,7 +314,7 @@ public class IpConnectivityEventBuilderTest extends TestCase {
verifySerialization(want, ev);
}
- @SmallTest
+ @Test
public void testIpReachabilityEventSerialization() {
ConnectivityMetricsEvent ev = describeIpEvent(
aType(IpReachabilityEvent.class),
@@ -319,7 +338,7 @@ public class IpConnectivityEventBuilderTest extends TestCase {
verifySerialization(want, ev);
}
- @SmallTest
+ @Test
public void testNetworkEventSerialization() {
ConnectivityMetricsEvent ev = describeIpEvent(
aType(NetworkEvent.class),
@@ -348,7 +367,7 @@ public class IpConnectivityEventBuilderTest extends TestCase {
verifySerialization(want, ev);
}
- @SmallTest
+ @Test
public void testValidationProbeEventSerialization() {
ConnectivityMetricsEvent ev = describeIpEvent(
aType(ValidationProbeEvent.class),
@@ -375,7 +394,7 @@ public class IpConnectivityEventBuilderTest extends TestCase {
verifySerialization(want, ev);
}
- @SmallTest
+ @Test
public void testApfProgramEventSerialization() {
ConnectivityMetricsEvent ev = describeIpEvent(
aType(ApfProgramEvent.class),
@@ -409,7 +428,7 @@ public class IpConnectivityEventBuilderTest extends TestCase {
verifySerialization(want, ev);
}
- @SmallTest
+ @Test
public void testApfStatsSerialization() {
ConnectivityMetricsEvent ev = describeIpEvent(
aType(ApfStats.class),
@@ -442,6 +461,8 @@ public class IpConnectivityEventBuilderTest extends TestCase {
" program_updates_all: 7",
" program_updates_allowing_multicast: 3",
" received_ras: 10",
+ " total_packet_dropped: 0",
+ " total_packet_processed: 0",
" zero_lifetime_ras: 1",
" >",
">",
@@ -450,7 +471,7 @@ public class IpConnectivityEventBuilderTest extends TestCase {
verifySerialization(want, ev);
}
- @SmallTest
+ @Test
public void testRaEventSerialization() {
ConnectivityMetricsEvent ev = describeIpEvent(
aType(RaEvent.class),
@@ -483,11 +504,49 @@ public class IpConnectivityEventBuilderTest extends TestCase {
verifySerialization(want, ev);
}
+ @Test
+ public void testWakeupStatsSerialization() {
+ WakeupStats stats = new WakeupStats("wlan0");
+ stats.totalWakeups = 14;
+ stats.applicationWakeups = 5;
+ stats.nonApplicationWakeups = 1;
+ stats.rootWakeups = 2;
+ stats.systemWakeups = 3;
+ stats.unroutedWakeups = 3;
+
+ IpConnectivityEvent got = IpConnectivityEventBuilder.toProto(stats);
+ String want = String.join("\n",
+ "dropped_events: 0",
+ "events <",
+ " if_name: \"\"",
+ " link_layer: 4",
+ " network_id: 0",
+ " time_ms: 0",
+ " transports: 0",
+ " wakeup_stats <",
+ " application_wakeups: 5",
+ " duration_sec: 0",
+ " non_application_wakeups: 1",
+ " root_wakeups: 2",
+ " system_wakeups: 3",
+ " total_wakeups: 14",
+ " unrouted_wakeups: 3",
+ " >",
+ ">",
+ "version: 2\n");
+
+ verifySerialization(want, got);
+ }
+
static void verifySerialization(String want, ConnectivityMetricsEvent... input) {
+ List<IpConnectivityEvent> protoInput =
+ IpConnectivityEventBuilder.toProto(Arrays.asList(input));
+ verifySerialization(want, protoInput.toArray(new IpConnectivityEvent[0]));
+ }
+
+ static void verifySerialization(String want, IpConnectivityEvent... input) {
try {
- List<IpConnectivityEvent> proto =
- IpConnectivityEventBuilder.toProto(Arrays.asList(input));
- byte[] got = IpConnectivityEventBuilder.serialize(0, proto);
+ byte[] got = IpConnectivityEventBuilder.serialize(0, Arrays.asList(input));
IpConnectivityLog log = IpConnectivityLog.parseFrom(got);
assertEquals(want, log.toString());
} catch (Exception e) {
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
index e01469b1825e..ede5988cdc6d 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
@@ -224,6 +224,15 @@ public class IpConnectivityMetricsTest {
dnsEvent(101, EVENT_GETADDRINFO, 0, 56);
dnsEvent(101, EVENT_GETHOSTBYNAME, 0, 34);
+ // iface, uid
+ wakeupEvent("wlan0", 1000);
+ wakeupEvent("rmnet0", 10123);
+ wakeupEvent("wlan0", 1000);
+ wakeupEvent("rmnet0", 10008);
+ wakeupEvent("wlan0", -1);
+ wakeupEvent("wlan0", 10008);
+ wakeupEvent("rmnet0", 1000);
+
String want = String.join("\n",
"dropped_events: 0",
"events <",
@@ -256,9 +265,14 @@ public class IpConnectivityMetricsTest {
" time_ms: 300",
" transports: 0",
" default_network_event <",
+ " default_network_duration_ms: 0",
+ " final_score: 0",
+ " initial_score: 0",
+ " ip_support: 0",
" network_id <",
" network_id: 102",
" >",
+ " no_default_network_duration_ms: 0",
" previous_network_id <",
" network_id: 101",
" >",
@@ -308,6 +322,8 @@ public class IpConnectivityMetricsTest {
" program_updates_all: 7",
" program_updates_allowing_multicast: 3",
" received_ras: 10",
+ " total_packet_dropped: 0",
+ " total_packet_processed: 0",
" zero_lifetime_ras: 1",
" >",
">",
@@ -367,6 +383,10 @@ public class IpConnectivityMetricsTest {
" event_types: 1",
" event_types: 1",
" event_types: 2",
+ " getaddrinfo_error_count: 0",
+ " getaddrinfo_query_count: 0",
+ " gethostbyname_error_count: 0",
+ " gethostbyname_query_count: 0",
" latencies_ms: 3456",
" latencies_ms: 45",
" latencies_ms: 638",
@@ -384,12 +404,48 @@ public class IpConnectivityMetricsTest {
" dns_lookup_batch <",
" event_types: 1",
" event_types: 2",
+ " getaddrinfo_error_count: 0",
+ " getaddrinfo_query_count: 0",
+ " gethostbyname_error_count: 0",
+ " gethostbyname_query_count: 0",
" latencies_ms: 56",
" latencies_ms: 34",
" return_codes: 0",
" return_codes: 0",
" >",
">",
+ "events <",
+ " if_name: \"\"",
+ " link_layer: 2",
+ " network_id: 0",
+ " time_ms: 0",
+ " transports: 0",
+ " wakeup_stats <",
+ " application_wakeups: 2",
+ " duration_sec: 0",
+ " non_application_wakeups: 0",
+ " root_wakeups: 0",
+ " system_wakeups: 1",
+ " total_wakeups: 3",
+ " unrouted_wakeups: 0",
+ " >",
+ ">",
+ "events <",
+ " if_name: \"\"",
+ " link_layer: 4",
+ " network_id: 0",
+ " time_ms: 0",
+ " transports: 0",
+ " wakeup_stats <",
+ " application_wakeups: 1",
+ " duration_sec: 0",
+ " non_application_wakeups: 0",
+ " root_wakeups: 0",
+ " system_wakeups: 2",
+ " total_wakeups: 4",
+ " unrouted_wakeups: 1",
+ " >",
+ ">",
"version: 2\n");
verifySerialization(want, getdump("flush"));
@@ -410,6 +466,11 @@ public class IpConnectivityMetricsTest {
mNetdListener.onDnsEvent(netId, type, result, latency, "", null, 0, 0);
}
+ void wakeupEvent(String iface, int uid) throws Exception {
+ String prefix = NetdEventListenerService.WAKEUP_EVENT_IFACE_PREFIX + iface;
+ mNetdListener.onWakeupEvent(prefix, uid, uid, 0);
+ }
+
List<ConnectivityMetricsEvent> verifyEvents(int n, int timeoutMs) throws Exception {
ArgumentCaptor<ConnectivityMetricsEvent> captor =
ArgumentCaptor.forClass(ConnectivityMetricsEvent.class);
diff --git a/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
new file mode 100644
index 000000000000..e3f46a40e2b1
--- /dev/null
+++ b/tests/net/java/com/android/server/connectivity/Nat464XlatTest.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.when;
+
+import android.net.ConnectivityManager;
+import android.net.InterfaceConfiguration;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
+import android.net.NetworkInfo;
+import android.os.Handler;
+import android.os.INetworkManagementService;
+import android.os.test.TestLooper;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.server.ConnectivityService;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class Nat464XlatTest {
+
+ static final String BASE_IFACE = "test0";
+ static final String STACKED_IFACE = "v4-test0";
+ static final LinkAddress ADDR = new LinkAddress("192.0.2.5/29");
+
+ @Mock ConnectivityService mConnectivity;
+ @Mock INetworkManagementService mNms;
+ @Mock InterfaceConfiguration mConfig;
+ @Mock NetworkAgentInfo mNai;
+
+ TestLooper mLooper;
+ Handler mHandler;
+
+ Nat464Xlat makeNat464Xlat() {
+ return new Nat464Xlat(mNms, mNai);
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ mLooper = new TestLooper();
+ mHandler = new Handler(mLooper.getLooper());
+
+ MockitoAnnotations.initMocks(this);
+
+ mNai.linkProperties = new LinkProperties();
+ mNai.linkProperties.setInterfaceName(BASE_IFACE);
+ mNai.networkInfo = new NetworkInfo(null);
+ mNai.networkInfo.setType(ConnectivityManager.TYPE_WIFI);
+ when(mNai.connService()).thenReturn(mConnectivity);
+ when(mNai.handler()).thenReturn(mHandler);
+
+ when(mNms.getInterfaceConfig(eq(STACKED_IFACE))).thenReturn(mConfig);
+ when(mConfig.getLinkAddress()).thenReturn(ADDR);
+ }
+
+ @Test
+ public void testNormalStartAndStop() throws Exception {
+ Nat464Xlat nat = makeNat464Xlat();
+ ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class);
+
+ // ConnectivityService starts clat.
+ nat.start();
+
+ verify(mNms).registerObserver(eq(nat));
+ verify(mNms).startClatd(eq(BASE_IFACE));
+
+ // Stacked interface up notification arrives.
+ nat.interfaceLinkStateChanged(STACKED_IFACE, true);
+ mLooper.dispatchNext();
+
+ verify(mNms).getInterfaceConfig(eq(STACKED_IFACE));
+ verify(mNms).setInterfaceIpv6NdOffload(eq(BASE_IFACE), eq(false));
+ verify(mConnectivity).handleUpdateLinkProperties(eq(mNai), c.capture());
+ assertFalse(c.getValue().getStackedLinks().isEmpty());
+ assertTrue(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
+ assertRunning(nat);
+
+ // ConnectivityService stops clat (Network disconnects, IPv4 addr appears, ...).
+ nat.stop();
+
+ verify(mNms).stopClatd(eq(BASE_IFACE));
+ verify(mNms).setInterfaceIpv6NdOffload(eq(BASE_IFACE), eq(true));
+
+ // Stacked interface removed notification arrives.
+ nat.interfaceRemoved(STACKED_IFACE);
+ mLooper.dispatchNext();
+
+ verify(mNms).unregisterObserver(eq(nat));
+ verify(mConnectivity, times(2)).handleUpdateLinkProperties(eq(mNai), c.capture());
+ assertTrue(c.getValue().getStackedLinks().isEmpty());
+ assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
+ assertIdle(nat);
+
+ verifyNoMoreInteractions(mNms, mConnectivity);
+ }
+
+ @Test
+ public void testClatdCrashWhileRunning() throws Exception {
+ Nat464Xlat nat = makeNat464Xlat();
+ ArgumentCaptor<LinkProperties> c = ArgumentCaptor.forClass(LinkProperties.class);
+
+ // ConnectivityService starts clat.
+ nat.start();
+
+ verify(mNms).registerObserver(eq(nat));
+ verify(mNms).startClatd(eq(BASE_IFACE));
+
+ // Stacked interface up notification arrives.
+ nat.interfaceLinkStateChanged(STACKED_IFACE, true);
+ mLooper.dispatchNext();
+
+ verify(mNms).getInterfaceConfig(eq(STACKED_IFACE));
+ verify(mNms).setInterfaceIpv6NdOffload(eq(BASE_IFACE), eq(false));
+ verify(mConnectivity, times(1)).handleUpdateLinkProperties(eq(mNai), c.capture());
+ assertFalse(c.getValue().getStackedLinks().isEmpty());
+ assertTrue(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
+ assertRunning(nat);
+
+ // Stacked interface removed notification arrives (clatd crashed, ...).
+ nat.interfaceRemoved(STACKED_IFACE);
+ mLooper.dispatchNext();
+
+ verify(mNms).unregisterObserver(eq(nat));
+ verify(mNms).stopClatd(eq(BASE_IFACE));
+ verify(mNms).setInterfaceIpv6NdOffload(eq(BASE_IFACE), eq(true));
+ verify(mConnectivity, times(2)).handleUpdateLinkProperties(eq(mNai), c.capture());
+ assertTrue(c.getValue().getStackedLinks().isEmpty());
+ assertFalse(c.getValue().getAllInterfaceNames().contains(STACKED_IFACE));
+ assertIdle(nat);
+
+ // ConnectivityService stops clat: no-op.
+ nat.stop();
+
+ verifyNoMoreInteractions(mNms, mConnectivity);
+ }
+
+ @Test
+ public void testStopBeforeClatdStarts() throws Exception {
+ Nat464Xlat nat = makeNat464Xlat();
+
+ // ConnectivityService starts clat.
+ nat.start();
+
+ verify(mNms).registerObserver(eq(nat));
+ verify(mNms).startClatd(eq(BASE_IFACE));
+
+ // ConnectivityService immediately stops clat (Network disconnects, IPv4 addr appears, ...)
+ nat.stop();
+
+ verify(mNms).unregisterObserver(eq(nat));
+ verify(mNms).stopClatd(eq(BASE_IFACE));
+ assertIdle(nat);
+
+ // In-flight interface up notification arrives: no-op
+ nat.interfaceLinkStateChanged(STACKED_IFACE, true);
+ mLooper.dispatchNext();
+
+
+ // Interface removed notification arrives after stopClatd() takes effect: no-op.
+ nat.interfaceRemoved(STACKED_IFACE);
+ mLooper.dispatchNext();
+
+ assertIdle(nat);
+
+ verifyNoMoreInteractions(mNms, mConnectivity);
+ }
+
+ @Test
+ public void testStopAndClatdNeverStarts() throws Exception {
+ Nat464Xlat nat = makeNat464Xlat();
+
+ // ConnectivityService starts clat.
+ nat.start();
+
+ verify(mNms).registerObserver(eq(nat));
+ verify(mNms).startClatd(eq(BASE_IFACE));
+
+ // ConnectivityService immediately stops clat (Network disconnects, IPv4 addr appears, ...)
+ nat.stop();
+
+ verify(mNms).unregisterObserver(eq(nat));
+ verify(mNms).stopClatd(eq(BASE_IFACE));
+ assertIdle(nat);
+
+ verifyNoMoreInteractions(mNms, mConnectivity);
+ }
+
+ static void assertIdle(Nat464Xlat nat) {
+ assertTrue("Nat464Xlat was not IDLE", !nat.isStarted());
+ }
+
+ static void assertRunning(Nat464Xlat nat) {
+ assertTrue("Nat464Xlat was not RUNNING", nat.isRunning());
+ }
+}
diff --git a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
index f98ab3d06921..2b105e5c92ee 100644
--- a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
+++ b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
@@ -19,6 +19,7 @@ package com.android.server.connectivity;
import static android.net.metrics.INetdEventListener.EVENT_GETADDRINFO;
import static android.net.metrics.INetdEventListener.EVENT_GETHOSTBYNAME;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
@@ -37,9 +38,11 @@ import android.support.test.runner.AndroidJUnit4;
import android.system.OsConstants;
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Base64;
+
import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.DNSLookupBatch;
import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityLog;
+
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
@@ -47,6 +50,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -75,6 +79,118 @@ public class NetdEventListenerServiceTest {
}
@Test
+ public void testWakeupEventLogging() throws Exception {
+ final int BUFFER_LENGTH = NetdEventListenerService.WAKEUP_EVENT_BUFFER_LENGTH;
+
+ // Assert no events
+ String[] events1 = listNetdEvent();
+ assertEquals(new String[]{""}, events1);
+
+ long now = System.currentTimeMillis();
+ String prefix = "iface:wlan0";
+ int[] uids = { 10001, 10002, 10004, 1000, 10052, 10023, 10002, 10123, 10004 };
+ for (int uid : uids) {
+ mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, now);
+ }
+
+ String[] events2 = listNetdEvent();
+ int expectedLength2 = uids.length + 1; // +1 for the WakeupStats line
+ assertEquals(expectedLength2, events2.length);
+ assertContains(events2[0], "WakeupStats");
+ assertContains(events2[0], "wlan0");
+ for (int i = 0; i < uids.length; i++) {
+ String got = events2[i+1];
+ assertContains(got, "WakeupEvent");
+ assertContains(got, "wlan0");
+ assertContains(got, "uid: " + uids[i]);
+ }
+
+ int uid = 20000;
+ for (int i = 0; i < BUFFER_LENGTH * 2; i++) {
+ long ts = now + 10;
+ mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, ts);
+ }
+
+ String[] events3 = listNetdEvent();
+ int expectedLength3 = BUFFER_LENGTH + 1; // +1 for the WakeupStats line
+ assertEquals(expectedLength3, events3.length);
+ assertContains(events2[0], "WakeupStats");
+ assertContains(events2[0], "wlan0");
+ for (int i = 1; i < expectedLength3; i++) {
+ String got = events3[i];
+ assertContains(got, "WakeupEvent");
+ assertContains(got, "wlan0");
+ assertContains(got, "uid: " + uid);
+ }
+
+ uid = 45678;
+ mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, now);
+
+ String[] events4 = listNetdEvent();
+ String lastEvent = events4[events4.length - 1];
+ assertContains(lastEvent, "WakeupEvent");
+ assertContains(lastEvent, "wlan0");
+ assertContains(lastEvent, "uid: " + uid);
+ }
+
+ @Test
+ public void testWakeupStatsLogging() throws Exception {
+ wakeupEvent("wlan0", 1000);
+ wakeupEvent("rmnet0", 10123);
+ wakeupEvent("wlan0", 1000);
+ wakeupEvent("rmnet0", 10008);
+ wakeupEvent("wlan0", -1);
+ wakeupEvent("wlan0", 10008);
+ wakeupEvent("rmnet0", 1000);
+ wakeupEvent("wlan0", 10004);
+ wakeupEvent("wlan0", 1000);
+ wakeupEvent("wlan0", 0);
+ wakeupEvent("wlan0", -1);
+ wakeupEvent("rmnet0", 10052);
+ wakeupEvent("wlan0", 0);
+ wakeupEvent("rmnet0", 1000);
+ wakeupEvent("wlan0", 1010);
+
+ String got = flushStatistics();
+ String want = String.join("\n",
+ "dropped_events: 0",
+ "events <",
+ " if_name: \"\"",
+ " link_layer: 2",
+ " network_id: 0",
+ " time_ms: 0",
+ " transports: 0",
+ " wakeup_stats <",
+ " application_wakeups: 3",
+ " duration_sec: 0",
+ " non_application_wakeups: 0",
+ " root_wakeups: 0",
+ " system_wakeups: 2",
+ " total_wakeups: 5",
+ " unrouted_wakeups: 0",
+ " >",
+ ">",
+ "events <",
+ " if_name: \"\"",
+ " link_layer: 4",
+ " network_id: 0",
+ " time_ms: 0",
+ " transports: 0",
+ " wakeup_stats <",
+ " application_wakeups: 2",
+ " duration_sec: 0",
+ " non_application_wakeups: 1",
+ " root_wakeups: 2",
+ " system_wakeups: 3",
+ " total_wakeups: 10",
+ " unrouted_wakeups: 2",
+ " >",
+ ">",
+ "version: 2\n");
+ assertEquals(want, got);
+ }
+
+ @Test
public void testDnsLogging() throws Exception {
asyncDump(100);
@@ -111,6 +227,10 @@ public class NetdEventListenerServiceTest {
" event_types: 1",
" event_types: 2",
" event_types: 2",
+ " getaddrinfo_error_count: 0",
+ " getaddrinfo_query_count: 0",
+ " gethostbyname_error_count: 0",
+ " gethostbyname_query_count: 0",
" latencies_ms: 3456",
" latencies_ms: 267",
" latencies_ms: 1230",
@@ -142,6 +262,10 @@ public class NetdEventListenerServiceTest {
" event_types: 2",
" event_types: 1",
" event_types: 1",
+ " getaddrinfo_error_count: 0",
+ " getaddrinfo_query_count: 0",
+ " gethostbyname_error_count: 0",
+ " gethostbyname_query_count: 0",
" latencies_ms: 56",
" latencies_ms: 78",
" latencies_ms: 14",
@@ -289,6 +413,11 @@ public class NetdEventListenerServiceTest {
mNetdEventListenerService.onDnsEvent(netId, type, result, latency, "", null, 0, 0);
}
+ void wakeupEvent(String iface, int uid) throws Exception {
+ String prefix = NetdEventListenerService.WAKEUP_EVENT_IFACE_PREFIX + iface;
+ mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, 0);
+ }
+
void asyncDump(long durationMs) throws Exception {
final long stop = System.currentTimeMillis() + durationMs;
final PrintWriter pw = new PrintWriter(new FileOutputStream("/dev/null"));
@@ -321,4 +450,15 @@ public class NetdEventListenerServiceTest {
}
return log.toString();
}
+
+ String[] listNetdEvent() throws Exception {
+ StringWriter buffer = new StringWriter();
+ PrintWriter writer = new PrintWriter(buffer);
+ mNetdEventListenerService.list(writer);
+ return buffer.toString().split("\\n");
+ }
+
+ static void assertContains(String got, String want) {
+ assertTrue(got + " did not contain \"" + want + "\"", got.contains(want));
+ }
}
diff --git a/tests/net/java/com/android/server/connectivity/NetworkMonitorTest.java b/tests/net/java/com/android/server/connectivity/NetworkMonitorTest.java
new file mode 100644
index 000000000000..27a897d175a2
--- /dev/null
+++ b/tests/net/java/com/android/server/connectivity/NetworkMonitorTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.net.Network;
+import android.net.NetworkRequest;
+import android.net.metrics.IpConnectivityLog;
+import android.net.wifi.WifiManager;
+import android.os.Handler;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.TelephonyManager;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class NetworkMonitorTest {
+
+ static final int TEST_ID = 60; // should be less than min netid 100
+
+ @Mock Context mContext;
+ @Mock Handler mHandler;
+ @Mock IpConnectivityLog mLogger;
+ @Mock NetworkAgentInfo mAgent;
+ @Mock NetworkMonitor.NetworkMonitorSettings mSettings;
+ @Mock NetworkRequest mRequest;
+ @Mock TelephonyManager mTelephony;
+ @Mock WifiManager mWifi;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ when(mAgent.network()).thenReturn(new Network(TEST_ID));
+ when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephony);
+ when(mContext.getSystemService(Context.WIFI_SERVICE)).thenReturn(mWifi);
+ }
+
+ NetworkMonitor makeMonitor() {
+ return new NetworkMonitor(mContext, mHandler, mAgent, mRequest, mLogger, mSettings);
+ }
+
+ @Test
+ public void testCreatingNetworkMonitor() {
+ NetworkMonitor monitor = makeMonitor();
+ }
+}
+
diff --git a/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java b/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java
index d7739518ef2d..2199a134d657 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/OffloadControllerTest.java
@@ -32,12 +32,13 @@ import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -68,6 +69,7 @@ import java.util.Set;
import org.junit.After;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.runner.RunWith;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
@@ -144,8 +146,8 @@ public class OffloadControllerTest {
return offload;
}
- // TODO: Restore when FakeSettingsProvider.clearSettingsProvider() is available.
- // @Test
+ @Test
+ @Ignore("Restore when FakeSettingsProvider.clearSettingsProvider() is available.")
public void testNoSettingsValueDefaultDisabledDoesNotStart() throws Exception {
setupFunctioningHardwareInterface();
when(mHardware.getDefaultTetherOffloadDisabled()).thenReturn(1);
@@ -165,8 +167,8 @@ public class OffloadControllerTest {
inOrder.verifyNoMoreInteractions();
}
- // TODO: Restore when FakeSettingsProvider.clearSettingsProvider() is available.
- // @Test
+ @Test
+ @Ignore("Restore when FakeSettingsProvider.clearSettingsProvider() is available.")
public void testNoSettingsValueDefaultEnabledDoesStart() throws Exception {
setupFunctioningHardwareInterface();
when(mHardware.getDefaultTetherOffloadDisabled()).thenReturn(0);
@@ -440,6 +442,9 @@ public class OffloadControllerTest {
ethernetStats.txBytes = 100000;
when(mHardware.getForwardedStats(eq(ethernetIface))).thenReturn(ethernetStats);
offload.setUpstreamLinkProperties(null);
+ // Expect that we first clear the HAL's upstream parameters.
+ inOrder.verify(mHardware, times(1)).setUpstreamParameters(
+ eq(""), eq("0.0.0.0"), eq("0.0.0.0"), eq(null));
// Expect that we fetch stats from the previous upstream.
inOrder.verify(mHardware, times(1)).getForwardedStats(eq(ethernetIface));
@@ -449,8 +454,6 @@ public class OffloadControllerTest {
waitForIdle();
// There is no current upstream, so no stats are fetched.
inOrder.verify(mHardware, never()).getForwardedStats(any());
- inOrder.verify(mHardware, times(1)).setUpstreamParameters(
- eq(null), eq(null), eq(null), eq(null));
inOrder.verifyNoMoreInteractions();
assertEquals(2, stats.size());
@@ -625,6 +628,73 @@ public class OffloadControllerTest {
inOrder.verifyNoMoreInteractions();
}
+ @Test
+ public void testControlCallbackOnStoppedUnsupportedFetchesAllStats() throws Exception {
+ setupFunctioningHardwareInterface();
+ enableOffload();
+
+ final OffloadController offload = makeOffloadController();
+ offload.start();
+
+ // Pretend to set a few different upstreams (only the interface name
+ // matters for this test; we're ignoring IP and route information).
+ final LinkProperties upstreamLp = new LinkProperties();
+ for (String ifname : new String[]{RMNET0, WLAN0, RMNET0}) {
+ upstreamLp.setInterfaceName(ifname);
+ offload.setUpstreamLinkProperties(upstreamLp);
+ }
+
+ // Clear invocation history, especially the getForwardedStats() calls
+ // that happen with setUpstreamParameters().
+ clearInvocations(mHardware);
+
+ OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue();
+ callback.onStoppedUnsupported();
+
+ // Verify forwarded stats behaviour.
+ verify(mHardware, times(1)).getForwardedStats(eq(RMNET0));
+ verify(mHardware, times(1)).getForwardedStats(eq(WLAN0));
+ verifyNoMoreInteractions(mHardware);
+ verify(mNMService, times(1)).tetherLimitReached(mTetherStatsProviderCaptor.getValue());
+ verifyNoMoreInteractions(mNMService);
+ }
+
+ @Test
+ public void testControlCallbackOnSupportAvailableFetchesAllStatsAndPushesAllParameters()
+ throws Exception {
+ setupFunctioningHardwareInterface();
+ enableOffload();
+
+ final OffloadController offload = makeOffloadController();
+ offload.start();
+
+ // Pretend to set a few different upstreams (only the interface name
+ // matters for this test; we're ignoring IP and route information).
+ final LinkProperties upstreamLp = new LinkProperties();
+ for (String ifname : new String[]{RMNET0, WLAN0, RMNET0}) {
+ upstreamLp.setInterfaceName(ifname);
+ offload.setUpstreamLinkProperties(upstreamLp);
+ }
+
+ // Clear invocation history, especially the getForwardedStats() calls
+ // that happen with setUpstreamParameters().
+ clearInvocations(mHardware);
+
+ OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue();
+ callback.onSupportAvailable();
+
+ // Verify forwarded stats behaviour.
+ verify(mHardware, times(1)).getForwardedStats(eq(RMNET0));
+ verify(mHardware, times(1)).getForwardedStats(eq(WLAN0));
+ verify(mNMService, times(1)).tetherLimitReached(mTetherStatsProviderCaptor.getValue());
+ verifyNoMoreInteractions(mNMService);
+
+ // TODO: verify local prefixes and downstreams are also pushed to the HAL.
+ verify(mHardware, times(1)).setUpstreamParameters(eq(RMNET0), any(), any(), any());
+ verify(mHardware, times(1)).setDataLimit(eq(RMNET0), anyLong());
+ verifyNoMoreInteractions(mHardware);
+ }
+
private static void assertArrayListContains(ArrayList<String> list, String... elems) {
for (String element : elems) {
assertTrue(list.contains(element));
diff --git a/tools/aapt/Android.bp b/tools/aapt/Android.bp
new file mode 100644
index 000000000000..e26c9c3fdf63
--- /dev/null
+++ b/tools/aapt/Android.bp
@@ -0,0 +1,115 @@
+//
+// Copyright (C) 2014 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// ==========================================================
+// Setup some common variables for the different build
+// targets here.
+// ==========================================================
+
+cc_defaults {
+ name: "aapt_defaults",
+
+ static_libs: [
+ "libandroidfw",
+ "libpng",
+ "libutils",
+ "liblog",
+ "libcutils",
+ "libexpat",
+ "libziparchive",
+ "libbase",
+ "libz",
+ ],
+ group_static_libs: true,
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ target: {
+ windows: {
+ enabled: true,
+ },
+ },
+
+ // This tool is prebuilt if we're doing an app-only build.
+ product_variables: {
+ pdk: {
+ enabled: false,
+ },
+ unbundled_build: {
+ enabled: false,
+ },
+ },
+}
+
+// ==========================================================
+// Build the host static library: libaapt
+// ==========================================================
+cc_library_host_static {
+ name: "libaapt",
+ defaults: ["aapt_defaults"],
+ target: {
+ darwin: {
+ cflags: ["-D_DARWIN_UNLIMITED_STREAMS"],
+ },
+ },
+ cflags: [
+ "-Wno-format-y2k",
+ "-DSTATIC_ANDROIDFW_FOR_TOOLS",
+ ],
+
+ srcs: [
+ "AaptAssets.cpp",
+ "AaptConfig.cpp",
+ "AaptUtil.cpp",
+ "AaptXml.cpp",
+ "ApkBuilder.cpp",
+ "Command.cpp",
+ "CrunchCache.cpp",
+ "FileFinder.cpp",
+ "Images.cpp",
+ "Package.cpp",
+ "pseudolocalize.cpp",
+ "Resource.cpp",
+ "ResourceFilter.cpp",
+ "ResourceIdCache.cpp",
+ "ResourceTable.cpp",
+ "SourcePos.cpp",
+ "StringPool.cpp",
+ "WorkQueue.cpp",
+ "XMLNode.cpp",
+ "ZipEntry.cpp",
+ "ZipFile.cpp",
+ ],
+}
+
+// ==========================================================
+// Build the host tests: libaapt_tests
+// ==========================================================
+cc_test_host {
+ name: "libaapt_tests",
+ defaults: ["aapt_defaults"],
+ srcs: [
+ "tests/AaptConfig_test.cpp",
+ "tests/AaptGroupEntry_test.cpp",
+ "tests/Pseudolocales_test.cpp",
+ "tests/ResourceFilter_test.cpp",
+ "tests/ResourceTable_test.cpp",
+ ],
+ static_libs: ["libaapt"],
+}
diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk
index 04f46d9b27fc..7bcf631da652 100644
--- a/tools/aapt/Android.mk
+++ b/tools/aapt/Android.mk
@@ -14,7 +14,6 @@
# limitations under the License.
#
-# This tool is prebuilt if we're doing an app-only build.
ifeq ($(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK)),)
# ==========================================================
@@ -23,37 +22,6 @@ ifeq ($(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK)),)
# ==========================================================
LOCAL_PATH:= $(call my-dir)
-aaptMain := Main.cpp
-aaptSources := \
- AaptAssets.cpp \
- AaptConfig.cpp \
- AaptUtil.cpp \
- AaptXml.cpp \
- ApkBuilder.cpp \
- Command.cpp \
- CrunchCache.cpp \
- FileFinder.cpp \
- Images.cpp \
- Package.cpp \
- pseudolocalize.cpp \
- Resource.cpp \
- ResourceFilter.cpp \
- ResourceIdCache.cpp \
- ResourceTable.cpp \
- SourcePos.cpp \
- StringPool.cpp \
- WorkQueue.cpp \
- XMLNode.cpp \
- ZipEntry.cpp \
- ZipFile.cpp
-
-aaptTests := \
- tests/AaptConfig_test.cpp \
- tests/AaptGroupEntry_test.cpp \
- tests/Pseudolocales_test.cpp \
- tests/ResourceFilter_test.cpp \
- tests/ResourceTable_test.cpp
-
aaptHostStaticLibs := \
libandroidfw \
libpng \
@@ -62,35 +30,10 @@ aaptHostStaticLibs := \
libcutils \
libexpat \
libziparchive \
- libbase
-
-aaptCFlags := -DAAPT_VERSION=\"$(BUILD_NUMBER_FROM_FILE)\"
-aaptCFlags += -Wall -Werror
+ libbase \
+ libz
-aaptHostLdLibs_linux := -lrt -ldl -lpthread
-
-# Statically link libz for MinGW (Win SDK under Linux),
-# and dynamically link for all others.
-aaptHostStaticLibs_windows := libz
-aaptHostLdLibs_linux += -lz
-aaptHostLdLibs_darwin := -lz
-
-
-# ==========================================================
-# Build the host static library: libaapt
-# ==========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libaapt
-LOCAL_MODULE_HOST_OS := darwin linux windows
-LOCAL_CFLAGS := -Wno-format-y2k -DSTATIC_ANDROIDFW_FOR_TOOLS $(aaptCFlags)
-LOCAL_CPPFLAGS := $(aaptCppFlags)
-LOCAL_CFLAGS_darwin := -D_DARWIN_UNLIMITED_STREAMS
-LOCAL_SRC_FILES := $(aaptSources)
-LOCAL_STATIC_LIBRARIES := $(aaptHostStaticLibs)
-LOCAL_STATIC_LIBRARIES_windows := $(aaptHostStaticLibs_windows)
-
-include $(BUILD_HOST_STATIC_LIBRARY)
+aaptCFlags := -Wall -Werror
# ==========================================================
# Build the host executable: aapt
@@ -99,33 +42,10 @@ include $(CLEAR_VARS)
LOCAL_MODULE := aapt
LOCAL_MODULE_HOST_OS := darwin linux windows
-LOCAL_CFLAGS := $(aaptCFlags)
-LOCAL_CPPFLAGS := $(aaptCppFlags)
-LOCAL_LDLIBS_darwin := $(aaptHostLdLibs_darwin)
-LOCAL_LDLIBS_linux := $(aaptHostLdLibs_linux)
-LOCAL_SRC_FILES := $(aaptMain)
+LOCAL_CFLAGS := -DAAPT_VERSION=\"$(BUILD_NUMBER_FROM_FILE)\" $(aaptCFlags)
+LOCAL_SRC_FILES := Main.cpp
LOCAL_STATIC_LIBRARIES := libaapt $(aaptHostStaticLibs)
-LOCAL_STATIC_LIBRARIES_windows := $(aaptHostStaticLibs_windows)
include $(BUILD_HOST_EXECUTABLE)
-
-# ==========================================================
-# Build the host tests: libaapt_tests
-# ==========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := libaapt_tests
-LOCAL_CFLAGS := $(aaptCFlags)
-LOCAL_CPPFLAGS := $(aaptCppFlags)
-LOCAL_LDLIBS_darwin := $(aaptHostLdLibs_darwin)
-LOCAL_LDLIBS_linux := $(aaptHostLdLibs_linux)
-LOCAL_SRC_FILES := $(aaptTests)
-LOCAL_C_INCLUDES := $(LOCAL_PATH)
-LOCAL_STATIC_LIBRARIES := libaapt $(aaptHostStaticLibs)
-LOCAL_STATIC_LIBRARIES_windows := $(aaptHostStaticLibs_windows)
-
-include $(BUILD_HOST_NATIVE_TEST)
-
-
endif # No TARGET_BUILD_APPS or TARGET_BUILD_PDK
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index ba731801e507..5e8580255197 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -29,24 +29,6 @@
using namespace android;
-#ifndef AAPT_VERSION
- #define AAPT_VERSION ""
-#endif
-
-/*
- * Show version info. All the cool kids do it.
- */
-int doVersion(Bundle* bundle)
-{
- if (bundle->getFileSpecCount() != 0) {
- printf("(ignoring extra arguments)\n");
- }
- printf("Android Asset Packaging Tool, v0.2-" AAPT_VERSION "\n");
-
- return 0;
-}
-
-
/*
* Open the file read only. The call fails if the file doesn't exist.
*
diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp
index 417b7ae087e1..d714687ee914 100644
--- a/tools/aapt/Main.cpp
+++ b/tools/aapt/Main.cpp
@@ -20,6 +20,23 @@ using namespace android;
static const char* gProgName = "aapt";
+#ifndef AAPT_VERSION
+ #define AAPT_VERSION ""
+#endif
+
+/*
+ * Show version info. All the cool kids do it.
+ */
+int doVersion(Bundle* bundle)
+{
+ if (bundle->getFileSpecCount() != 0) {
+ printf("(ignoring extra arguments)\n");
+ }
+ printf("Android Asset Packaging Tool, v0.2-" AAPT_VERSION "\n");
+
+ return 0;
+}
+
/*
* When running under Cygwin on Windows, this will convert slash-based
* paths into back-slash-based ones. Otherwise the ApptAssets file comparisons
diff --git a/tools/aapt2/Android.bp b/tools/aapt2/Android.bp
index eff82837dd8a..353c3e0652fb 100644
--- a/tools/aapt2/Android.bp
+++ b/tools/aapt2/Android.bp
@@ -24,7 +24,7 @@ toolSources = [
]
cc_defaults {
- name: "aapt_defaults",
+ name: "aapt2_defaults",
cflags: [
"-Wall",
"-Werror",
@@ -39,14 +39,9 @@ cc_defaults {
windows: {
enabled: true,
cflags: ["-Wno-maybe-uninitialized"],
- static_libs: ["libz"],
},
darwin: {
cflags: ["-D_DARWIN_UNLIMITED_STREAMS"],
- host_ldlibs: ["-lz"],
- },
- linux: {
- host_ldlibs: ["-lz"],
},
},
static_libs: [
@@ -59,6 +54,7 @@ cc_defaults {
"libpng",
"libbase",
"libprotobuf-cpp-lite",
+ "libz",
],
group_static_libs: true,
}
@@ -141,7 +137,7 @@ cc_library_host_static {
proto: {
export_proto_headers: true,
},
- defaults: ["aapt_defaults"],
+ defaults: ["aapt2_defaults"],
}
// ==========================================================
@@ -151,7 +147,7 @@ cc_library_host_shared {
name: "libaapt2_jni",
srcs: toolSources + ["jni/aapt2_jni.cpp"],
static_libs: ["libaapt2"],
- defaults: ["aapt_defaults"],
+ defaults: ["aapt2_defaults"],
}
// ==========================================================
@@ -161,7 +157,7 @@ cc_test_host {
name: "aapt2_tests",
srcs: ["test/Common.cpp", "**/*_test.cpp"],
static_libs: ["libaapt2", "libgmock"],
- defaults: ["aapt_defaults"],
+ defaults: ["aapt2_defaults"],
}
// ==========================================================
@@ -171,5 +167,5 @@ cc_binary_host {
name: "aapt2",
srcs: ["Main.cpp"] + toolSources,
static_libs: ["libaapt2"],
- defaults: ["aapt_defaults"],
+ defaults: ["aapt2_defaults"],
}
diff --git a/tools/aapt2/flatten/XmlFlattener.cpp b/tools/aapt2/flatten/XmlFlattener.cpp
index 0711749d0378..bfebedef2a1e 100644
--- a/tools/aapt2/flatten/XmlFlattener.cpp
+++ b/tools/aapt2/flatten/XmlFlattener.cpp
@@ -257,9 +257,11 @@ class XmlFlattenerVisitor : public xml::Visitor {
// Process plain strings to make sure they get properly escaped.
StringPiece raw_value = xml_attr->value;
- util::StringBuilder str_builder;
+
+ util::StringBuilder str_builder(true /*preserve_spaces*/);
+ str_builder.Append(xml_attr->value);
+
if (!options_.keep_raw_values) {
- str_builder.Append(xml_attr->value);
raw_value = str_builder.ToString();
}
diff --git a/tools/aapt2/flatten/XmlFlattener_test.cpp b/tools/aapt2/flatten/XmlFlattener_test.cpp
index f1e903f2151e..a57e3178accd 100644
--- a/tools/aapt2/flatten/XmlFlattener_test.cpp
+++ b/tools/aapt2/flatten/XmlFlattener_test.cpp
@@ -23,7 +23,13 @@
#include "util/BigBuffer.h"
#include "util/Util.h"
-using android::StringPiece16;
+using ::aapt::test::StrEq;
+using ::android::StringPiece16;
+using ::testing::Eq;
+using ::testing::Ge;
+using ::testing::IsNull;
+using ::testing::Ne;
+using ::testing::NotNull;
namespace aapt {
@@ -72,163 +78,138 @@ class XmlFlattenerTest : public ::testing::Test {
};
TEST_F(XmlFlattenerTest, FlattenXmlWithNoCompiledAttributes) {
- std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"EOF(
- <View xmlns:test="http://com.test"
- attr="hey">
- <Layout test:hello="hi" />
- <Layout>Some text\\</Layout>
- </View>)EOF");
+ std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(
+ <View xmlns:test="http://com.test" attr="hey">
+ <Layout test:hello="hi" />
+ <Layout>Some text\\</Layout>
+ </View>)");
android::ResXMLTree tree;
ASSERT_TRUE(Flatten(doc.get(), &tree));
-
- ASSERT_EQ(android::ResXMLTree::START_NAMESPACE, tree.next());
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_NAMESPACE));
size_t len;
- const char16_t* namespace_prefix = tree.getNamespacePrefix(&len);
- EXPECT_EQ(StringPiece16(u"test"), StringPiece16(namespace_prefix, len));
-
- const char16_t* namespace_uri = tree.getNamespaceUri(&len);
- ASSERT_EQ(StringPiece16(u"http://com.test"), StringPiece16(namespace_uri, len));
-
- ASSERT_EQ(android::ResXMLTree::START_TAG, tree.next());
-
- ASSERT_EQ(nullptr, tree.getElementNamespace(&len));
- const char16_t* tag_name = tree.getElementName(&len);
- EXPECT_EQ(StringPiece16(u"View"), StringPiece16(tag_name, len));
-
- ASSERT_EQ(1u, tree.getAttributeCount());
- ASSERT_EQ(nullptr, tree.getAttributeNamespace(0, &len));
- const char16_t* attr_name = tree.getAttributeName(0, &len);
- EXPECT_EQ(StringPiece16(u"attr"), StringPiece16(attr_name, len));
-
- EXPECT_EQ(0, tree.indexOfAttribute(nullptr, 0, u"attr", StringPiece16(u"attr").size()));
+ EXPECT_THAT(tree.getNamespacePrefix(&len), StrEq(u"test"));
+ EXPECT_THAT(tree.getNamespaceUri(&len), StrEq(u"http://com.test"));
- ASSERT_EQ(android::ResXMLTree::START_TAG, tree.next());
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
+ EXPECT_THAT(tree.getElementNamespace(&len), IsNull());
+ EXPECT_THAT(tree.getElementName(&len), StrEq(u"View"));
- ASSERT_EQ(nullptr, tree.getElementNamespace(&len));
- tag_name = tree.getElementName(&len);
- EXPECT_EQ(StringPiece16(u"Layout"), StringPiece16(tag_name, len));
+ ASSERT_THAT(tree.getAttributeCount(), Eq(1u));
+ EXPECT_THAT(tree.getAttributeNamespace(0, &len), IsNull());
+ EXPECT_THAT(tree.getAttributeName(0, &len), StrEq(u"attr"));
- ASSERT_EQ(1u, tree.getAttributeCount());
- const char16_t* attr_namespace = tree.getAttributeNamespace(0, &len);
- EXPECT_EQ(StringPiece16(u"http://com.test"), StringPiece16(attr_namespace, len));
+ const StringPiece16 kAttr(u"attr");
+ EXPECT_THAT(tree.indexOfAttribute(nullptr, 0, kAttr.data(), kAttr.size()), Eq(0));
- attr_name = tree.getAttributeName(0, &len);
- EXPECT_EQ(StringPiece16(u"hello"), StringPiece16(attr_name, len));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
+ EXPECT_THAT(tree.getElementNamespace(&len), IsNull());
+ EXPECT_THAT(tree.getElementName(&len), StrEq(u"Layout"));
- ASSERT_EQ(android::ResXMLTree::END_TAG, tree.next());
- ASSERT_EQ(android::ResXMLTree::START_TAG, tree.next());
+ ASSERT_THAT(tree.getAttributeCount(), Eq(1u));
+ EXPECT_THAT(tree.getAttributeNamespace(0, &len), StrEq(u"http://com.test"));
+ EXPECT_THAT(tree.getAttributeName(0, &len), StrEq(u"hello"));
- ASSERT_EQ(nullptr, tree.getElementNamespace(&len));
- tag_name = tree.getElementName(&len);
- EXPECT_EQ(StringPiece16(u"Layout"), StringPiece16(tag_name, len));
- ASSERT_EQ(0u, tree.getAttributeCount());
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
- ASSERT_EQ(android::ResXMLTree::TEXT, tree.next());
- const char16_t* text = tree.getText(&len);
- EXPECT_EQ(StringPiece16(u"Some text\\"), StringPiece16(text, len));
+ EXPECT_THAT(tree.getElementNamespace(&len), IsNull());
+ EXPECT_THAT(tree.getElementName(&len), StrEq(u"Layout"));
+ ASSERT_THAT(tree.getAttributeCount(), Eq(0u));
- ASSERT_EQ(android::ResXMLTree::END_TAG, tree.next());
- ASSERT_EQ(nullptr, tree.getElementNamespace(&len));
- tag_name = tree.getElementName(&len);
- EXPECT_EQ(StringPiece16(u"Layout"), StringPiece16(tag_name, len));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
+ EXPECT_THAT(tree.getText(&len), StrEq(u"Some text\\"));
- ASSERT_EQ(android::ResXMLTree::END_TAG, tree.next());
- ASSERT_EQ(nullptr, tree.getElementNamespace(&len));
- tag_name = tree.getElementName(&len);
- EXPECT_EQ(StringPiece16(u"View"), StringPiece16(tag_name, len));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
+ EXPECT_THAT(tree.getElementNamespace(&len), IsNull());
+ EXPECT_THAT(tree.getElementName(&len), StrEq(u"Layout"));
- ASSERT_EQ(android::ResXMLTree::END_NAMESPACE, tree.next());
- namespace_prefix = tree.getNamespacePrefix(&len);
- EXPECT_EQ(StringPiece16(u"test"), StringPiece16(namespace_prefix, len));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_TAG));
+ EXPECT_THAT(tree.getElementNamespace(&len), IsNull());
+ EXPECT_THAT(tree.getElementName(&len), StrEq(u"View"));
- namespace_uri = tree.getNamespaceUri(&len);
- ASSERT_EQ(StringPiece16(u"http://com.test"), StringPiece16(namespace_uri, len));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_NAMESPACE));
+ EXPECT_THAT(tree.getNamespacePrefix(&len), StrEq(u"test"));
+ EXPECT_THAT(tree.getNamespaceUri(&len), StrEq(u"http://com.test"));
- ASSERT_EQ(android::ResXMLTree::END_DOCUMENT, tree.next());
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::END_DOCUMENT));
}
TEST_F(XmlFlattenerTest, FlattenCompiledXmlAndStripOnlyTools) {
- std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"EOF(
- <View xmlns:tools="http://schemas.android.com/tools"
- xmlns:foo="http://schemas.android.com/foo"
- foo:bar="Foo"
- tools:ignore="MissingTranslation"/>)EOF");
+ std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(
+ <View xmlns:tools="http://schemas.android.com/tools"
+ xmlns:foo="http://schemas.android.com/foo"
+ foo:bar="Foo"
+ tools:ignore="MissingTranslation"/>)");
android::ResXMLTree tree;
ASSERT_TRUE(Flatten(doc.get(), &tree));
-
- ASSERT_EQ(tree.next(), android::ResXMLTree::START_NAMESPACE);
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_NAMESPACE));
size_t len;
- const char16_t* namespace_prefix = tree.getNamespacePrefix(&len);
- EXPECT_EQ(StringPiece16(namespace_prefix, len), u"foo");
-
- const char16_t* namespace_uri = tree.getNamespaceUri(&len);
- ASSERT_EQ(StringPiece16(namespace_uri, len),
- u"http://schemas.android.com/foo");
-
- ASSERT_EQ(tree.next(), android::ResXMLTree::START_TAG);
+ EXPECT_THAT(tree.getNamespacePrefix(&len), StrEq(u"foo"));
+ EXPECT_THAT(tree.getNamespaceUri(&len), StrEq(u"http://schemas.android.com/foo"));
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::START_TAG));
- EXPECT_EQ(tree.indexOfAttribute("http://schemas.android.com/tools", "ignore"),
- android::NAME_NOT_FOUND);
- EXPECT_GE(tree.indexOfAttribute("http://schemas.android.com/foo", "bar"), 0);
+ EXPECT_THAT(tree.indexOfAttribute("http://schemas.android.com/tools", "ignore"),
+ Eq(android::NAME_NOT_FOUND));
+ EXPECT_THAT(tree.indexOfAttribute("http://schemas.android.com/foo", "bar"), Ge(0));
}
TEST_F(XmlFlattenerTest, AssignSpecialAttributeIndices) {
- std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"EOF(
- <View xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@id/id"
- class="str"
- style="@id/id"/>)EOF");
+ std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(
+ <View xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@id/id"
+ class="str"
+ style="@id/id"/>)");
android::ResXMLTree tree;
ASSERT_TRUE(Flatten(doc.get(), &tree));
while (tree.next() != android::ResXMLTree::START_TAG) {
- ASSERT_NE(tree.getEventType(), android::ResXMLTree::BAD_DOCUMENT);
- ASSERT_NE(tree.getEventType(), android::ResXMLTree::END_DOCUMENT);
+ ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT));
+ ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT));
}
- EXPECT_EQ(tree.indexOfClass(), 0);
- EXPECT_EQ(tree.indexOfStyle(), 1);
+ EXPECT_THAT(tree.indexOfClass(), Eq(0));
+ EXPECT_THAT(tree.indexOfStyle(), Eq(1));
}
// The device ResXMLParser in libandroidfw differentiates between empty namespace and null
// namespace.
TEST_F(XmlFlattenerTest, NoNamespaceIsNotTheSameAsEmptyNamespace) {
- std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom("<View package=\"android\"/>");
+ std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(<View package="android"/>)");
android::ResXMLTree tree;
ASSERT_TRUE(Flatten(doc.get(), &tree));
while (tree.next() != android::ResXMLTree::START_TAG) {
- ASSERT_NE(tree.getEventType(), android::ResXMLTree::BAD_DOCUMENT);
- ASSERT_NE(tree.getEventType(), android::ResXMLTree::END_DOCUMENT);
+ ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT));
+ ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT));
}
const StringPiece16 kPackage = u"package";
- EXPECT_GE(tree.indexOfAttribute(nullptr, 0, kPackage.data(), kPackage.size()), 0);
+ EXPECT_THAT(tree.indexOfAttribute(nullptr, 0, kPackage.data(), kPackage.size()), Ge(0));
}
TEST_F(XmlFlattenerTest, EmptyStringValueInAttributeIsNotNull) {
- std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom("<View package=\"\"/>");
+ std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(<View package=""/>)");
android::ResXMLTree tree;
ASSERT_TRUE(Flatten(doc.get(), &tree));
while (tree.next() != android::ResXMLTree::START_TAG) {
- ASSERT_NE(tree.getEventType(), android::ResXMLTree::BAD_DOCUMENT);
- ASSERT_NE(tree.getEventType(), android::ResXMLTree::END_DOCUMENT);
+ ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT));
+ ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT));
}
const StringPiece16 kPackage = u"package";
ssize_t idx = tree.indexOfAttribute(nullptr, 0, kPackage.data(), kPackage.size());
- ASSERT_GE(idx, 0);
+ ASSERT_THAT(idx, Ge(0));
size_t len;
- EXPECT_NE(nullptr, tree.getAttributeStringValue(idx, &len));
+ EXPECT_THAT(tree.getAttributeStringValue(idx, &len), NotNull());
}
TEST_F(XmlFlattenerTest, FlattenNonStandardPackageId) {
@@ -236,11 +217,11 @@ TEST_F(XmlFlattenerTest, FlattenNonStandardPackageId) {
context_->SetPackageId(0x80);
context_->SetNameManglerPolicy({"com.app.test.feature"});
- std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"EOF(
+ std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
<View xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@id/foo"
- app:foo="@id/foo" />)EOF");
+ app:foo="@id/foo" />)");
XmlReferenceLinker linker;
ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
@@ -253,59 +234,57 @@ TEST_F(XmlFlattenerTest, FlattenNonStandardPackageId) {
ASSERT_TRUE(Flatten(doc.get(), &tree));
while (tree.next() != android::ResXMLTree::START_TAG) {
- ASSERT_NE(android::ResXMLTree::BAD_DOCUMENT, tree.getEventType());
- ASSERT_NE(android::ResXMLTree::END_DOCUMENT, tree.getEventType());
+ ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT));
+ ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT));
}
ssize_t idx;
idx = tree.indexOfAttribute(xml::kSchemaAndroid, "id");
- ASSERT_GE(idx, 0);
- EXPECT_EQ(idx, tree.indexOfID());
- EXPECT_EQ(ResourceId(0x010100d0), ResourceId(tree.getAttributeNameResID(idx)));
+ ASSERT_THAT(idx, Ge(0));
+ EXPECT_THAT(tree.indexOfID(), Eq(idx));
+ EXPECT_THAT(tree.getAttributeNameResID(idx), Eq(0x010100d0u));
idx = tree.indexOfAttribute(xml::kSchemaAuto, "foo");
- ASSERT_GE(idx, 0);
- EXPECT_EQ(ResourceId(0x80010000), ResourceId(tree.getAttributeNameResID(idx)));
- EXPECT_EQ(android::Res_value::TYPE_REFERENCE, tree.getAttributeDataType(idx));
- EXPECT_EQ(ResourceId(0x80020000), tree.getAttributeData(idx));
+ ASSERT_THAT(idx, Ge(0));
+ EXPECT_THAT(tree.getAttributeNameResID(idx), Eq(0x80010000u));
+ EXPECT_THAT(tree.getAttributeDataType(idx), Eq(android::Res_value::TYPE_REFERENCE));
+ EXPECT_THAT(tree.getAttributeData(idx), Eq(int32_t(0x80020000)));
}
TEST_F(XmlFlattenerTest, ProcessEscapedStrings) {
std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(
- R"EOF(<element value="\?hello" pattern="\\d{5}">\\d{5}</element>)EOF");
+ R"(<element value="\?hello" pattern="\\d{5}" other="&quot;">\\d{5}</element>)");
android::ResXMLTree tree;
ASSERT_TRUE(Flatten(doc.get(), &tree));
while (tree.next() != android::ResXMLTree::START_TAG) {
- ASSERT_NE(tree.getEventType(), android::ResXMLTree::BAD_DOCUMENT);
- ASSERT_NE(tree.getEventType(), android::ResXMLTree::END_DOCUMENT);
+ ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::BAD_DOCUMENT));
+ ASSERT_THAT(tree.getEventType(), Ne(android::ResXMLTree::END_DOCUMENT));
}
const StringPiece16 kValue = u"value";
const StringPiece16 kPattern = u"pattern";
+ const StringPiece16 kOther = u"other";
size_t len;
ssize_t idx;
- const char16_t* str16;
idx = tree.indexOfAttribute(nullptr, 0, kValue.data(), kValue.size());
- ASSERT_GE(idx, 0);
- str16 = tree.getAttributeStringValue(idx, &len);
- ASSERT_NE(nullptr, str16);
- EXPECT_EQ(StringPiece16(u"?hello"), StringPiece16(str16, len));
+ ASSERT_THAT(idx, Ge(0));
+ EXPECT_THAT(tree.getAttributeStringValue(idx, &len), StrEq(u"?hello"));
idx = tree.indexOfAttribute(nullptr, 0, kPattern.data(), kPattern.size());
- ASSERT_GE(idx, 0);
- str16 = tree.getAttributeStringValue(idx, &len);
- ASSERT_NE(nullptr, str16);
- EXPECT_EQ(StringPiece16(u"\\d{5}"), StringPiece16(str16, len));
-
- ASSERT_EQ(android::ResXMLTree::TEXT, tree.next());
- str16 = tree.getText(&len);
- ASSERT_NE(nullptr, str16);
- EXPECT_EQ(StringPiece16(u"\\d{5}"), StringPiece16(str16, len));
+ ASSERT_THAT(idx, Ge(0));
+ EXPECT_THAT(tree.getAttributeStringValue(idx, &len), StrEq(u"\\d{5}"));
+
+ idx = tree.indexOfAttribute(nullptr, 0, kOther.data(), kOther.size());
+ ASSERT_THAT(idx, Ge(0));
+ EXPECT_THAT(tree.getAttributeStringValue(idx, &len), StrEq(u"\""));
+
+ ASSERT_THAT(tree.next(), Eq(android::ResXMLTree::TEXT));
+ EXPECT_THAT(tree.getText(&len), StrEq(u"\\d{5}"));
}
} // namespace aapt
diff --git a/tools/aapt2/link/ManifestFixer.cpp b/tools/aapt2/link/ManifestFixer.cpp
index 53c66a628568..f0ac467bdd18 100644
--- a/tools/aapt2/link/ManifestFixer.cpp
+++ b/tools/aapt2/link/ManifestFixer.cpp
@@ -293,6 +293,7 @@ bool ManifestFixer::BuildRules(xml::XmlActionExecutor* executor,
manifest_action["instrumentation"]["meta-data"] = meta_data_action;
manifest_action["original-package"];
+ manifest_action["overlay"];
manifest_action["protected-broadcast"];
manifest_action["uses-permission"];
manifest_action["uses-permission-sdk-23"];
diff --git a/tools/aapt2/test/Common.h b/tools/aapt2/test/Common.h
index 05851485e216..01b2d143de50 100644
--- a/tools/aapt2/test/Common.h
+++ b/tools/aapt2/test/Common.h
@@ -145,6 +145,12 @@ void PrintTo(const Maybe<T>& value, std::ostream* out) {
namespace test {
+MATCHER_P(StrEq, a,
+ std::string(negation ? "isn't" : "is") + " equal to " +
+ ::testing::PrintToString(android::StringPiece16(a))) {
+ return android::StringPiece16(arg) == a;
+}
+
MATCHER_P(ValueEq, a,
std::string(negation ? "isn't" : "is") + " equal to " + ::testing::PrintToString(a)) {
return arg.Equals(&a);
diff --git a/tools/aapt2/util/Maybe.h b/tools/aapt2/util/Maybe.h
index b43f8e87fd68..9a82418e0a5a 100644
--- a/tools/aapt2/util/Maybe.h
+++ b/tools/aapt2/util/Maybe.h
@@ -281,16 +281,12 @@ inline Maybe<T> make_nothing() {
return Maybe<T>();
}
-/**
- * Define the == operator between Maybe<T> and Maybe<U> only if the operator T
- * == U is defined.
- * That way the compiler will show an error at the callsite when comparing two
- * Maybe<> objects
- * whose inner types can't be compared.
- */
+// Define the == operator between Maybe<T> and Maybe<U> only if the operator T == U is defined.
+// That way the compiler will show an error at the callsite when comparing two Maybe<> objects
+// whose inner types can't be compared.
template <typename T, typename U>
-typename std::enable_if<has_eq_op<T, U>::value, bool>::type operator==(
- const Maybe<T>& a, const Maybe<U>& b) {
+typename std::enable_if<has_eq_op<T, U>::value, bool>::type operator==(const Maybe<T>& a,
+ const Maybe<U>& b) {
if (a && b) {
return a.value() == b.value();
} else if (!a && !b) {
@@ -299,18 +295,22 @@ typename std::enable_if<has_eq_op<T, U>::value, bool>::type operator==(
return false;
}
-/**
- * Same as operator== but negated.
- */
template <typename T, typename U>
-typename std::enable_if<has_eq_op<T, U>::value, bool>::type operator!=(
- const Maybe<T>& a, const Maybe<U>& b) {
+typename std::enable_if<has_eq_op<T, U>::value, bool>::type operator==(const Maybe<T>& a,
+ const U& b) {
+ return a ? a.value() == b : false;
+}
+
+// Same as operator== but negated.
+template <typename T, typename U>
+typename std::enable_if<has_eq_op<T, U>::value, bool>::type operator!=(const Maybe<T>& a,
+ const Maybe<U>& b) {
return !(a == b);
}
template <typename T, typename U>
-typename std::enable_if<has_lt_op<T, U>::value, bool>::type operator<(
- const Maybe<T>& a, const Maybe<U>& b) {
+typename std::enable_if<has_lt_op<T, U>::value, bool>::type operator<(const Maybe<T>& a,
+ const Maybe<U>& b) {
if (a && b) {
return a.value() < b.value();
} else if (!a && !b) {
diff --git a/tools/aapt2/util/Util.cpp b/tools/aapt2/util/Util.cpp
index cf2232254ec4..aefc24b82691 100644
--- a/tools/aapt2/util/Util.cpp
+++ b/tools/aapt2/util/Util.cpp
@@ -312,6 +312,9 @@ static Maybe<std::string> ParseUnicodeCodepoint(const char** start,
return result_utf8;
}
+StringBuilder::StringBuilder(bool preserve_spaces) : preserve_spaces_(preserve_spaces) {
+}
+
StringBuilder& StringBuilder::Append(const StringPiece& str) {
if (!error_.empty()) {
return *this;
@@ -368,14 +371,12 @@ StringBuilder& StringBuilder::Append(const StringPiece& str) {
}
last_char_was_escape_ = false;
start = current + 1;
- } else if (*current == '"') {
+ } else if (!preserve_spaces_ && *current == '"') {
if (!quote_ && trailing_space_) {
- // We found an opening quote, and we have
- // trailing space, so we should append that
+ // We found an opening quote, and we have trailing space, so we should append that
// space now.
if (trailing_space_) {
- // We had trailing whitespace, so
- // replace with a single space.
+ // We had trailing whitespace, so replace with a single space.
if (!str_.empty()) {
str_ += ' ';
}
@@ -385,7 +386,7 @@ StringBuilder& StringBuilder::Append(const StringPiece& str) {
quote_ = !quote_;
str_.append(start, current - start);
start = current + 1;
- } else if (*current == '\'' && !quote_) {
+ } else if (!preserve_spaces_ && *current == '\'' && !quote_) {
// This should be escaped.
error_ = "unescaped apostrophe";
return *this;
@@ -402,7 +403,7 @@ StringBuilder& StringBuilder::Append(const StringPiece& str) {
str_.append(start, current - start);
start = current + 1;
last_char_was_escape_ = true;
- } else if (!quote_) {
+ } else if (!preserve_spaces_ && !quote_) {
// This is not quoted text, so look for whitespace.
if (isspace(*current)) {
// We found whitespace, see if we have seen some
diff --git a/tools/aapt2/util/Util.h b/tools/aapt2/util/Util.h
index 386f74b0301a..b9ada7704a26 100644
--- a/tools/aapt2/util/Util.h
+++ b/tools/aapt2/util/Util.h
@@ -166,6 +166,8 @@ bool VerifyJavaStringFormat(const android::StringPiece& str);
class StringBuilder {
public:
+ explicit StringBuilder(bool preserve_spaces = false);
+
StringBuilder& Append(const android::StringPiece& str);
const std::string& ToString() const;
const std::string& Error() const;
@@ -179,6 +181,7 @@ class StringBuilder {
explicit operator bool() const;
private:
+ bool preserve_spaces_;
std::string str_;
size_t utf16_len_ = 0;
bool quote_ = false;
diff --git a/tools/aapt2/util/Util_test.cpp b/tools/aapt2/util/Util_test.cpp
index e49aee5d50ed..5cced3e9acab 100644
--- a/tools/aapt2/util/Util_test.cpp
+++ b/tools/aapt2/util/Util_test.cpp
@@ -20,16 +20,17 @@
#include "test/Test.h"
-using android::StringPiece;
+using ::android::StringPiece;
+using ::testing::Eq;
+using ::testing::Ne;
+using ::testing::SizeIs;
namespace aapt {
TEST(UtilTest, TrimOnlyWhitespace) {
- const std::string full = "\n ";
-
- StringPiece trimmed = util::TrimWhitespace(full);
+ const StringPiece trimmed = util::TrimWhitespace("\n ");
EXPECT_TRUE(trimmed.empty());
- EXPECT_EQ(0u, trimmed.size());
+ EXPECT_THAT(trimmed, SizeIs(0u));
}
TEST(UtilTest, StringEndsWith) {
@@ -41,85 +42,74 @@ TEST(UtilTest, StringStartsWith) {
}
TEST(UtilTest, StringBuilderSplitEscapeSequence) {
- EXPECT_EQ(StringPiece("this is a new\nline."), util::StringBuilder()
- .Append("this is a new\\")
- .Append("nline.")
- .ToString());
+ EXPECT_THAT(util::StringBuilder().Append("this is a new\\").Append("nline.").ToString(),
+ Eq("this is a new\nline."));
}
TEST(UtilTest, StringBuilderWhitespaceRemoval) {
- EXPECT_EQ(StringPiece("hey guys this is so cool"),
- util::StringBuilder()
- .Append(" hey guys ")
- .Append(" this is so cool ")
- .ToString());
-
- EXPECT_EQ(StringPiece(" wow, so many \t spaces. what?"),
- util::StringBuilder()
- .Append(" \" wow, so many \t ")
- .Append("spaces. \"what? ")
- .ToString());
-
- EXPECT_EQ(StringPiece("where is the pie?"), util::StringBuilder()
- .Append(" where \t ")
- .Append(" \nis the "
- " pie?")
- .ToString());
+ EXPECT_THAT(util::StringBuilder().Append(" hey guys ").Append(" this is so cool ").ToString(),
+ Eq("hey guys this is so cool"));
+ EXPECT_THAT(
+ util::StringBuilder().Append(" \" wow, so many \t ").Append("spaces. \"what? ").ToString(),
+ Eq(" wow, so many \t spaces. what?"));
+ EXPECT_THAT(util::StringBuilder().Append(" where \t ").Append(" \nis the pie?").ToString(),
+ Eq("where is the pie?"));
}
TEST(UtilTest, StringBuilderEscaping) {
- EXPECT_EQ(StringPiece("hey guys\n this \t is so\\ cool"),
- util::StringBuilder()
- .Append(" hey guys\\n ")
- .Append(" this \\t is so\\\\ cool ")
- .ToString());
-
- EXPECT_EQ(StringPiece("@?#\\\'"),
- util::StringBuilder().Append("\\@\\?\\#\\\\\\'").ToString());
+ EXPECT_THAT(util::StringBuilder()
+ .Append(" hey guys\\n ")
+ .Append(" this \\t is so\\\\ cool ")
+ .ToString(),
+ Eq("hey guys\n this \t is so\\ cool"));
+ EXPECT_THAT(util::StringBuilder().Append("\\@\\?\\#\\\\\\'").ToString(), Eq("@?#\\\'"));
}
TEST(UtilTest, StringBuilderMisplacedQuote) {
- util::StringBuilder builder{};
+ util::StringBuilder builder;
EXPECT_FALSE(builder.Append("they're coming!"));
}
TEST(UtilTest, StringBuilderUnicodeCodes) {
- EXPECT_EQ(std::string("\u00AF\u0AF0 woah"),
- util::StringBuilder().Append("\\u00AF\\u0AF0 woah").ToString());
-
+ EXPECT_THAT(util::StringBuilder().Append("\\u00AF\\u0AF0 woah").ToString(),
+ Eq("\u00AF\u0AF0 woah"));
EXPECT_FALSE(util::StringBuilder().Append("\\u00 yo"));
}
+TEST(UtilTest, StringBuilderPreserveSpaces) {
+ EXPECT_THAT(util::StringBuilder(true /*preserve_spaces*/).Append("\"").ToString(), Eq("\""));
+}
+
TEST(UtilTest, TokenizeInput) {
auto tokenizer = util::Tokenize(StringPiece("this| is|the|end"), '|');
auto iter = tokenizer.begin();
- ASSERT_EQ(*iter, StringPiece("this"));
+ ASSERT_THAT(*iter, Eq("this"));
++iter;
- ASSERT_EQ(*iter, StringPiece(" is"));
+ ASSERT_THAT(*iter, Eq(" is"));
++iter;
- ASSERT_EQ(*iter, StringPiece("the"));
+ ASSERT_THAT(*iter, Eq("the"));
++iter;
- ASSERT_EQ(*iter, StringPiece("end"));
+ ASSERT_THAT(*iter, Eq("end"));
++iter;
- ASSERT_EQ(tokenizer.end(), iter);
+ ASSERT_THAT(iter, Eq(tokenizer.end()));
}
TEST(UtilTest, TokenizeEmptyString) {
auto tokenizer = util::Tokenize(StringPiece(""), '|');
auto iter = tokenizer.begin();
- ASSERT_NE(tokenizer.end(), iter);
- ASSERT_EQ(StringPiece(), *iter);
+ ASSERT_THAT(iter, Ne(tokenizer.end()));
+ ASSERT_THAT(*iter, Eq(StringPiece()));
++iter;
- ASSERT_EQ(tokenizer.end(), iter);
+ ASSERT_THAT(iter, Eq(tokenizer.end()));
}
TEST(UtilTest, TokenizeAtEnd) {
auto tokenizer = util::Tokenize(StringPiece("one."), '.');
auto iter = tokenizer.begin();
- ASSERT_EQ(*iter, StringPiece("one"));
+ ASSERT_THAT(*iter, Eq("one"));
++iter;
- ASSERT_NE(iter, tokenizer.end());
- ASSERT_EQ(*iter, StringPiece());
+ ASSERT_THAT(iter, Ne(tokenizer.end()));
+ ASSERT_THAT(*iter, Eq(StringPiece()));
}
TEST(UtilTest, IsJavaClassName) {
diff --git a/tools/aapt2/xml/XmlDom_test.cpp b/tools/aapt2/xml/XmlDom_test.cpp
index fb18ea316ca6..031801e1dd2f 100644
--- a/tools/aapt2/xml/XmlDom_test.cpp
+++ b/tools/aapt2/xml/XmlDom_test.cpp
@@ -28,17 +28,16 @@ constexpr const char* kXmlPreamble =
TEST(XmlDomTest, Inflate) {
std::stringstream in(kXmlPreamble);
- in << R"EOF(
- <Layout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
- <TextView android:id="@+id/id"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
- </Layout>
- )EOF";
-
- const Source source = {"test.xml"};
+ in << R"(
+ <Layout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
+ <TextView android:id="@+id/id"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+ </Layout>)";
+
+ const Source source("test.xml");
StdErrDiagnostics diag;
std::unique_ptr<xml::XmlResource> doc = xml::Inflate(&in, &diag, source);
ASSERT_NE(doc, nullptr);
@@ -51,8 +50,8 @@ TEST(XmlDomTest, Inflate) {
// Escaping is handled after parsing of the values for resource-specific values.
TEST(XmlDomTest, ForwardEscapes) {
- std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"EOF(
- <element value="\?hello" pattern="\\d{5}">\\d{5}</element>)EOF");
+ std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(
+ <element value="\?hello" pattern="\\d{5}">\\d{5}</element>)");
xml::Element* el = xml::FindRootElement(doc->root.get());
ASSERT_NE(nullptr, el);
@@ -65,10 +64,20 @@ TEST(XmlDomTest, ForwardEscapes) {
ASSERT_NE(nullptr, attr);
EXPECT_EQ("\\?hello", attr->value);
- ASSERT_EQ(1u, el->children.size());
xml::Text* text = xml::NodeCast<xml::Text>(el->children[0].get());
ASSERT_NE(nullptr, text);
EXPECT_EQ("\\\\d{5}", text->text);
}
+TEST(XmlDomTest, XmlEscapeSequencesAreParsed) {
+ std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"(<element value="&quot;" />)");
+
+ xml::Element* el = xml::FindRootElement(doc.get());
+ ASSERT_NE(nullptr, el);
+
+ xml::Attribute* attr = el->FindAttribute({}, "value");
+ ASSERT_NE(nullptr, attr);
+ EXPECT_EQ("\"", attr->value);
+}
+
} // namespace aapt
diff --git a/tools/bit/Android.bp b/tools/bit/Android.bp
new file mode 100644
index 000000000000..258e9b517f6d
--- /dev/null
+++ b/tools/bit/Android.bp
@@ -0,0 +1,40 @@
+//
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// ==========================================================
+// Build the host executable: bit
+// ==========================================================
+cc_binary_host {
+ name: "bit",
+
+ srcs: [
+ "aapt.cpp",
+ "adb.cpp",
+ "command.cpp",
+ "main.cpp",
+ "make.cpp",
+ "print.cpp",
+ "util.cpp",
+ ],
+
+ static_libs: [
+ "libexpat",
+ "libinstrumentation",
+ "libjsoncpp",
+ ],
+
+ shared_libs: ["libprotobuf-cpp-full"],
+}
diff --git a/tools/bit/Android.mk b/tools/bit/Android.mk
deleted file mode 100644
index 57f46d490f24..000000000000
--- a/tools/bit/Android.mk
+++ /dev/null
@@ -1,44 +0,0 @@
-#
-# Copyright (C) 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-LOCAL_PATH:= $(call my-dir)
-
-# ==========================================================
-# Build the host executable: protoc-gen-javastream
-# ==========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := bit
-
-LOCAL_MODULE_HOST_OS := linux darwin
-
-LOCAL_SRC_FILES := \
- aapt.cpp \
- adb.cpp \
- command.cpp \
- main.cpp \
- make.cpp \
- print.cpp \
- util.cpp
-
-LOCAL_STATIC_LIBRARIES := \
- libexpat \
- libinstrumentation \
- libjsoncpp
-
-LOCAL_SHARED_LIBRARIES := \
- libprotobuf-cpp-full
-
-include $(BUILD_HOST_EXECUTABLE)
diff --git a/tools/bit/adb.h b/tools/bit/adb.h
index dca80c853b45..f0774db933ba 100644
--- a/tools/bit/adb.h
+++ b/tools/bit/adb.h
@@ -17,7 +17,7 @@
#ifndef ADB_H
#define ADB_H
-#include "instrumentation_data.pb.h"
+#include "proto/instrumentation_data.pb.h"
#include <string>
diff --git a/tools/incident_report/Android.bp b/tools/incident_report/Android.bp
new file mode 100644
index 000000000000..6f21605a99a4
--- /dev/null
+++ b/tools/incident_report/Android.bp
@@ -0,0 +1,38 @@
+//
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// ==========================================================
+// Build the host executable: incident_report
+// ==========================================================
+cc_binary_host {
+ name: "incident_report",
+
+ srcs: [
+ "generic_message.cpp",
+ "main.cpp",
+ "printer.cpp",
+ ],
+
+ shared_libs: [
+ "libplatformprotos",
+ "libprotobuf-cpp-full",
+ ],
+
+ cflags: ["-Wno-unused-parameter"],
+
+ // b/34740546, work around clang-tidy segmentation fault.
+ tidy_checks: ["-modernize*"],
+}
diff --git a/tools/incident_report/Android.mk b/tools/incident_report/Android.mk
deleted file mode 100644
index e57a95949e5a..000000000000
--- a/tools/incident_report/Android.mk
+++ /dev/null
@@ -1,44 +0,0 @@
-#
-# Copyright (C) 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-LOCAL_PATH:= $(call my-dir)
-
-# ==========================================================
-# Build the host executable: protoc-gen-javastream
-# ==========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := incident_report
-
-LOCAL_C_INCLUDES := \
- external/protobuf/src
-
-LOCAL_SRC_FILES := \
- generic_message.cpp \
- main.cpp \
- printer.cpp
-
-LOCAL_SHARED_LIBRARIES := \
- libplatformprotos \
- libprotobuf-cpp-full
-
-# b/34740546, work around clang-tidy segmentation fault.
-LOCAL_TIDY_CHECKS := -modernize*
-
-LOCAL_C_FLAGS := \
- -Wno-unused-parameter
-include $(BUILD_HOST_EXECUTABLE)
-
-
diff --git a/tools/incident_section_gen/Android.bp b/tools/incident_section_gen/Android.bp
new file mode 100644
index 000000000000..7f8151f196ae
--- /dev/null
+++ b/tools/incident_section_gen/Android.bp
@@ -0,0 +1,33 @@
+//
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// ==========================================================
+// Build the host executable: incident-section-gen
+// ==========================================================
+cc_binary_host {
+ name: "incident-section-gen",
+ // b/34740546, work around clang-tidy segmentation fault.
+ tidy_checks: ["-modernize*"],
+ cflags: [
+ "-g",
+ "-O0",
+ ],
+ srcs: ["main.cpp"],
+ shared_libs: [
+ "libplatformprotos",
+ "libprotobuf-cpp-full",
+ ],
+}
diff --git a/tools/incident_section_gen/Android.mk b/tools/incident_section_gen/Android.mk
deleted file mode 100644
index 05490266576a..000000000000
--- a/tools/incident_section_gen/Android.mk
+++ /dev/null
@@ -1,37 +0,0 @@
-#
-# Copyright (C) 2015 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-LOCAL_PATH:= $(call my-dir)
-
-# ==========================================================
-# Build the host executable: protoc-gen-javastream
-# ==========================================================
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := incident-section-gen
-# b/34740546, work around clang-tidy segmentation fault.
-LOCAL_TIDY_CHECKS := -modernize*
-LOCAL_CFLAGS += -g -O0
-LOCAL_C_INCLUDES := \
- external/protobuf/src
-LOCAL_SRC_FILES := \
- main.cpp
-LOCAL_LDFLAGS := -ldl
-LOCAL_SHARED_LIBRARIES := \
- libplatformprotos \
- libprotobuf-cpp-full
-
-include $(BUILD_HOST_EXECUTABLE)
-
diff --git a/tools/obbtool/Android.bp b/tools/obbtool/Android.bp
new file mode 100644
index 000000000000..f87965860ce1
--- /dev/null
+++ b/tools/obbtool/Android.bp
@@ -0,0 +1,51 @@
+//
+// Copyright 2010 The Android Open Source Project
+//
+// Opaque Binary Blob (OBB) Tool
+//
+
+cc_binary_host {
+ name: "obbtool",
+
+ srcs: ["Main.cpp"],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-mismatched-tags",
+ ],
+
+ static_libs: [
+ "libandroidfw",
+ "libutils",
+ "libcutils",
+ "liblog",
+ ],
+
+ // This tool is prebuilt if we're doing an app-only build.
+ product_variables: {
+ unbundled_build: {
+ enabled: false,
+ },
+ },
+}
+
+//####################################################
+cc_binary_host {
+ name: "pbkdf2gen",
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-mismatched-tags",
+ ],
+ srcs: ["pbkdf2gen.cpp"],
+ static_libs: ["libcrypto"],
+
+ // This tool is prebuilt if we're doing an app-only build.
+ product_variables: {
+ unbundled_build: {
+ enabled: false,
+ },
+ },
+}
diff --git a/tools/obbtool/Android.mk b/tools/obbtool/Android.mk
deleted file mode 100644
index 6dc306e85bc6..000000000000
--- a/tools/obbtool/Android.mk
+++ /dev/null
@@ -1,47 +0,0 @@
-#
-# Copyright 2010 The Android Open Source Project
-#
-# Opaque Binary Blob (OBB) Tool
-#
-
-# This tool is prebuilt if we're doing an app-only build.
-ifeq ($(TARGET_BUILD_APPS),)
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
- Main.cpp
-
-LOCAL_CFLAGS := -Wall -Werror -Wno-mismatched-tags
-
-#LOCAL_C_INCLUDES +=
-
-LOCAL_STATIC_LIBRARIES := \
- libandroidfw \
- libutils \
- libcutils \
- liblog
-
-ifeq ($(HOST_OS),linux)
-LOCAL_LDLIBS += -ldl -lpthread
-endif
-
-LOCAL_MODULE := obbtool
-
-include $(BUILD_HOST_EXECUTABLE)
-
-#####################################################
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := pbkdf2gen
-LOCAL_MODULE_TAGS := optional
-LOCAL_CFLAGS := -Wall -Werror -Wno-mismatched-tags
-LOCAL_SRC_FILES := pbkdf2gen.cpp
-LOCAL_LDLIBS += -ldl
-LOCAL_STATIC_LIBRARIES := libcrypto
-
-include $(BUILD_HOST_EXECUTABLE)
-
-#######################################################
-endif # TARGET_BUILD_APPS
diff --git a/tools/split-select/Android.bp b/tools/split-select/Android.bp
new file mode 100644
index 000000000000..ee822b7d7fa7
--- /dev/null
+++ b/tools/split-select/Android.bp
@@ -0,0 +1,108 @@
+//
+// Copyright (C) 2014 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// ==========================================================
+// Setup some common variables for the different build
+// targets here.
+// ==========================================================
+
+cc_defaults {
+ name: "split-select_defaults",
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+ include_dirs: ["frameworks/base/tools"],
+ static_libs: [
+ "libaapt",
+ "libandroidfw",
+ "libpng",
+ "libutils",
+ "liblog",
+ "libcutils",
+ "libexpat",
+ "libziparchive",
+ "libbase",
+ "libz",
+ ],
+ group_static_libs: true,
+
+ target: {
+ windows: {
+ enabled: true,
+ },
+ },
+
+ // This tool is prebuilt if we're doing an app-only build.
+ product_variables: {
+ pdk: {
+ enabled: false,
+ },
+ unbundled_build: {
+ enabled: false,
+ },
+ },
+}
+
+// ==========================================================
+// Build the host static library: libsplit-select
+// ==========================================================
+cc_library_host_static {
+ name: "libsplit-select",
+ defaults: ["split-select_defaults"],
+
+ srcs: [
+ "Abi.cpp",
+ "Grouper.cpp",
+ "Rule.cpp",
+ "RuleGenerator.cpp",
+ "SplitDescription.cpp",
+ "SplitSelector.cpp",
+ ],
+ cflags: ["-D_DARWIN_UNLIMITED_STREAMS"],
+
+}
+
+// ==========================================================
+// Build the host tests: libsplit-select_tests
+// ==========================================================
+cc_test_host {
+ name: "libsplit-select_tests",
+ defaults: ["split-select_defaults"],
+
+ srcs: [
+ "Grouper_test.cpp",
+ "Rule_test.cpp",
+ "RuleGenerator_test.cpp",
+ "SplitSelector_test.cpp",
+ "TestRules.cpp",
+ ],
+
+ static_libs: ["libsplit-select"],
+
+}
+
+// ==========================================================
+// Build the host executable: split-select
+// ==========================================================
+cc_binary_host {
+ name: "split-select",
+ defaults: ["split-select_defaults"],
+ srcs: ["Main.cpp"],
+
+ static_libs: ["libsplit-select"],
+}
diff --git a/tools/split-select/Android.mk b/tools/split-select/Android.mk
deleted file mode 100644
index 4a1511eae43a..000000000000
--- a/tools/split-select/Android.mk
+++ /dev/null
@@ -1,119 +0,0 @@
-#
-# Copyright (C) 2014 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-# This tool is prebuilt if we're doing an app-only build.
-ifeq ($(TARGET_BUILD_APPS)$(filter true,$(TARGET_BUILD_PDK)),)
-
-# ==========================================================
-# Setup some common variables for the different build
-# targets here.
-# ==========================================================
-LOCAL_PATH:= $(call my-dir)
-
-main := Main.cpp
-sources := \
- Abi.cpp \
- Grouper.cpp \
- Rule.cpp \
- RuleGenerator.cpp \
- SplitDescription.cpp \
- SplitSelector.cpp
-
-testSources := \
- Grouper_test.cpp \
- Rule_test.cpp \
- RuleGenerator_test.cpp \
- SplitSelector_test.cpp \
- TestRules.cpp
-
-cIncludes := \
- external/zlib \
- frameworks/base/tools
-
-hostStaticLibs := \
- libaapt \
- libandroidfw \
- libpng \
- libutils \
- liblog \
- libcutils \
- libexpat \
- libziparchive \
- libbase
-
-cFlags := -Wall -Werror
-
-hostLdLibs_linux := -lrt -ldl -lpthread
-
-# Statically link libz for MinGW (Win SDK under Linux),
-# and dynamically link for all others.
-hostStaticLibs_windows := libz
-hostLdLibs_darwin := -lz
-hostLdLibs_linux += -lz
-
-
-# ==========================================================
-# Build the host static library: libsplit-select
-# ==========================================================
-include $(CLEAR_VARS)
-LOCAL_MODULE := libsplit-select
-LOCAL_MODULE_HOST_OS := darwin linux windows
-
-LOCAL_SRC_FILES := $(sources)
-LOCAL_STATIC_LIBRARIES := $(hostStaticLibs)
-LOCAL_C_INCLUDES := $(cIncludes)
-LOCAL_CFLAGS := $(cFlags) -D_DARWIN_UNLIMITED_STREAMS
-
-include $(BUILD_HOST_STATIC_LIBRARY)
-
-
-# ==========================================================
-# Build the host tests: libsplit-select_tests
-# ==========================================================
-include $(CLEAR_VARS)
-LOCAL_MODULE := libsplit-select_tests
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(testSources)
-
-LOCAL_C_INCLUDES := $(cIncludes)
-LOCAL_STATIC_LIBRARIES := libsplit-select $(hostStaticLibs)
-LOCAL_STATIC_LIBRARIES_windows := $(hostStaticLibs_windows)
-LOCAL_LDLIBS_darwin := $(hostLdLibs_darwin)
-LOCAL_LDLIBS_linux := $(hostLdLibs_linux)
-LOCAL_CFLAGS := $(cFlags)
-
-include $(BUILD_HOST_NATIVE_TEST)
-
-# ==========================================================
-# Build the host executable: split-select
-# ==========================================================
-include $(CLEAR_VARS)
-LOCAL_MODULE := split-select
-LOCAL_MODULE_HOST_OS := darwin linux windows
-
-LOCAL_SRC_FILES := $(main)
-
-LOCAL_C_INCLUDES := $(cIncludes)
-LOCAL_STATIC_LIBRARIES := libsplit-select $(hostStaticLibs)
-LOCAL_STATIC_LIBRARIES_windows := $(hostStaticLibs_windows)
-LOCAL_LDLIBS_darwin := $(hostLdLibs_darwin)
-LOCAL_LDLIBS_linux := $(hostLdLibs_linux)
-LOCAL_CFLAGS := $(cFlags)
-
-include $(BUILD_HOST_EXECUTABLE)
-
-endif # No TARGET_BUILD_APPS or TARGET_BUILD_PDK
diff --git a/tools/streaming_proto/Android.bp b/tools/streaming_proto/Android.bp
new file mode 100644
index 000000000000..24068e9ffe92
--- /dev/null
+++ b/tools/streaming_proto/Android.bp
@@ -0,0 +1,29 @@
+//
+// Copyright (C) 2015 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+// ==========================================================
+// Build the host executable: protoc-gen-javastream
+// ==========================================================
+cc_binary_host {
+ name: "protoc-gen-javastream",
+ srcs: [
+ "Errors.cpp",
+ "string_utils.cpp",
+ "main.cpp",
+ ],
+
+ shared_libs: ["libprotoc"],
+}
diff --git a/tools/streaming_proto/Android.mk b/tools/streaming_proto/Android.mk
index 5a54fd10415d..ebb77a197883 100644
--- a/tools/streaming_proto/Android.mk
+++ b/tools/streaming_proto/Android.mk
@@ -16,19 +16,6 @@
LOCAL_PATH:= $(call my-dir)
# ==========================================================
-# Build the host executable: protoc-gen-javastream
-# ==========================================================
-include $(CLEAR_VARS)
-LOCAL_MODULE := protoc-gen-javastream
-LOCAL_SRC_FILES := \
- Errors.cpp \
- string_utils.cpp \
- main.cpp
-LOCAL_SHARED_LIBRARIES := \
- libprotoc
-include $(BUILD_HOST_EXECUTABLE)
-
-# ==========================================================
# Build the java test
# ==========================================================
include $(CLEAR_VARS)
diff --git a/tools/validatekeymaps/Android.bp b/tools/validatekeymaps/Android.bp
new file mode 100644
index 000000000000..6fb278c83f0a
--- /dev/null
+++ b/tools/validatekeymaps/Android.bp
@@ -0,0 +1,30 @@
+//
+// Copyright 2010 The Android Open Source Project
+//
+// Keymap validation tool.
+//
+
+cc_binary_host {
+ name: "validatekeymaps",
+
+ srcs: ["Main.cpp"],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+
+ static_libs: [
+ "libinput",
+ "libutils",
+ "libcutils",
+ "liblog",
+ ],
+
+ // This tool is prebuilt if we're doing an app-only build.
+ product_variables: {
+ unbundled_build: {
+ enabled: false,
+ },
+ },
+}
diff --git a/tools/validatekeymaps/Android.mk b/tools/validatekeymaps/Android.mk
deleted file mode 100644
index 9af721de97db..000000000000
--- a/tools/validatekeymaps/Android.mk
+++ /dev/null
@@ -1,33 +0,0 @@
-#
-# Copyright 2010 The Android Open Source Project
-#
-# Keymap validation tool.
-#
-
-# This tool is prebuilt if we're doing an app-only build.
-ifeq ($(TARGET_BUILD_APPS),)
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
- Main.cpp
-
-LOCAL_CFLAGS := -Wall -Werror
-
-LOCAL_STATIC_LIBRARIES := \
- libinput \
- libutils \
- libcutils \
- liblog
-
-ifeq ($(HOST_OS),linux)
-LOCAL_LDLIBS += -ldl -lpthread
-endif
-
-LOCAL_MODULE := validatekeymaps
-LOCAL_MODULE_TAGS := optional
-
-include $(BUILD_HOST_EXECUTABLE)
-
-endif # TARGET_BUILD_APPS