summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.bp78
-rw-r--r--Android.mk35
-rw-r--r--api/TEST_MAPPING7
-rwxr-xr-xapi/current.txt551
-rw-r--r--api/removed.txt2
-rw-r--r--api/system-current.txt147
-rw-r--r--api/test-current.txt7
-rw-r--r--cmds/am/src/com/android/commands/am/Am.java2
-rw-r--r--cmds/bootanimation/bootanim.rc1
-rw-r--r--cmds/statsd/src/atoms.proto96
-rw-r--r--config/hiddenapi-force-blacklist.txt2
-rw-r--r--config/hiddenapi-greylist-max-o.txt1
-rw-r--r--config/hiddenapi-greylist.txt4
-rw-r--r--core/java/android/accessibilityservice/OWNERS3
-rw-r--r--core/java/android/app/LoadedApk.java10
-rw-r--r--core/java/android/app/SystemServiceRegistry.java30
-rw-r--r--core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl34
-rw-r--r--core/java/android/app/timezonedetector/TimeZoneDetector.java57
-rw-r--r--core/java/android/bluetooth/BluetoothDevice.java3
-rw-r--r--core/java/android/content/Context.java22
-rw-r--r--core/java/android/content/DynamicAndroidClient.java370
-rw-r--r--core/java/android/ddm/DdmHandleHello.java14
-rw-r--r--core/java/android/net/ConnectivityManager.java169
-rw-r--r--core/java/android/net/IConnectivityManager.aidl8
-rw-r--r--core/java/android/net/INetworkMonitor.aidl1
-rw-r--r--core/java/android/net/INetworkMonitorCallbacks.aidl3
-rw-r--r--core/java/android/net/InterfaceConfiguration.java5
-rw-r--r--core/java/android/net/KeepalivePacketData.java59
-rw-r--r--core/java/android/net/NattKeepalivePacketData.java80
-rw-r--r--core/java/android/net/NetworkAgent.java85
-rw-r--r--core/java/android/net/NetworkCapabilities.java14
-rw-r--r--core/java/android/net/NetworkPolicyManager.java10
-rw-r--r--core/java/android/net/NetworkStack.java242
-rw-r--r--core/java/android/net/NetworkUtils.java22
-rw-r--r--core/java/android/net/SocketKeepalive.java45
-rw-r--r--core/java/android/net/TcpKeepalivePacketData.java234
-rw-r--r--core/java/android/net/TcpKeepalivePacketDataParcelable.aidl (renamed from telephony/java/android/telephony/ims/RcsPart.java)15
-rw-r--r--core/java/android/net/TcpRepairWindow.java45
-rw-r--r--core/java/android/net/TcpSocketKeepalive.java78
-rw-r--r--core/java/android/net/VpnService.java21
-rw-r--r--core/java/android/net/ip/IIpClient.aidl3
-rw-r--r--core/java/android/os/AsyncTask.java107
-rw-r--r--core/java/android/os/BugreportManager.java18
-rw-r--r--core/java/android/os/DynamicAndroidManager.java188
-rw-r--r--core/java/android/os/IDynamicAndroidService.aidl87
-rw-r--r--core/java/android/os/ParcelFileDescriptor.aidl20
-rw-r--r--core/java/android/os/RecoverySystem.java4
-rw-r--r--core/java/android/os/UserHandle.java1
-rw-r--r--core/java/android/os/connectivity/CellularBatteryStats.java13
-rw-r--r--core/java/android/provider/CallLog.java95
-rw-r--r--core/java/android/provider/Settings.java13
-rw-r--r--core/java/android/service/euicc/EuiccService.java7
-rw-r--r--core/java/android/text/TextUtils.java5
-rw-r--r--core/java/android/view/View.java12
-rw-r--r--core/java/android/view/ViewRootImpl.java3
-rw-r--r--core/java/android/view/accessibility/OWNERS3
-rw-r--r--core/java/android/webkit/OWNERS1
-rw-r--r--core/java/com/android/internal/net/VpnConfig.java3
-rw-r--r--core/java/com/android/internal/os/Zygote.java5
-rw-r--r--core/java/com/android/internal/os/ZygoteConnection.java9
-rw-r--r--core/java/com/android/internal/os/ZygoteInit.java10
-rw-r--r--core/jni/Android.bp7
-rw-r--r--core/jni/OWNERS4
-rw-r--r--core/jni/android_net_NetUtils.cpp67
-rw-r--r--core/jni/android_util_Process.cpp22
-rw-r--r--core/jni/com_android_internal_os_Zygote.cpp46
-rw-r--r--core/proto/android/bluetooth/enums.proto28
-rw-r--r--core/proto/android/server/connectivity/Android.bp25
-rw-r--r--core/res/AndroidManifest.xml7
-rw-r--r--core/tests/coretests/src/android/provider/SettingsBackupTest.java4
-rw-r--r--data/etc/Android.bp8
-rw-r--r--data/etc/com.android.dialer.xml28
-rw-r--r--data/etc/privapp-permissions-platform.xml24
-rw-r--r--keystore/java/android/security/OWNERS1
-rw-r--r--keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java8
-rw-r--r--keystore/tests/OWNERS5
-rw-r--r--libs/hwui/FrameMetricsObserver.h2
-rw-r--r--media/OWNERS3
-rw-r--r--media/java/android/media/MediaHTTPConnection.java32
-rw-r--r--media/jni/Android.bp3
-rw-r--r--packages/CaptivePortalLogin/Android.bp27
-rw-r--r--packages/CaptivePortalLogin/Android.mk13
-rw-r--r--packages/CaptivePortalLogin/AndroidManifest.xml5
-rw-r--r--packages/CaptivePortalLogin/res/layout/ssl_error_msg.xml28
-rw-r--r--packages/CaptivePortalLogin/res/layout/ssl_warning.xml11
-rw-r--r--packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java72
-rw-r--r--packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java1
-rw-r--r--packages/NetworkStack/Android.bp4
-rw-r--r--packages/NetworkStack/AndroidManifest.xml11
-rw-r--r--packages/NetworkStack/OWNERS5
-rw-r--r--packages/NetworkStack/jarjar-rules-shared.txt19
-rw-r--r--packages/NetworkStack/src/android/net/apf/ApfFilter.java213
-rw-r--r--packages/NetworkStack/src/android/net/apf/ApfGenerator.java4
-rw-r--r--packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java2
-rw-r--r--packages/NetworkStack/src/android/net/ip/IpClient.java57
-rw-r--r--packages/NetworkStack/src/android/net/util/FdEventsReader.java (renamed from core/java/android/net/shared/FdEventsReader.java)3
-rw-r--r--packages/NetworkStack/src/android/net/util/PacketReader.java1
-rw-r--r--packages/NetworkStack/src/com/android/server/NetworkStackService.java9
-rw-r--r--packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java114
-rw-r--r--packages/NetworkStack/src/com/android/server/util/PermissionUtil.java3
-rw-r--r--packages/NetworkStack/tests/Android.bp2
-rw-r--r--packages/NetworkStack/tests/src/android/net/apf/ApfTest.java226
-rw-r--r--packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java2
-rw-r--r--packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java31
-rw-r--r--packages/NetworkStackPermissionStub/Android.bp27
-rw-r--r--packages/NetworkStackPermissionStub/AndroidManifest.xml37
-rw-r--r--packages/NetworkStackPermissionStub/src/com/android/server/NetworkStackPermissionStub.java (renamed from telephony/java/android/telephony/ims/RcsParticipantEvent.java)11
-rw-r--r--packages/SettingsLib/Android.bp16
-rw-r--r--packages/SettingsLib/common.mk5
-rw-r--r--packages/SettingsLib/res/values/strings.xml6
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java5
-rw-r--r--packages/Shell/AndroidManifest.xml1
-rw-r--r--packages/SystemUI/Android.bp1
-rw-r--r--[-rwxr-xr-x]packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java5
-rw-r--r--proto/Android.bp21
-rw-r--r--sax/tests/saxtests/Android.bp11
-rw-r--r--sax/tests/saxtests/Android.mk16
-rw-r--r--services/accessibility/OWNERS3
-rw-r--r--services/core/Android.bp4
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java229
-rw-r--r--services/core/java/com/android/server/DynamicAndroidService.java134
-rw-r--r--services/core/java/com/android/server/NetworkManagementService.java38
-rw-r--r--services/core/java/com/android/server/TelephonyRegistry.java113
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java19
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java15
-rw-r--r--services/core/java/com/android/server/connectivity/KeepaliveTracker.java243
-rw-r--r--services/core/java/com/android/server/connectivity/TcpKeepaliveController.java316
-rw-r--r--services/core/java/com/android/server/connectivity/Tethering.java30
-rw-r--r--services/core/java/com/android/server/connectivity/Vpn.java95
-rw-r--r--services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java93
-rw-r--r--services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java63
-rw-r--r--services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java18
-rw-r--r--services/core/java/com/android/server/net/LockdownVpnTracker.java8
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyLogger.java10
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java10
-rw-r--r--services/core/java/com/android/server/om/OverlayManagerService.java7
-rw-r--r--services/core/java/com/android/server/os/BugreportManagerServiceImpl.java139
-rw-r--r--services/core/java/com/android/server/pm/OWNERS5
-rw-r--r--services/core/java/com/android/server/pm/dex/ArtManagerService.java4
-rw-r--r--services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java67
-rwxr-xr-x[-rw-r--r--]services/core/java/com/android/server/tv/TvInputHardwareManager.java5
-rw-r--r--services/core/java/com/android/server/tv/TvInputManagerService.java25
-rw-r--r--services/core/jni/Android.bp1
-rw-r--r--services/core/jni/com_android_server_am_BatteryStatsService.cpp29
-rw-r--r--services/core/jni/com_android_server_power_PowerManagerService.cpp41
-rw-r--r--services/java/com/android/server/SystemServer.java21
-rw-r--r--services/net/Android.bp4
-rw-r--r--services/net/java/android/net/NetworkStackClient.java289
-rw-r--r--services/net/java/android/net/dhcp/DhcpServerCallbacks.java (renamed from core/java/android/net/dhcp/DhcpServerCallbacks.java)0
-rw-r--r--services/net/java/android/net/ip/IpClientCallbacks.java (renamed from core/java/android/net/ip/IpClientCallbacks.java)0
-rw-r--r--services/net/java/android/net/ip/IpClientUtil.java24
-rw-r--r--services/net/java/android/net/ip/IpServer.java9
-rw-r--r--services/tests/servicestests/src/com/android/server/DynamicAndroidServiceTest.java47
-rw-r--r--services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java10
-rw-r--r--services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java45
-rw-r--r--services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java80
-rw-r--r--services/usage/java/com/android/server/usage/AppStandbyController.java22
-rw-r--r--services/usb/java/com/android/server/usb/UsbDeviceManager.java3
-rw-r--r--startop/view_compiler/OWNERS2
-rw-r--r--startop/view_compiler/TEST_MAPPING8
-rw-r--r--startop/view_compiler/dex_builder.cc158
-rw-r--r--startop/view_compiler/dex_builder.h59
-rw-r--r--startop/view_compiler/dex_builder_test.cc38
-rw-r--r--startop/view_compiler/dex_builder_test/Android.bp14
-rw-r--r--startop/view_compiler/dex_builder_test/AndroidTest.xml1
-rw-r--r--startop/view_compiler/dex_builder_test/res/layout/layout2.xml45
-rw-r--r--startop/view_compiler/dex_builder_test/src/android/startop/test/LayoutCompilerTest.java11
-rw-r--r--telecomm/java/android/telecom/Call.java46
-rw-r--r--telecomm/java/android/telecom/CallIdentification.java22
-rw-r--r--telecomm/java/android/telecom/CallScreeningService.java188
-rw-r--r--telecomm/java/android/telecom/ConferenceParticipant.java93
-rw-r--r--telecomm/java/android/telecom/Connection.java8
-rw-r--r--telecomm/java/android/telecom/DisconnectCause.java8
-rw-r--r--telecomm/java/android/telecom/InCallService.java107
-rw-r--r--telecomm/java/android/telecom/ParcelableCall.java18
-rw-r--r--telecomm/java/android/telecom/TelecomManager.java53
-rw-r--r--telecomm/java/com/android/internal/telecom/ITelecomService.aidl3
-rw-r--r--telephony/java/android/provider/Telephony.java642
-rw-r--r--telephony/java/android/telephony/AccessNetworkConstants.java8
-rw-r--r--telephony/java/android/telephony/AvailableNetworkInfo.java2
-rw-r--r--telephony/java/android/telephony/CallAttributes.java15
-rw-r--r--telephony/java/android/telephony/CarrierConfigManager.java9
-rw-r--r--telephony/java/android/telephony/CarrierRestrictionRules.java122
-rw-r--r--telephony/java/android/telephony/CellIdentityNr.java21
-rw-r--r--telephony/java/android/telephony/CellIdentityTdscdma.java8
-rw-r--r--telephony/java/android/telephony/CellSignalStrengthGsm.java4
-rw-r--r--telephony/java/android/telephony/CellSignalStrengthNr.java8
-rw-r--r--telephony/java/android/telephony/CellSignalStrengthTdscdma.java8
-rw-r--r--telephony/java/android/telephony/CellSignalStrengthWcdma.java12
-rw-r--r--telephony/java/android/telephony/DebugEventReporter.java174
-rw-r--r--telephony/java/android/telephony/LocationAccessPolicy.java294
-rw-r--r--telephony/java/android/telephony/NetworkRegistrationState.java59
-rw-r--r--telephony/java/android/telephony/PhoneCapability.java10
-rw-r--r--telephony/java/android/telephony/PhoneStateListener.java3
-rw-r--r--telephony/java/android/telephony/PreciseCallState.java10
-rw-r--r--telephony/java/android/telephony/RadioAccessFamily.java306
-rw-r--r--telephony/java/android/telephony/ServiceState.java44
-rw-r--r--telephony/java/android/telephony/SignalStrength.java42
-rw-r--r--telephony/java/android/telephony/SmsMessage.java36
-rw-r--r--telephony/java/android/telephony/SubscriptionManager.java100
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java537
-rw-r--r--telephony/java/android/telephony/TelephonyScanManager.java13
-rw-r--r--telephony/java/android/telephony/euicc/EuiccManager.java83
-rw-r--r--telephony/java/android/telephony/ims/ImsCallProfile.java31
-rw-r--r--telephony/java/android/telephony/ims/ImsException.java113
-rw-r--r--telephony/java/android/telephony/ims/ImsMmTelManager.java43
-rw-r--r--telephony/java/android/telephony/ims/ImsReasonInfo.java183
-rw-r--r--telephony/java/android/telephony/ims/ProvisioningManager.java27
-rw-r--r--telephony/java/android/telephony/ims/Rcs1To1Thread.java74
-rw-r--r--telephony/java/android/telephony/ims/RcsControllerCall.java64
-rw-r--r--telephony/java/android/telephony/ims/RcsEvent.aidl (renamed from telephony/java/android/telephony/ims/RcsPart.aidl)2
-rw-r--r--telephony/java/android/telephony/ims/RcsEvent.java61
-rw-r--r--telephony/java/android/telephony/ims/RcsEventQueryParams.aidl (renamed from telephony/java/android/telephony/ims/RcsIncomingMessage.aidl)2
-rw-r--r--telephony/java/android/telephony/ims/RcsEventQueryParams.java319
-rw-r--r--telephony/java/android/telephony/ims/RcsEventQueryResult.aidl (renamed from telephony/java/android/telephony/ims/Rcs1To1Thread.aidl)2
-rw-r--r--telephony/java/android/telephony/ims/RcsEventQueryResult.java89
-rw-r--r--telephony/java/android/telephony/ims/RcsFileTransferCreationParams.aidl20
-rw-r--r--telephony/java/android/telephony/ims/RcsFileTransferCreationParams.java358
-rw-r--r--telephony/java/android/telephony/ims/RcsFileTransferPart.java350
-rw-r--r--telephony/java/android/telephony/ims/RcsGroupThread.java191
-rw-r--r--telephony/java/android/telephony/ims/RcsGroupThreadEvent.aidl (renamed from telephony/java/android/telephony/ims/RcsLocationPart.aidl)3
-rw-r--r--telephony/java/android/telephony/ims/RcsGroupThreadEvent.java68
-rw-r--r--telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.aidl19
-rw-r--r--telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.java109
-rw-r--r--telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.aidl20
-rw-r--r--telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.java107
-rw-r--r--telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.aidl20
-rw-r--r--telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.java107
-rw-r--r--telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.aidl (renamed from telephony/java/android/telephony/ims/RcsMessage.aidl)2
-rw-r--r--telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.java106
-rw-r--r--telephony/java/android/telephony/ims/RcsIncomingMessage.java84
-rw-r--r--telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.aidl20
-rw-r--r--telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.java178
-rw-r--r--telephony/java/android/telephony/ims/RcsLocationPart.java48
-rw-r--r--telephony/java/android/telephony/ims/RcsManager.java10
-rw-r--r--telephony/java/android/telephony/ims/RcsMessage.java310
-rw-r--r--telephony/java/android/telephony/ims/RcsMessageCreationParams.java240
-rw-r--r--telephony/java/android/telephony/ims/RcsMessageQueryParams.aidl (renamed from telephony/java/android/telephony/ims/RcsGroupThread.aidl)2
-rw-r--r--telephony/java/android/telephony/ims/RcsMessageQueryParams.java359
-rw-r--r--telephony/java/android/telephony/ims/RcsMessageQueryResult.aidl20
-rw-r--r--telephony/java/android/telephony/ims/RcsMessageQueryResult.java113
-rw-r--r--telephony/java/android/telephony/ims/RcsMessageSnippet.aidl (renamed from telephony/java/android/telephony/ims/RcsManager.aidl)2
-rw-r--r--telephony/java/android/telephony/ims/RcsMessageSnippet.java96
-rw-r--r--telephony/java/android/telephony/ims/RcsMessageStore.java231
-rw-r--r--telephony/java/android/telephony/ims/RcsMessageStoreException.java (renamed from telephony/java/android/telephony/ims/RcsThreadEvent.java)20
-rw-r--r--telephony/java/android/telephony/ims/RcsMultiMediaPart.java50
-rw-r--r--telephony/java/android/telephony/ims/RcsMultimediaPart.aidl20
-rw-r--r--telephony/java/android/telephony/ims/RcsOutgoingMessage.aidl20
-rw-r--r--telephony/java/android/telephony/ims/RcsOutgoingMessage.java53
-rw-r--r--telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.aidl20
-rw-r--r--telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.java87
-rw-r--r--telephony/java/android/telephony/ims/RcsOutgoingMessageDelivery.java129
-rw-r--r--telephony/java/android/telephony/ims/RcsParticipant.aidl20
-rw-r--r--telephony/java/android/telephony/ims/RcsParticipant.java145
-rw-r--r--telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java94
-rw-r--r--telephony/java/android/telephony/ims/RcsParticipantEvent.aidl20
-rw-r--r--telephony/java/android/telephony/ims/RcsParticipantQueryParams.aidl20
-rw-r--r--telephony/java/android/telephony/ims/RcsParticipantQueryParams.java308
-rw-r--r--telephony/java/android/telephony/ims/RcsParticipantQueryResult.aidl20
-rw-r--r--telephony/java/android/telephony/ims/RcsParticipantQueryResult.java103
-rw-r--r--telephony/java/android/telephony/ims/RcsQueryContinuationToken.aidl20
-rw-r--r--telephony/java/android/telephony/ims/RcsQueryContinuationToken.java155
-rw-r--r--telephony/java/android/telephony/ims/RcsTextPart.aidl20
-rw-r--r--telephony/java/android/telephony/ims/RcsTextPart.java48
-rw-r--r--telephony/java/android/telephony/ims/RcsThread.aidl20
-rw-r--r--telephony/java/android/telephony/ims/RcsThread.java132
-rw-r--r--telephony/java/android/telephony/ims/RcsThreadEvent.aidl20
-rw-r--r--telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl20
-rw-r--r--telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.java49
-rw-r--r--telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.aidl20
-rw-r--r--telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.java49
-rw-r--r--telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.aidl20
-rw-r--r--telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.java49
-rw-r--r--telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.aidl20
-rw-r--r--telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.java49
-rw-r--r--telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.aidl20
-rw-r--r--telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.java52
-rw-r--r--telephony/java/android/telephony/ims/RcsThreadQueryParameters.aidl20
-rw-r--r--telephony/java/android/telephony/ims/RcsThreadQueryParameters.java225
-rw-r--r--telephony/java/android/telephony/ims/RcsThreadQueryParams.aidl (renamed from telephony/java/android/telephony/ims/RcsFileTransferPart.aidl)2
-rw-r--r--telephony/java/android/telephony/ims/RcsThreadQueryParams.java303
-rw-r--r--telephony/java/android/telephony/ims/RcsThreadQueryResult.aidl30
-rw-r--r--telephony/java/android/telephony/ims/RcsThreadQueryResult.java57
-rw-r--r--telephony/java/android/telephony/ims/aidl/IRcs.aidl241
-rw-r--r--telephony/java/android/telephony/mbms/GroupCallCallback.java9
-rw-r--r--telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java10
-rw-r--r--telephony/java/com/android/ims/ImsException.java2
-rw-r--r--telephony/java/com/android/ims/RcsTypeIdPair.java79
-rw-r--r--telephony/java/com/android/internal/telephony/EncodeException.java15
-rw-r--r--telephony/java/com/android/internal/telephony/GsmAlphabet.java5
-rwxr-xr-xtelephony/java/com/android/internal/telephony/ISub.aidl5
-rw-r--r--telephony/java/com/android/internal/telephony/ITelephony.aidl27
-rw-r--r--telephony/java/com/android/internal/telephony/RILConstants.java129
-rw-r--r--telephony/java/com/android/internal/telephony/SmsMessageBase.java16
-rw-r--r--telephony/java/com/android/internal/telephony/TelephonyProperties.java7
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/BearerData.java50
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/SmsMessage.java53
-rw-r--r--telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl2
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/SmsMessage.java34
-rw-r--r--test-base/api/TEST_MAPPING7
-rw-r--r--test-mock/api/TEST_MAPPING10
-rw-r--r--test-runner/Android.mk20
-rw-r--r--test-runner/api/TEST_MAPPING10
-rw-r--r--test-runner/tests/Android.bp40
-rw-r--r--test-runner/tests/Android.mk39
-rw-r--r--tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadIconChangedEventTest.java55
-rw-r--r--tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadNameChangedEventTest.java55
-rw-r--r--tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantJoinedEventTest.java54
-rw-r--r--tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantLeftEventTest.java53
-rw-r--r--tests/RcsTests/src/com/android/tests/ims/RcsMessageStoreTest.java32
-rw-r--r--tests/RcsTests/src/com/android/tests/ims/RcsParticipantAliasChangedEventTest.java57
-rw-r--r--tests/RcsTests/src/com/android/tests/ims/RcsParticipantQueryParamsTest.java57
-rw-r--r--tests/RcsTests/src/com/android/tests/ims/RcsParticipantTest.java46
-rw-r--r--tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParametersTest.java53
-rw-r--r--tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParamsTest.java58
-rw-r--r--tests/net/java/android/net/LinkPropertiesTest.java23
-rw-r--r--tests/net/java/android/net/TcpKeepalivePacketDataTest.java126
-rw-r--r--tests/net/java/com/android/internal/util/TestUtils.java17
-rw-r--r--tests/net/java/com/android/server/ConnectivityServiceTest.java162
-rw-r--r--tests/net/java/com/android/server/connectivity/LingerMonitorTest.java5
-rw-r--r--tests/net/java/com/android/server/connectivity/TetheringTest.java12
-rw-r--r--tests/net/java/com/android/server/connectivity/VpnTest.java61
-rw-r--r--tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java227
-rw-r--r--tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java79
-rw-r--r--tools/preload/Android.bp17
-rw-r--r--tools/preload/Android.mk23
-rw-r--r--tools/preload/loadclass/Android.bp4
-rw-r--r--tools/preload/loadclass/Android.mk9
329 files changed, 15512 insertions, 3657 deletions
diff --git a/Android.bp b/Android.bp
index 0407c41a16a7..d58b48399a8f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -104,7 +104,6 @@ java_defaults {
"core/java/android/app/timedetector/ITimeDetectorService.aidl",
"core/java/android/app/timezone/ICallback.aidl",
"core/java/android/app/timezone/IRulesManager.aidl",
- "core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl",
"core/java/android/app/usage/ICacheQuotaService.aidl",
"core/java/android/app/usage/IStorageStatsManager.aidl",
"core/java/android/app/usage/IUsageStatsManager.aidl",
@@ -187,6 +186,7 @@ java_defaults {
"core/java/android/hardware/radio/ITunerCallback.aidl",
"core/java/android/hardware/soundtrigger/IRecognitionStatusCallback.aidl",
"core/java/android/hardware/usb/IUsbManager.aidl",
+ "core/java/android/net/ICaptivePortal.aidl",
"core/java/android/net/IConnectivityManager.aidl",
"core/java/android/net/IIpConnectivityMetrics.aidl",
"core/java/android/net/IEthernetManager.aidl",
@@ -221,6 +221,7 @@ java_defaults {
"core/java/android/os/ICancellationSignal.aidl",
"core/java/android/os/IDeviceIdentifiersPolicyService.aidl",
"core/java/android/os/IDeviceIdleController.aidl",
+ "core/java/android/os/IDynamicAndroidService.aidl",
"core/java/android/os/IHardwarePropertiesManager.aidl",
":libincident_aidl",
"core/java/android/os/IMaintenanceActivityListener.aidl",
@@ -601,6 +602,7 @@ java_defaults {
":storaged_aidl",
":vold_aidl",
+ ":gsiservice_aidl",
":installd_aidl",
":dumpstate_aidl",
@@ -657,6 +659,7 @@ java_defaults {
"frameworks/native/aidl/gui",
"system/core/storaged/binder",
"system/vold/binder",
+ "system/gsid/aidl",
"system/bt/binder",
"system/security/keystore/binder",
],
@@ -678,7 +681,6 @@ java_defaults {
static_libs: [
"apex_aidl_interface-java",
- "networkstack-aidl-interfaces-java",
"framework-protos",
"android.hidl.base-V1.0-java",
"android.hardware.cas-V1.0-java",
@@ -702,8 +704,8 @@ java_defaults {
"android.hardware.vibrator-V1.1-java",
"android.hardware.vibrator-V1.2-java",
"android.hardware.wifi-V1.0-java-constants",
- "networkstack-aidl-interfaces-java",
- "netd_aidl_interface-java",
+ "networkstack-aidl-framework-java",
+ "netd_aidl_parcelables-java",
],
required: [
@@ -814,10 +816,8 @@ aidl_interface {
srcs: [
"core/java/android/net/ApfCapabilitiesParcelable.aidl",
"core/java/android/net/DhcpResultsParcelable.aidl",
- "core/java/android/net/ICaptivePortal.aidl",
"core/java/android/net/INetworkMonitor.aidl",
"core/java/android/net/INetworkMonitorCallbacks.aidl",
- "core/java/android/net/IIpMemoryStore.aidl",
"core/java/android/net/INetworkStackConnector.aidl",
"core/java/android/net/INetworkStackStatusCallback.aidl",
"core/java/android/net/InitialConfigurationParcelable.aidl",
@@ -830,11 +830,22 @@ aidl_interface {
"core/java/android/net/ProxyInfoParcelable.aidl",
"core/java/android/net/RouteInfoParcelable.aidl",
"core/java/android/net/StaticIpConfigurationParcelable.aidl",
+ "core/java/android/net/TcpKeepalivePacketDataParcelable.aidl",
"core/java/android/net/dhcp/DhcpServingParamsParcel.aidl",
"core/java/android/net/dhcp/IDhcpServer.aidl",
"core/java/android/net/dhcp/IDhcpServerCallbacks.aidl",
"core/java/android/net/ip/IIpClient.aidl",
"core/java/android/net/ip/IIpClientCallbacks.aidl",
+ ],
+ api_dir: "aidl/networkstack",
+}
+
+aidl_interface {
+ name: "networkstack-aidl-framework",
+ local_include_dir: "core/java",
+ srcs: [
+ "core/java/android/net/TcpKeepalivePacketDataParcelable.aidl",
+ "core/java/android/net/IIpMemoryStore.aidl",
"core/java/android/net/ipmemorystore/**/*.aidl",
],
api_dir: "aidl/networkstack",
@@ -1107,58 +1118,21 @@ packages_to_document = [
"org/apache/http/params",
]
-// The since flag (-since N.xml API_LEVEL) is used to add API Level information
-// to the reference documentation. Must be in order of oldest to newest.
-//
-// Conscrypt (com.android.org.conscrypt) is an implementation detail and should
-// not be referenced in the documentation.
-framework_docs_args = "-android -manifest $(location core/res/AndroidManifest.xml) " +
- "-hidePackage com.android.internal " +
- "-hidePackage com.android.internal.util " +
- "-hidePackage com.android.okhttp " +
- "-hidePackage com.android.org.conscrypt " +
- "-hidePackage com.android.server " +
- "-since $(location 1/public/api/android.xml) 1 " +
- "-since $(location 2/public/api/android.xml) 2 " +
- "-since $(location 3/public/api/android.xml) 3 " +
- "-since $(location 4/public/api/android.xml) 4 " +
- "-since $(location 5/public/api/android.xml) 5 " +
- "-since $(location 6/public/api/android.xml) 6 " +
- "-since $(location 7/public/api/android.xml) 7 " +
- "-since $(location 8/public/api/android.xml) 8 " +
- "-since $(location 9/public/api/android.xml) 9 " +
- "-since $(location 10/public/api/android.xml) 10 " +
- "-since $(location 11/public/api/android.xml) 11 " +
- "-since $(location 12/public/api/android.xml) 12 " +
- "-since $(location 13/public/api/android.xml) 13 " +
- "-since $(location 14/public/api/android.txt) 14 " +
- "-since $(location 15/public/api/android.txt) 15 " +
- "-since $(location 16/public/api/android.txt) 16 " +
- "-since $(location 17/public/api/android.txt) 17 " +
- "-since $(location 18/public/api/android.txt) 18 " +
- "-since $(location 19/public/api/android.txt) 19 " +
- "-since $(location 20/public/api/android.txt) 20 " +
- "-since $(location 21/public/api/android.txt) 21 " +
- "-since $(location 22/public/api/android.txt) 22 " +
- "-since $(location 23/public/api/android.txt) 23 " +
- "-since $(location 24/public/api/android.txt) 24 " +
- "-since $(location 25/public/api/android.txt) 25 " +
- "-since $(location 26/public/api/android.txt) 26 " +
- "-since $(location 27/public/api/android.txt) 27 " +
- "-since $(location 28/public/api/android.txt) 28 " +
- "-since $(location api/current.txt) Q " +
- "-werror -lerror -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 " +
- "-overview $(location core/java/overview.html) " +
- // Federate Support Library references against local API file.
- "-federate SupportLib https://developer.android.com " +
- "-federationapi SupportLib $(location current/support-api.txt) "
+// Make the api/current.txt file available for use by modules in other
+// directories.
+filegroup {
+ name: "frameworks-base-api-current.txt",
+ srcs: [
+ "api/current.txt",
+ ],
+}
framework_docs_only_args = " -android -manifest $(location core/res/AndroidManifest.xml) " +
"-werror -lerror -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 " +
"-overview $(location core/java/overview.html) " +
// Federate Support Library references against local API file.
"-federate SupportLib https://developer.android.com " +
- "-federationapi SupportLib $(location current/support-api.txt) "
+ "-federationapi SupportLib $(location :current-support-api) "
framework_docs_only_libs = [
"voip-common",
diff --git a/Android.mk b/Android.mk
index e4053452a9c5..c58f7af1d7d5 100644
--- a/Android.mk
+++ b/Android.mk
@@ -72,42 +72,17 @@ $(OUT_DOCS)/offline-sdk-timestamp: $(OUT_DOCS)/offline-sdk-docs-docs.zip
$(hide) mkdir -p $(OUT_DOCS)/offline-sdk
( unzip -qo $< -d $(OUT_DOCS)/offline-sdk && touch -f $@ ) || exit 1
+.PHONY: docs offline-sdk-docs
+docs offline-sdk-docs: $(OUT_DOCS)/offline-sdk-timestamp
+
# Run this for checkbuild
checkbuild: doc-comment-check-docs
-# Check comment when you are updating the API
-update-api: doc-comment-check-docs
# ==== hiddenapi lists =======================================
-.KATI_RESTAT: $(INTERNAL_PLATFORM_HIDDENAPI_FLAGS)
-$(INTERNAL_PLATFORM_HIDDENAPI_FLAGS): \
- PRIVATE_FLAGS_INPUTS := $(PRIVATE_FLAGS_INPUTS) $(SOONG_HIDDENAPI_FLAGS)
-$(INTERNAL_PLATFORM_HIDDENAPI_FLAGS): \
- frameworks/base/tools/hiddenapi/generate_hiddenapi_lists.py \
- frameworks/base/config/hiddenapi-greylist.txt \
- frameworks/base/config/hiddenapi-greylist-max-p.txt \
- frameworks/base/config/hiddenapi-greylist-max-o.txt \
- frameworks/base/config/hiddenapi-force-blacklist.txt \
- $(INTERNAL_PLATFORM_HIDDENAPI_STUB_FLAGS) \
- $(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE) \
- $(SOONG_HIDDENAPI_FLAGS)
- frameworks/base/tools/hiddenapi/generate_hiddenapi_lists.py \
- --csv $(INTERNAL_PLATFORM_HIDDENAPI_STUB_FLAGS) $(PRIVATE_FLAGS_INPUTS) \
- --greylist frameworks/base/config/hiddenapi-greylist.txt \
- --greylist-ignore-conflicts $(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE) \
- --greylist-max-p frameworks/base/config/hiddenapi-greylist-max-p.txt \
- --greylist-max-o-ignore-conflicts \
- frameworks/base/config/hiddenapi-greylist-max-o.txt \
- --blacklist frameworks/base/config/hiddenapi-force-blacklist.txt \
- --output $@.tmp
- $(call commit-change-for-toc,$@)
-
-$(INTERNAL_PLATFORM_HIDDENAPI_GREYLIST_METADATA): \
- frameworks/base/tools/hiddenapi/merge_csv.py \
- $(PRIVATE_METADATA_INPUTS)
- frameworks/base/tools/hiddenapi/merge_csv.py $(PRIVATE_METADATA_INPUTS) > $@
-
+ifneq ($(UNSAFE_DISABLE_HIDDENAPI_FLAGS),true)
$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_FLAGS))
$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_GREYLIST_METADATA))
+endif # UNSAFE_DISABLE_HIDDENAPI_FLAGS
# Include subdirectory makefiles
# ============================================================
diff --git a/api/TEST_MAPPING b/api/TEST_MAPPING
new file mode 100644
index 000000000000..8a676e994081
--- /dev/null
+++ b/api/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsCurrentApiSignatureTestCases"
+ }
+ ]
+}
diff --git a/api/current.txt b/api/current.txt
index cb09edc7caf9..172140fd653f 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -18656,9 +18656,9 @@ package android.icu.text {
method public static android.icu.text.BreakIterator getSentenceInstance(java.util.Locale);
method public static android.icu.text.BreakIterator getSentenceInstance(android.icu.util.ULocale);
method public abstract java.text.CharacterIterator getText();
- method public static android.icu.text.BreakIterator getTitleInstance();
- method public static android.icu.text.BreakIterator getTitleInstance(java.util.Locale);
- method public static android.icu.text.BreakIterator getTitleInstance(android.icu.util.ULocale);
+ method @Deprecated public static android.icu.text.BreakIterator getTitleInstance();
+ method @Deprecated public static android.icu.text.BreakIterator getTitleInstance(java.util.Locale);
+ method @Deprecated public static android.icu.text.BreakIterator getTitleInstance(android.icu.util.ULocale);
method public static android.icu.text.BreakIterator getWordInstance();
method public static android.icu.text.BreakIterator getWordInstance(java.util.Locale);
method public static android.icu.text.BreakIterator getWordInstance(android.icu.util.ULocale);
@@ -18675,7 +18675,7 @@ package android.icu.text {
field public static final int KIND_CHARACTER = 0; // 0x0
field public static final int KIND_LINE = 2; // 0x2
field public static final int KIND_SENTENCE = 3; // 0x3
- field public static final int KIND_TITLE = 4; // 0x4
+ field @Deprecated public static final int KIND_TITLE = 4; // 0x4
field public static final int KIND_WORD = 1; // 0x1
field public static final int WORD_IDEO = 400; // 0x190
field public static final int WORD_IDEO_LIMIT = 500; // 0x1f4
@@ -27167,7 +27167,7 @@ package android.net {
field public static final String EXTRA_NETWORK = "android.net.extra.NETWORK";
field @Deprecated public static final String EXTRA_NETWORK_INFO = "networkInfo";
field public static final String EXTRA_NETWORK_REQUEST = "android.net.extra.NETWORK_REQUEST";
- field public static final String EXTRA_NETWORK_TYPE = "networkType";
+ field @Deprecated public static final String EXTRA_NETWORK_TYPE = "networkType";
field public static final String EXTRA_NO_CONNECTIVITY = "noConnectivity";
field @Deprecated public static final String EXTRA_OTHER_NETWORK_INFO = "otherNetwork";
field public static final String EXTRA_REASON = "reason";
@@ -27854,6 +27854,7 @@ package android.net {
method public android.net.VpnService.Builder setBlocking(boolean);
method public android.net.VpnService.Builder setConfigureIntent(android.app.PendingIntent);
method public android.net.VpnService.Builder setHttpProxy(android.net.ProxyInfo);
+ method public android.net.VpnService.Builder setMetered(boolean);
method public android.net.VpnService.Builder setMtu(int);
method public android.net.VpnService.Builder setSession(String);
method public android.net.VpnService.Builder setUnderlyingNetworks(android.net.Network[]);
@@ -40409,6 +40410,7 @@ package android.system {
method public static java.io.FileDescriptor accept(java.io.FileDescriptor, java.net.InetSocketAddress) throws android.system.ErrnoException, java.net.SocketException;
method public static boolean access(String, int) throws android.system.ErrnoException;
method public static void bind(java.io.FileDescriptor, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
+ method public static void bind(java.io.FileDescriptor, java.net.SocketAddress) throws android.system.ErrnoException, java.net.SocketException;
method public static void chmod(String, int) throws android.system.ErrnoException;
method public static void chown(String, int, int) throws android.system.ErrnoException;
method public static void close(java.io.FileDescriptor) throws android.system.ErrnoException;
@@ -40477,6 +40479,7 @@ package android.system {
method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.system.Int64Ref, long) throws android.system.ErrnoException;
method public static int sendto(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
method public static int sendto(java.io.FileDescriptor, byte[], int, int, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
+ method public static int sendto(java.io.FileDescriptor, byte[], int, int, int, java.net.SocketAddress) throws android.system.ErrnoException, java.net.SocketException;
method @Deprecated public static void setegid(int) throws android.system.ErrnoException;
method public static void setenv(String, String, boolean) throws android.system.ErrnoException;
method @Deprecated public static void seteuid(int) throws android.system.ErrnoException;
@@ -41156,6 +41159,7 @@ package android.telecom {
method public static String capabilitiesToString(int);
method public android.telecom.PhoneAccountHandle getAccountHandle();
method public int getCallCapabilities();
+ method public int getCallDirection();
method @Nullable public android.telecom.CallIdentification getCallIdentification();
method public int getCallProperties();
method public String getCallerDisplayName();
@@ -41192,6 +41196,9 @@ package android.telecom {
field public static final int CAPABILITY_SUPPORT_DEFLECT = 16777216; // 0x1000000
field public static final int CAPABILITY_SUPPORT_HOLD = 2; // 0x2
field public static final int CAPABILITY_SWAP_CONFERENCE = 8; // 0x8
+ field public static final int DIRECTION_INCOMING = 0; // 0x0
+ field public static final int DIRECTION_OUTGOING = 1; // 0x1
+ field public static final int DIRECTION_UNKNOWN = -1; // 0xffffffff
field public static final int PROPERTY_CONFERENCE = 1; // 0x1
field public static final int PROPERTY_EMERGENCY_CALLBACK_MODE = 4; // 0x4
field public static final int PROPERTY_ENTERPRISE_CALL = 32; // 0x20
@@ -41280,6 +41287,15 @@ package android.telecom {
method public abstract void onScreenCall(@NonNull android.telecom.Call.Details);
method public final void provideCallIdentification(@NonNull android.telecom.Call.Details, @NonNull android.telecom.CallIdentification);
method public final void respondToCall(@NonNull android.telecom.Call.Details, @NonNull android.telecom.CallScreeningService.CallResponse);
+ field public static final String ACTION_NUISANCE_CALL_STATUS_CHANGED = "android.telecom.action.NUISANCE_CALL_STATUS_CHANGED";
+ field public static final int CALL_DURATION_LONG = 4; // 0x4
+ field public static final int CALL_DURATION_MEDIUM = 3; // 0x3
+ field public static final int CALL_DURATION_SHORT = 2; // 0x2
+ field public static final int CALL_DURATION_VERY_SHORT = 1; // 0x1
+ field public static final String EXTRA_CALL_DURATION = "android.telecom.extra.CALL_DURATION";
+ field public static final String EXTRA_CALL_HANDLE = "android.telecom.extra.CALL_HANDLE";
+ field public static final String EXTRA_CALL_TYPE = "android.telecom.extra.CALL_TYPE";
+ field public static final String EXTRA_IS_NUISANCE = "android.telecom.extra.IS_NUISANCE";
field public static final String SERVICE_INTERFACE = "android.telecom.CallScreeningService";
}
@@ -41858,12 +41874,12 @@ package android.telecom {
public class TelecomManager {
method public void acceptHandover(android.net.Uri, int, android.telecom.PhoneAccountHandle);
- method @RequiresPermission(anyOf={android.Manifest.permission.ANSWER_PHONE_CALLS, android.Manifest.permission.MODIFY_PHONE_STATE}) public void acceptRingingCall();
- method @RequiresPermission(anyOf={android.Manifest.permission.ANSWER_PHONE_CALLS, android.Manifest.permission.MODIFY_PHONE_STATE}) public void acceptRingingCall(int);
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ANSWER_PHONE_CALLS, android.Manifest.permission.MODIFY_PHONE_STATE}) public void acceptRingingCall();
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ANSWER_PHONE_CALLS, android.Manifest.permission.MODIFY_PHONE_STATE}) public void acceptRingingCall(int);
method public void addNewIncomingCall(android.telecom.PhoneAccountHandle, android.os.Bundle);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void cancelMissedCallsNotification();
method public android.content.Intent createManageBlockedNumbersIntent();
- method @RequiresPermission(android.Manifest.permission.ANSWER_PHONE_CALLS) public boolean endCall();
+ method @Deprecated @RequiresPermission(android.Manifest.permission.ANSWER_PHONE_CALLS) public boolean endCall();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.net.Uri getAdnUriForPhoneAccount(android.telecom.PhoneAccountHandle);
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<android.telecom.PhoneAccountHandle> getCallCapablePhoneAccounts();
method public String getDefaultDialerPackage();
@@ -41885,6 +41901,7 @@ package android.telecom {
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isVoiceMailNumber(android.telecom.PhoneAccountHandle, String);
method @RequiresPermission(anyOf={android.Manifest.permission.CALL_PHONE, android.Manifest.permission.MANAGE_OWN_CALLS}) public void placeCall(android.net.Uri, android.os.Bundle);
method public void registerPhoneAccount(android.telecom.PhoneAccount);
+ method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void reportNuisanceCallStatus(@NonNull android.net.Uri, boolean);
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public void showInCallScreen(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void silenceRinger();
method public void unregisterPhoneAccount(android.telecom.PhoneAccountHandle);
@@ -42078,7 +42095,7 @@ package android.telephony {
}
public final class AvailableNetworkInfo implements android.os.Parcelable {
- ctor public AvailableNetworkInfo(int, int, java.util.ArrayList<java.lang.String>);
+ ctor public AvailableNetworkInfo(int, int, java.util.List<java.lang.String>);
method public int describeContents();
method public java.util.List<java.lang.String> getMccMncs();
method public int getPriority();
@@ -42319,6 +42336,7 @@ package android.telephony {
method public int getChannelNumber();
method public String getMccString();
method public String getMncString();
+ method public long getNci();
method public int getPci();
method public int getTac();
method public void writeToParcel(android.os.Parcel, int);
@@ -42332,6 +42350,7 @@ package android.telephony {
method public String getMccString();
method public String getMncString();
method @Nullable public String getMobileNetworkOperator();
+ method public int getUarfcn();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.telephony.CellIdentityTdscdma> CREATOR;
}
@@ -42999,12 +43018,12 @@ package android.telephony {
method public boolean canChangeDtmfToneLength();
method @Nullable public android.telephony.TelephonyManager createForPhoneAccountHandle(android.telecom.PhoneAccountHandle);
method public android.telephony.TelephonyManager createForSubscriptionId(int);
- method @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) public java.util.List<android.telephony.CellInfo> getAllCellInfo();
+ method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public java.util.List<android.telephony.CellInfo> getAllCellInfo();
method public int getCallState();
method public int getCardIdForDefaultEuicc();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @WorkerThread public android.os.PersistableBundle getCarrierConfig();
method public int getCarrierIdFromSimMccMnc();
- method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.telephony.CellLocation getCellLocation();
+ method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public android.telephony.CellLocation getCellLocation();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @Nullable public java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>> getCurrentEmergencyNumberList();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) @Nullable public java.util.Map<java.lang.Integer,java.util.List<android.telephony.emergency.EmergencyNumber>> getCurrentEmergencyNumberList(int);
method public int getDataActivity();
@@ -43033,8 +43052,8 @@ package android.telephony {
method public int getNetworkType();
method public int getPhoneCount();
method public int getPhoneType();
- method public int getPreferredOpportunisticDataSubscription();
- method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telephony.ServiceState getServiceState();
+ method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PHONE_STATE}) public int getPreferredOpportunisticDataSubscription();
+ method @RequiresPermission(allOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.ACCESS_COARSE_LOCATION}) public android.telephony.ServiceState getServiceState();
method @Nullable public android.telephony.SignalStrength getSignalStrength();
method public int getSimCarrierId();
method @Nullable public CharSequence getSimCarrierIdName();
@@ -43069,14 +43088,15 @@ package android.telephony {
method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_NETWORK_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isDataRoamingEnabled();
method public boolean isHearingAidCompatibilitySupported();
method public boolean isNetworkRoaming();
+ method public boolean isRttSupported();
method public boolean isSmsCapable();
method @Deprecated public boolean isTtyModeSupported();
method public boolean isVoiceCapable();
method public boolean isVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle);
method public boolean isWorldPhone();
method public void listen(android.telephony.PhoneStateListener, int);
- method @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) public void requestCellInfoUpdate(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, java.util.concurrent.Executor, android.telephony.TelephonyScanManager.NetworkScanCallback);
+ method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public void requestCellInfoUpdate(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
+ method @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, java.util.concurrent.Executor, android.telephony.TelephonyScanManager.NetworkScanCallback);
method public void sendDialerSpecialCode(String);
method public String sendEnvelopeWithStatus(String);
method @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void sendUssdRequest(String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler);
@@ -43092,6 +43112,7 @@ package android.telephony {
method public boolean setVoiceMailNumber(String, String);
method @Deprecated public void setVoicemailRingtoneUri(android.telecom.PhoneAccountHandle, android.net.Uri);
method @Deprecated public void setVoicemailVibrationEnabled(android.telecom.PhoneAccountHandle, boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void switchMultiSimConfig(int);
method public boolean updateAvailableNetworks(java.util.List<android.telephony.AvailableNetworkInfo>);
field public static final String ACTION_CONFIGURE_VOICEMAIL = "android.telephony.action.CONFIGURE_VOICEMAIL";
field public static final String ACTION_NETWORK_COUNTRY_CHANGED = "android.telephony.action.NETWORK_COUNTRY_CHANGED";
@@ -43143,7 +43164,6 @@ package android.telephony {
field public static final String EXTRA_STATE_RINGING;
field public static final String EXTRA_SUBSCRIPTION_ID = "android.telephony.extra.SUBSCRIPTION_ID";
field public static final String EXTRA_VOICEMAIL_NUMBER = "android.telephony.extra.VOICEMAIL_NUMBER";
- field public static final int INVALID_CARD_ID = -1; // 0xffffffff
field public static final String METADATA_HIDE_VOICEMAIL_SETTINGS_MENU = "android.telephony.HIDE_VOICEMAIL_SETTINGS_MENU";
field public static final int NETWORK_TYPE_1xRTT = 7; // 0x7
field public static final int NETWORK_TYPE_CDMA = 4; // 0x4
@@ -43179,7 +43199,9 @@ package android.telephony {
field public static final int SIM_STATE_PUK_REQUIRED = 3; // 0x3
field public static final int SIM_STATE_READY = 5; // 0x5
field public static final int SIM_STATE_UNKNOWN = 0; // 0x0
+ field public static final int UNINITIALIZED_CARD_ID = -2; // 0xfffffffe
field public static final int UNKNOWN_CARRIER_ID = -1; // 0xffffffff
+ field public static final int UNSUPPORTED_CARD_ID = -1; // 0xffffffff
field public static final int USSD_ERROR_SERVICE_UNAVAIL = -2; // 0xfffffffe
field public static final int USSD_RETURN_FAILURE = -1; // 0xffffffff
field public static final String VVM_TYPE_CVVM = "vvm_type_cvvm";
@@ -43533,6 +43555,483 @@ package android.telephony.gsm {
}
+package android.telephony.ims {
+
+ public class Rcs1To1Thread extends android.telephony.ims.RcsThread {
+ method @WorkerThread public long getFallbackThreadId() throws android.telephony.ims.RcsMessageStoreException;
+ method @NonNull @WorkerThread public android.telephony.ims.RcsParticipant getRecipient() throws android.telephony.ims.RcsMessageStoreException;
+ method public boolean isGroup();
+ method @WorkerThread public void setFallbackThreadId(long) throws android.telephony.ims.RcsMessageStoreException;
+ }
+
+ public abstract class RcsEvent {
+ ctor protected RcsEvent(long);
+ method public long getTimestamp();
+ }
+
+ public final class RcsEventQueryParams implements android.os.Parcelable {
+ method public int describeContents();
+ method @android.telephony.ims.RcsEventQueryParams.EventType public int getEventType();
+ method public int getLimit();
+ method public boolean getSortDirection();
+ method public int getSortingProperty();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final int ALL_EVENTS = -1; // 0xffffffff
+ field public static final int ALL_GROUP_THREAD_EVENTS = 0; // 0x0
+ field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsEventQueryParams> CREATOR;
+ field public static final int GROUP_THREAD_ICON_CHANGED_EVENT = 8; // 0x8
+ field public static final int GROUP_THREAD_NAME_CHANGED_EVENT = 16; // 0x10
+ field public static final int GROUP_THREAD_PARTICIPANT_JOINED_EVENT = 2; // 0x2
+ field public static final int GROUP_THREAD_PARTICIPANT_LEFT_EVENT = 4; // 0x4
+ field public static final int PARTICIPANT_ALIAS_CHANGED_EVENT = 1; // 0x1
+ field public static final int SORT_BY_CREATION_ORDER = 0; // 0x0
+ field public static final int SORT_BY_TIMESTAMP = 1; // 0x1
+ }
+
+ public static class RcsEventQueryParams.Builder {
+ ctor public RcsEventQueryParams.Builder();
+ method public android.telephony.ims.RcsEventQueryParams build();
+ method @CheckResult public android.telephony.ims.RcsEventQueryParams.Builder setEventType(@android.telephony.ims.RcsEventQueryParams.EventType int);
+ method @CheckResult public android.telephony.ims.RcsEventQueryParams.Builder setGroupThread(@NonNull android.telephony.ims.RcsGroupThread);
+ method @CheckResult public android.telephony.ims.RcsEventQueryParams.Builder setResultLimit(@IntRange(from=0) int) throws java.security.InvalidParameterException;
+ method @CheckResult public android.telephony.ims.RcsEventQueryParams.Builder setSortDirection(boolean);
+ method @CheckResult public android.telephony.ims.RcsEventQueryParams.Builder setSortProperty(@android.telephony.ims.RcsEventQueryParams.SortingProperty int);
+ }
+
+ @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({android.telephony.ims.RcsEventQueryParams.ALL_EVENTS, android.telephony.ims.RcsEventQueryParams.ALL_GROUP_THREAD_EVENTS, android.telephony.ims.RcsEventQueryParams.PARTICIPANT_ALIAS_CHANGED_EVENT, android.telephony.ims.RcsEventQueryParams.GROUP_THREAD_PARTICIPANT_JOINED_EVENT, android.telephony.ims.RcsEventQueryParams.GROUP_THREAD_PARTICIPANT_LEFT_EVENT, android.telephony.ims.RcsEventQueryParams.GROUP_THREAD_NAME_CHANGED_EVENT, android.telephony.ims.RcsEventQueryParams.GROUP_THREAD_ICON_CHANGED_EVENT}) public static @interface RcsEventQueryParams.EventType {
+ }
+
+ @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({android.telephony.ims.RcsEventQueryParams.SORT_BY_CREATION_ORDER, android.telephony.ims.RcsEventQueryParams.SORT_BY_TIMESTAMP}) public static @interface RcsEventQueryParams.SortingProperty {
+ }
+
+ public final class RcsEventQueryResult implements android.os.Parcelable {
+ method public int describeContents();
+ method public android.telephony.ims.RcsQueryContinuationToken getContinuationToken();
+ method public java.util.List<android.telephony.ims.RcsEvent> getEvents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsEventQueryResult> CREATOR;
+ }
+
+ public final class RcsFileTransferCreationParams implements android.os.Parcelable {
+ method public int describeContents();
+ method public String getContentMimeType();
+ method public android.net.Uri getContentUri();
+ method public long getFileSize();
+ method @android.telephony.ims.RcsFileTransferPart.RcsFileTransferStatus public int getFileTransferStatus();
+ method public int getHeight();
+ method public long getMediaDuration();
+ method public String getPreviewMimeType();
+ method public android.net.Uri getPreviewUri();
+ method public String getRcsFileTransferSessionId();
+ method public long getTransferOffset();
+ method public int getWidth();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsFileTransferCreationParams> CREATOR;
+ }
+
+ public class RcsFileTransferCreationParams.Builder {
+ ctor public RcsFileTransferCreationParams.Builder();
+ method public android.telephony.ims.RcsFileTransferCreationParams build();
+ method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setContentMimeType(String);
+ method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setContentUri(android.net.Uri);
+ method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setFileSize(long);
+ method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setFileTransferSessionId(String);
+ method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setFileTransferStatus(@android.telephony.ims.RcsFileTransferPart.RcsFileTransferStatus int);
+ method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setHeight(int);
+ method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setMediaDuration(long);
+ method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setPreviewMimeType(String);
+ method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setPreviewUri(android.net.Uri);
+ method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setTransferOffset(long);
+ method @CheckResult public android.telephony.ims.RcsFileTransferCreationParams.Builder setWidth(int);
+ }
+
+ public class RcsFileTransferPart {
+ method @WorkerThread @Nullable public String getContentMimeType() throws android.telephony.ims.RcsMessageStoreException;
+ method @Nullable @WorkerThread public android.net.Uri getContentUri() throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public long getFileSize() throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public String getFileTransferSessionId() throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread @android.telephony.ims.RcsFileTransferPart.RcsFileTransferStatus public int getFileTransferStatus() throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public int getHeight() throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public long getLength() throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public String getPreviewMimeType() throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public android.net.Uri getPreviewUri() throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public long getTransferOffset() throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public int getWidth() throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public void setContentMimeType(String) throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public void setContentUri(android.net.Uri) throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public void setFileSize(long) throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public void setFileTransferSessionId(String) throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public void setFileTransferStatus(@android.telephony.ims.RcsFileTransferPart.RcsFileTransferStatus int) throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public void setHeight(int) throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public void setLength(long) throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public void setPreviewMimeType(String) throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public void setPreviewUri(android.net.Uri) throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public void setTransferOffset(long) throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public void setWidth(int) throws android.telephony.ims.RcsMessageStoreException;
+ field public static final int DOWNLOADING = 6; // 0x6
+ field public static final int DOWNLOADING_CANCELLED = 9; // 0x9
+ field public static final int DOWNLOADING_FAILED = 8; // 0x8
+ field public static final int DOWNLOADING_PAUSED = 7; // 0x7
+ field public static final int DRAFT = 1; // 0x1
+ field public static final int NOT_SET = 0; // 0x0
+ field public static final int SENDING = 2; // 0x2
+ field public static final int SENDING_CANCELLED = 5; // 0x5
+ field public static final int SENDING_FAILED = 4; // 0x4
+ field public static final int SENDING_PAUSED = 3; // 0x3
+ field public static final int SUCCEEDED = 10; // 0xa
+ }
+
+ @IntDef({android.telephony.ims.RcsFileTransferPart.DRAFT, android.telephony.ims.RcsFileTransferPart.SENDING, android.telephony.ims.RcsFileTransferPart.SENDING_PAUSED, android.telephony.ims.RcsFileTransferPart.SENDING_FAILED, android.telephony.ims.RcsFileTransferPart.SENDING_CANCELLED, android.telephony.ims.RcsFileTransferPart.DOWNLOADING, android.telephony.ims.RcsFileTransferPart.DOWNLOADING_PAUSED, android.telephony.ims.RcsFileTransferPart.DOWNLOADING_FAILED, android.telephony.ims.RcsFileTransferPart.DOWNLOADING_CANCELLED, android.telephony.ims.RcsFileTransferPart.SUCCEEDED}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface RcsFileTransferPart.RcsFileTransferStatus {
+ }
+
+ public class RcsGroupThread extends android.telephony.ims.RcsThread {
+ method @WorkerThread public void addParticipant(@NonNull android.telephony.ims.RcsParticipant) throws android.telephony.ims.RcsMessageStoreException;
+ method @Nullable @WorkerThread public android.net.Uri getConferenceUri() throws android.telephony.ims.RcsMessageStoreException;
+ method @Nullable public android.net.Uri getGroupIcon() throws android.telephony.ims.RcsMessageStoreException;
+ method @Nullable @WorkerThread public String getGroupName() throws android.telephony.ims.RcsMessageStoreException;
+ method @Nullable @WorkerThread public android.telephony.ims.RcsParticipant getOwner() throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread @NonNull public java.util.Set<android.telephony.ims.RcsParticipant> getParticipants() throws android.telephony.ims.RcsMessageStoreException;
+ method public boolean isGroup();
+ method @WorkerThread public void removeParticipant(@NonNull android.telephony.ims.RcsParticipant) throws android.telephony.ims.RcsMessageStoreException;
+ method @Nullable @WorkerThread public void setConferenceUri(android.net.Uri) throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public void setGroupIcon(@Nullable android.net.Uri) throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public void setGroupName(String) throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public void setOwner(@Nullable android.telephony.ims.RcsParticipant) throws android.telephony.ims.RcsMessageStoreException;
+ }
+
+ public abstract class RcsGroupThreadEvent extends android.telephony.ims.RcsEvent {
+ method @NonNull public android.telephony.ims.RcsParticipant getOriginatingParticipant();
+ method @NonNull public android.telephony.ims.RcsGroupThread getRcsGroupThread();
+ }
+
+ public final class RcsGroupThreadIconChangedEvent extends android.telephony.ims.RcsGroupThreadEvent implements android.os.Parcelable {
+ ctor public RcsGroupThreadIconChangedEvent(long, @NonNull android.telephony.ims.RcsGroupThread, @NonNull android.telephony.ims.RcsParticipant, @Nullable android.net.Uri);
+ method public int describeContents();
+ method @Nullable public android.net.Uri getNewIcon();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsGroupThreadIconChangedEvent> CREATOR;
+ }
+
+ public final class RcsGroupThreadNameChangedEvent extends android.telephony.ims.RcsGroupThreadEvent implements android.os.Parcelable {
+ ctor public RcsGroupThreadNameChangedEvent(long, @NonNull android.telephony.ims.RcsGroupThread, @NonNull android.telephony.ims.RcsParticipant, @Nullable String);
+ method public int describeContents();
+ method @Nullable public String getNewName();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsGroupThreadNameChangedEvent> CREATOR;
+ }
+
+ public final class RcsGroupThreadParticipantJoinedEvent extends android.telephony.ims.RcsGroupThreadEvent implements android.os.Parcelable {
+ ctor public RcsGroupThreadParticipantJoinedEvent(long, @NonNull android.telephony.ims.RcsGroupThread, @NonNull android.telephony.ims.RcsParticipant, @NonNull android.telephony.ims.RcsParticipant);
+ method public int describeContents();
+ method public android.telephony.ims.RcsParticipant getJoinedParticipant();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsGroupThreadParticipantJoinedEvent> CREATOR;
+ }
+
+ public final class RcsGroupThreadParticipantLeftEvent extends android.telephony.ims.RcsGroupThreadEvent implements android.os.Parcelable {
+ ctor public RcsGroupThreadParticipantLeftEvent(long, @NonNull android.telephony.ims.RcsGroupThread, @NonNull android.telephony.ims.RcsParticipant, @NonNull android.telephony.ims.RcsParticipant);
+ method public int describeContents();
+ method @NonNull public android.telephony.ims.RcsParticipant getLeavingParticipantId();
+ method public void persist() throws android.telephony.ims.RcsMessageStoreException;
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsGroupThreadParticipantLeftEvent> CREATOR;
+ }
+
+ public class RcsIncomingMessage extends android.telephony.ims.RcsMessage {
+ method @WorkerThread public long getArrivalTimestamp() throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public long getSeenTimestamp() throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public android.telephony.ims.RcsParticipant getSenderParticipant() throws android.telephony.ims.RcsMessageStoreException;
+ method public boolean isIncoming();
+ method @WorkerThread public void setArrivalTimestamp(long) throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public void setSeenTimestamp(long) throws android.telephony.ims.RcsMessageStoreException;
+ }
+
+ public final class RcsIncomingMessageCreationParams extends android.telephony.ims.RcsMessageCreationParams implements android.os.Parcelable {
+ method public int describeContents();
+ method public long getArrivalTimestamp();
+ method public long getSeenTimestamp();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsIncomingMessageCreationParams> CREATOR;
+ }
+
+ public static class RcsIncomingMessageCreationParams.Builder extends android.telephony.ims.RcsMessageCreationParams.Builder {
+ ctor public RcsIncomingMessageCreationParams.Builder(long, long, int);
+ method public android.telephony.ims.RcsIncomingMessageCreationParams build();
+ method @CheckResult public android.telephony.ims.RcsIncomingMessageCreationParams.Builder setArrivalTimestamp(long);
+ method @CheckResult public android.telephony.ims.RcsIncomingMessageCreationParams.Builder setSeenTimestamp(long);
+ method @CheckResult public android.telephony.ims.RcsIncomingMessageCreationParams.Builder setSenderParticipant(android.telephony.ims.RcsParticipant);
+ }
+
+ public class RcsManager {
+ method public android.telephony.ims.RcsMessageStore getRcsMessageStore();
+ }
+
+ public abstract class RcsMessage {
+ method @NonNull @WorkerThread public java.util.Set<android.telephony.ims.RcsFileTransferPart> getFileTransferParts() throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public double getLatitude() throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public double getLongitude() throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public long getOriginationTimestamp() throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public String getRcsMessageId() throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread @android.telephony.ims.RcsMessage.RcsMessageStatus public int getStatus() throws android.telephony.ims.RcsMessageStoreException;
+ method public int getSubscriptionId() throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public String getText() throws android.telephony.ims.RcsMessageStoreException;
+ method @NonNull @WorkerThread public android.telephony.ims.RcsFileTransferPart insertFileTransfer(android.telephony.ims.RcsFileTransferCreationParams) throws android.telephony.ims.RcsMessageStoreException;
+ method public abstract boolean isIncoming();
+ method @WorkerThread public void removeFileTransferPart(@NonNull android.telephony.ims.RcsFileTransferPart) throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public void setLatitude(double) throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public void setLongitude(double) throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public void setOriginationTimestamp(long) throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public void setRcsMessageId(String) throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public void setStatus(@android.telephony.ims.RcsMessage.RcsMessageStatus int) throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public void setSubscriptionId(int) throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public void setText(String) throws android.telephony.ims.RcsMessageStoreException;
+ field public static final int DRAFT = 1; // 0x1
+ field public static final int FAILED = 6; // 0x6
+ field public static final double LOCATION_NOT_SET = 4.9E-324;
+ field public static final int NOT_SET = 0; // 0x0
+ field public static final int QUEUED = 2; // 0x2
+ field public static final int RECEIVED = 7; // 0x7
+ field public static final int RETRYING = 5; // 0x5
+ field public static final int SEEN = 9; // 0x9
+ field public static final int SENDING = 3; // 0x3
+ field public static final int SENT = 4; // 0x4
+ }
+
+ @IntDef({android.telephony.ims.RcsMessage.DRAFT, android.telephony.ims.RcsMessage.QUEUED, android.telephony.ims.RcsMessage.SENDING, android.telephony.ims.RcsMessage.SENT, android.telephony.ims.RcsMessage.RETRYING, android.telephony.ims.RcsMessage.FAILED, android.telephony.ims.RcsMessage.RECEIVED, android.telephony.ims.RcsMessage.SEEN}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface RcsMessage.RcsMessageStatus {
+ }
+
+ public class RcsMessageCreationParams {
+ ctor protected RcsMessageCreationParams(android.telephony.ims.RcsMessageCreationParams.Builder);
+ method public double getLatitude();
+ method public double getLongitude();
+ method public int getMessageStatus();
+ method public long getOriginationTimestamp();
+ method @Nullable public String getRcsMessageGlobalId();
+ method public int getSubId();
+ method @Nullable public String getText();
+ }
+
+ public static class RcsMessageCreationParams.Builder {
+ method public android.telephony.ims.RcsMessageCreationParams build();
+ method @CheckResult public android.telephony.ims.RcsMessageCreationParams.Builder setLatitude(double);
+ method @CheckResult public android.telephony.ims.RcsMessageCreationParams.Builder setLongitude(double);
+ method @CheckResult public android.telephony.ims.RcsMessageCreationParams.Builder setRcsMessageId(String);
+ method @CheckResult public android.telephony.ims.RcsMessageCreationParams.Builder setStatus(@android.telephony.ims.RcsMessage.RcsMessageStatus int);
+ method @CheckResult public android.telephony.ims.RcsMessageCreationParams.Builder setText(String);
+ }
+
+ public final class RcsMessageQueryParams implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getFileTransferPresence();
+ method public int getLimit();
+ method public String getMessageLike();
+ method public int getMessageType();
+ method public boolean getSortDirection();
+ method @android.telephony.ims.RcsMessageQueryParams.SortingProperty public int getSortingProperty();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsMessageQueryParams> CREATOR;
+ field public static final int MESSAGES_WITHOUT_FILE_TRANSFERS = 8; // 0x8
+ field public static final int MESSAGES_WITH_FILE_TRANSFERS = 4; // 0x4
+ field public static final int MESSAGE_TYPE_INCOMING = 1; // 0x1
+ field public static final int MESSAGE_TYPE_OUTGOING = 2; // 0x2
+ field public static final int SORT_BY_CREATION_ORDER = 0; // 0x0
+ field public static final int SORT_BY_TIMESTAMP = 1; // 0x1
+ }
+
+ public static class RcsMessageQueryParams.Builder {
+ ctor public RcsMessageQueryParams.Builder();
+ method public android.telephony.ims.RcsMessageQueryParams build();
+ method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setFileTransferPresence(int);
+ method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setMessageLike(String);
+ method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setMessageType(int);
+ method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setResultLimit(@IntRange(from=0) int) throws java.security.InvalidParameterException;
+ method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setSortDirection(boolean);
+ method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setSortProperty(@android.telephony.ims.RcsMessageQueryParams.SortingProperty int);
+ method @CheckResult public android.telephony.ims.RcsMessageQueryParams.Builder setThread(@Nullable android.telephony.ims.RcsThread);
+ }
+
+ @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({android.telephony.ims.RcsMessageQueryParams.SORT_BY_CREATION_ORDER, android.telephony.ims.RcsMessageQueryParams.SORT_BY_TIMESTAMP}) public static @interface RcsMessageQueryParams.SortingProperty {
+ }
+
+ public final class RcsMessageQueryResult implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public android.telephony.ims.RcsQueryContinuationToken getContinuationToken();
+ method @NonNull public java.util.List<android.telephony.ims.RcsMessage> getMessages();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsMessageQueryResult> CREATOR;
+ }
+
+ public final class RcsMessageSnippet implements android.os.Parcelable {
+ method public int describeContents();
+ method @android.telephony.ims.RcsMessage.RcsMessageStatus public int getSnippetStatus();
+ method @Nullable public String getSnippetText();
+ method public long getSnippetTimestamp();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsMessageSnippet> CREATOR;
+ }
+
+ public class RcsMessageStore {
+ ctor public RcsMessageStore();
+ method @WorkerThread @NonNull public android.telephony.ims.RcsGroupThread createGroupThread(@Nullable java.util.List<android.telephony.ims.RcsParticipant>, @Nullable String, @Nullable android.net.Uri) throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread @NonNull public android.telephony.ims.Rcs1To1Thread createRcs1To1Thread(@NonNull android.telephony.ims.RcsParticipant) throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread @NonNull public android.telephony.ims.RcsParticipant createRcsParticipant(String, @Nullable String) throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public void deleteThread(@NonNull android.telephony.ims.RcsThread) throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread @NonNull public android.telephony.ims.RcsEventQueryResult getRcsEvents(@Nullable android.telephony.ims.RcsEventQueryParams) throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread @NonNull public android.telephony.ims.RcsEventQueryResult getRcsEvents(@NonNull android.telephony.ims.RcsQueryContinuationToken) throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread @NonNull public android.telephony.ims.RcsMessageQueryResult getRcsMessages(@Nullable android.telephony.ims.RcsMessageQueryParams) throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread @NonNull public android.telephony.ims.RcsMessageQueryResult getRcsMessages(@NonNull android.telephony.ims.RcsQueryContinuationToken) throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread @NonNull public android.telephony.ims.RcsParticipantQueryResult getRcsParticipants(@Nullable android.telephony.ims.RcsParticipantQueryParams) throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread @NonNull public android.telephony.ims.RcsParticipantQueryResult getRcsParticipants(@NonNull android.telephony.ims.RcsQueryContinuationToken) throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread @NonNull public android.telephony.ims.RcsThreadQueryResult getRcsThreads(@Nullable android.telephony.ims.RcsThreadQueryParams) throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread @NonNull public android.telephony.ims.RcsThreadQueryResult getRcsThreads(@NonNull android.telephony.ims.RcsQueryContinuationToken) throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread @NonNull public void persistRcsEvent(android.telephony.ims.RcsEvent) throws android.telephony.ims.RcsMessageStoreException;
+ }
+
+ public class RcsMessageStoreException extends java.lang.Exception {
+ ctor public RcsMessageStoreException(String);
+ }
+
+ public class RcsOutgoingMessage extends android.telephony.ims.RcsMessage {
+ method @NonNull @WorkerThread public java.util.List<android.telephony.ims.RcsOutgoingMessageDelivery> getOutgoingDeliveries() throws android.telephony.ims.RcsMessageStoreException;
+ method public boolean isIncoming();
+ }
+
+ public final class RcsOutgoingMessageCreationParams extends android.telephony.ims.RcsMessageCreationParams implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsOutgoingMessageCreationParams> CREATOR;
+ }
+
+ public static class RcsOutgoingMessageCreationParams.Builder extends android.telephony.ims.RcsMessageCreationParams.Builder {
+ ctor public RcsOutgoingMessageCreationParams.Builder(long, int);
+ method public android.telephony.ims.RcsOutgoingMessageCreationParams build();
+ }
+
+ public class RcsOutgoingMessageDelivery {
+ method @WorkerThread public long getDeliveredTimestamp() throws android.telephony.ims.RcsMessageStoreException;
+ method @NonNull public android.telephony.ims.RcsOutgoingMessage getMessage();
+ method @NonNull public android.telephony.ims.RcsParticipant getRecipient();
+ method @WorkerThread public long getSeenTimestamp() throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread @android.telephony.ims.RcsMessage.RcsMessageStatus public int getStatus() throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public void setDeliveredTimestamp(long) throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public void setSeenTimestamp(long) throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public void setStatus(@android.telephony.ims.RcsMessage.RcsMessageStatus int) throws android.telephony.ims.RcsMessageStoreException;
+ }
+
+ public class RcsParticipant {
+ method @Nullable @WorkerThread public String getAlias() throws android.telephony.ims.RcsMessageStoreException;
+ method @Nullable @WorkerThread public String getCanonicalAddress() throws android.telephony.ims.RcsMessageStoreException;
+ method @Nullable @WorkerThread public String getContactId() throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public void setAlias(String) throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public void setContactId(String) throws android.telephony.ims.RcsMessageStoreException;
+ }
+
+ public final class RcsParticipantAliasChangedEvent extends android.telephony.ims.RcsEvent implements android.os.Parcelable {
+ ctor public RcsParticipantAliasChangedEvent(long, @NonNull android.telephony.ims.RcsParticipant, @Nullable String);
+ method public int describeContents();
+ method @Nullable public String getNewAlias();
+ method @NonNull public android.telephony.ims.RcsParticipant getParticipantId();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsParticipantAliasChangedEvent> CREATOR;
+ }
+
+ public final class RcsParticipantQueryParams implements android.os.Parcelable {
+ method public int describeContents();
+ method public String getAliasLike();
+ method public String getCanonicalAddressLike();
+ method public int getLimit();
+ method public boolean getSortDirection();
+ method public int getSortingProperty();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsParticipantQueryParams> CREATOR;
+ field public static final int SORT_BY_ALIAS = 1; // 0x1
+ field public static final int SORT_BY_CANONICAL_ADDRESS = 2; // 0x2
+ field public static final int SORT_BY_CREATION_ORDER = 0; // 0x0
+ }
+
+ public static class RcsParticipantQueryParams.Builder {
+ ctor public RcsParticipantQueryParams.Builder();
+ method public android.telephony.ims.RcsParticipantQueryParams build();
+ method @CheckResult public android.telephony.ims.RcsParticipantQueryParams.Builder setAliasLike(String);
+ method @CheckResult public android.telephony.ims.RcsParticipantQueryParams.Builder setCanonicalAddressLike(String);
+ method @CheckResult public android.telephony.ims.RcsParticipantQueryParams.Builder setResultLimit(@IntRange(from=0) int) throws java.security.InvalidParameterException;
+ method @CheckResult public android.telephony.ims.RcsParticipantQueryParams.Builder setSortDirection(boolean);
+ method @CheckResult public android.telephony.ims.RcsParticipantQueryParams.Builder setSortProperty(@android.telephony.ims.RcsParticipantQueryParams.SortingProperty int);
+ method @CheckResult public android.telephony.ims.RcsParticipantQueryParams.Builder setThread(android.telephony.ims.RcsThread);
+ }
+
+ @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({android.telephony.ims.RcsParticipantQueryParams.SORT_BY_CREATION_ORDER, android.telephony.ims.RcsParticipantQueryParams.SORT_BY_ALIAS, android.telephony.ims.RcsParticipantQueryParams.SORT_BY_CANONICAL_ADDRESS}) public static @interface RcsParticipantQueryParams.SortingProperty {
+ }
+
+ public final class RcsParticipantQueryResult implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public android.telephony.ims.RcsQueryContinuationToken getContinuationToken();
+ method @NonNull public java.util.List<android.telephony.ims.RcsParticipant> getParticipants();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsParticipantQueryResult> CREATOR;
+ }
+
+ public final class RcsQueryContinuationToken implements android.os.Parcelable {
+ method public int describeContents();
+ method @android.telephony.ims.RcsQueryContinuationToken.ContinuationTokenType public int getQueryType();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsQueryContinuationToken> CREATOR;
+ field public static final int EVENT_QUERY_CONTINUATION_TOKEN_TYPE = 0; // 0x0
+ field public static final int MESSAGE_QUERY_CONTINUATION_TOKEN_TYPE = 1; // 0x1
+ field public static final int PARTICIPANT_QUERY_CONTINUATION_TOKEN_TYPE = 2; // 0x2
+ field public static final int THREAD_QUERY_CONTINUATION_TOKEN_TYPE = 3; // 0x3
+ }
+
+ @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({android.telephony.ims.RcsQueryContinuationToken.EVENT_QUERY_CONTINUATION_TOKEN_TYPE, android.telephony.ims.RcsQueryContinuationToken.MESSAGE_QUERY_CONTINUATION_TOKEN_TYPE, android.telephony.ims.RcsQueryContinuationToken.PARTICIPANT_QUERY_CONTINUATION_TOKEN_TYPE, android.telephony.ims.RcsQueryContinuationToken.THREAD_QUERY_CONTINUATION_TOKEN_TYPE}) public static @interface RcsQueryContinuationToken.ContinuationTokenType {
+ }
+
+ public abstract class RcsThread {
+ method @WorkerThread @NonNull public android.telephony.ims.RcsIncomingMessage addIncomingMessage(@NonNull android.telephony.ims.RcsIncomingMessageCreationParams) throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread @NonNull public android.telephony.ims.RcsOutgoingMessage addOutgoingMessage(@NonNull android.telephony.ims.RcsOutgoingMessageCreationParams) throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread public void deleteMessage(@NonNull android.telephony.ims.RcsMessage) throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread @NonNull public android.telephony.ims.RcsMessageQueryResult getMessages() throws android.telephony.ims.RcsMessageStoreException;
+ method @WorkerThread @NonNull public android.telephony.ims.RcsMessageSnippet getSnippet() throws android.telephony.ims.RcsMessageStoreException;
+ method public abstract boolean isGroup();
+ }
+
+ public final class RcsThreadQueryParams implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getLimit();
+ method public boolean getSortDirection();
+ method @android.telephony.ims.RcsThreadQueryParams.SortingProperty public int getSortingProperty();
+ method public int getThreadType();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsThreadQueryParams> CREATOR;
+ field public static final int SORT_BY_CREATION_ORDER = 0; // 0x0
+ field public static final int SORT_BY_TIMESTAMP = 1; // 0x1
+ field public static final int THREAD_TYPE_1_TO_1 = 2; // 0x2
+ field public static final int THREAD_TYPE_GROUP = 1; // 0x1
+ }
+
+ public static class RcsThreadQueryParams.Builder {
+ ctor public RcsThreadQueryParams.Builder();
+ method public android.telephony.ims.RcsThreadQueryParams build();
+ method @CheckResult public android.telephony.ims.RcsThreadQueryParams.Builder setParticipant(@NonNull android.telephony.ims.RcsParticipant);
+ method @CheckResult public android.telephony.ims.RcsThreadQueryParams.Builder setParticipants(@NonNull java.util.List<android.telephony.ims.RcsParticipant>);
+ method @CheckResult public android.telephony.ims.RcsThreadQueryParams.Builder setResultLimit(@IntRange(from=0) int) throws java.security.InvalidParameterException;
+ method @CheckResult public android.telephony.ims.RcsThreadQueryParams.Builder setSortDirection(boolean);
+ method @CheckResult public android.telephony.ims.RcsThreadQueryParams.Builder setSortProperty(@android.telephony.ims.RcsThreadQueryParams.SortingProperty int);
+ method @CheckResult public android.telephony.ims.RcsThreadQueryParams.Builder setThreadType(int);
+ }
+
+ @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @IntDef({android.telephony.ims.RcsThreadQueryParams.SORT_BY_CREATION_ORDER, android.telephony.ims.RcsThreadQueryParams.SORT_BY_TIMESTAMP}) public static @interface RcsThreadQueryParams.SortingProperty {
+ }
+
+ public final class RcsThreadQueryResult implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public android.telephony.ims.RcsQueryContinuationToken getContinuationToken();
+ method @NonNull public java.util.List<android.telephony.ims.RcsThread> getThreads();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.telephony.ims.RcsThreadQueryResult> CREATOR;
+ }
+
+}
+
package android.telephony.mbms {
public class DownloadProgressListener {
@@ -43599,9 +44098,9 @@ package android.telephony.mbms {
}
public interface GroupCallCallback {
- method public void onBroadcastSignalStrengthUpdated(@IntRange(from=0xffffffff, to=4) int);
- method public void onError(int, @Nullable String);
- method public void onGroupCallStateChanged(int, int);
+ method public default void onBroadcastSignalStrengthUpdated(@IntRange(from=0xffffffff, to=4) int);
+ method public default void onError(int, @Nullable String);
+ method public default void onGroupCallStateChanged(int, int);
field public static final int SIGNAL_STRENGTH_UNAVAILABLE = -1; // 0xffffffff
}
@@ -43659,10 +44158,10 @@ package android.telephony.mbms {
}
public interface MbmsGroupCallSessionCallback {
- method public void onAvailableSaisUpdated(@NonNull java.util.List<java.lang.Integer>, @NonNull java.util.List<java.util.List<java.lang.Integer>>);
- method public void onError(int, @Nullable String);
- method public void onMiddlewareReady();
- method public void onServiceInterfaceAvailable(@NonNull String, int);
+ method public default void onAvailableSaisUpdated(@NonNull java.util.List<java.lang.Integer>, @NonNull java.util.List<java.util.List<java.lang.Integer>>);
+ method public default void onError(int, @Nullable String);
+ method public default void onMiddlewareReady();
+ method public default void onServiceInterfaceAvailable(@NonNull String, int);
}
public class MbmsStreamingSessionCallback {
@@ -58466,9 +58965,9 @@ package java.lang.invoke {
method public String getName();
method public int getReferenceKind();
method public default boolean isVarArgs();
- method public static boolean refKindIsField(int);
- method public static boolean refKindIsValid(int);
- method public static String refKindName(int);
+ method @Deprecated public static boolean refKindIsField(int);
+ method @Deprecated public static boolean refKindIsValid(int);
+ method @Deprecated public static String refKindName(int);
method public static String referenceKindToString(int);
method public <T extends java.lang.reflect.Member> T reflectAs(Class<T>, java.lang.invoke.MethodHandles.Lookup);
method public static String toString(int, Class<?>, String, java.lang.invoke.MethodType);
diff --git a/api/removed.txt b/api/removed.txt
index 05d52d4e40c0..72202ad9712a 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -540,7 +540,7 @@ package android.telephony {
public class TelephonyManager {
method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION) public java.util.List<android.telephony.NeighboringCellInfo> getNeighboringCellInfo();
- method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, android.telephony.TelephonyScanManager.NetworkScanCallback);
+ method @Deprecated @RequiresPermission(allOf={android.Manifest.permission.MODIFY_PHONE_STATE, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, android.telephony.TelephonyScanManager.NetworkScanCallback);
}
}
diff --git a/api/system-current.txt b/api/system-current.txt
index 9cfe60428fe1..9c45fb4ee5c4 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -101,6 +101,7 @@ package android {
field public static final String OBSERVE_APP_USAGE = "android.permission.OBSERVE_APP_USAGE";
field public static final String OVERRIDE_WIFI_CONFIG = "android.permission.OVERRIDE_WIFI_CONFIG";
field public static final String PACKAGE_VERIFICATION_AGENT = "android.permission.PACKAGE_VERIFICATION_AGENT";
+ field public static final String PACKET_KEEPALIVE_OFFLOAD = "android.permission.PACKET_KEEPALIVE_OFFLOAD";
field public static final String PEERS_MAC_ADDRESS = "android.permission.PEERS_MAC_ADDRESS";
field public static final String PERFORM_CDMA_PROVISIONING = "android.permission.PERFORM_CDMA_PROVISIONING";
field public static final String PERFORM_SIM_ACTIVATION = "android.permission.PERFORM_SIM_ACTIVATION";
@@ -837,6 +838,7 @@ package android.content {
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public void startActivityAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle);
field public static final String BACKUP_SERVICE = "backup";
field public static final String CONTEXTHUB_SERVICE = "contexthub";
+ field public static final String DYNAMIC_ANDROID_SERVICE = "dynamic_android";
field public static final String EUICC_CARD_SERVICE = "euicc_card";
field public static final String HDMI_CONTROL_SERVICE = "hdmi_control";
field public static final String NETD_SERVICE = "netd";
@@ -860,6 +862,32 @@ package android.content {
method public void sendOrderedBroadcast(android.content.Intent, String, android.os.Bundle, android.content.BroadcastReceiver, android.os.Handler, int, String, android.os.Bundle);
}
+ public class DynamicAndroidClient {
+ ctor public DynamicAndroidClient(@NonNull android.content.Context);
+ method public void bind();
+ method public void setOnStatusChangedListener(@NonNull android.content.DynamicAndroidClient.OnStatusChangedListener, @NonNull java.util.concurrent.Executor);
+ method public void setOnStatusChangedListener(@NonNull android.content.DynamicAndroidClient.OnStatusChangedListener);
+ method public void start(String, long);
+ method public void start(String, long, long);
+ method public void unbind();
+ field public static final int CAUSE_ERROR_EXCEPTION = 6; // 0x6
+ field public static final int CAUSE_ERROR_INVALID_URL = 4; // 0x4
+ field public static final int CAUSE_ERROR_IO = 3; // 0x3
+ field public static final int CAUSE_ERROR_IPC = 5; // 0x5
+ field public static final int CAUSE_INSTALL_CANCELLED = 2; // 0x2
+ field public static final int CAUSE_INSTALL_COMPLETED = 1; // 0x1
+ field public static final int CAUSE_NOT_SPECIFIED = 0; // 0x0
+ field public static final int STATUS_IN_PROGRESS = 2; // 0x2
+ field public static final int STATUS_IN_USE = 4; // 0x4
+ field public static final int STATUS_NOT_STARTED = 1; // 0x1
+ field public static final int STATUS_READY = 3; // 0x3
+ field public static final int STATUS_UNKNOWN = 0; // 0x0
+ }
+
+ public static interface DynamicAndroidClient.OnStatusChangedListener {
+ method public void onStatusChanged(int, int, long);
+ }
+
public class Intent implements java.lang.Cloneable android.os.Parcelable {
field public static final String ACTION_BATTERY_LEVEL_CHANGED = "android.intent.action.BATTERY_LEVEL_CHANGED";
field public static final String ACTION_CALL_EMERGENCY = "android.intent.action.CALL_EMERGENCY";
@@ -3066,11 +3094,14 @@ package android.net {
}
public class ConnectivityManager {
- method @RequiresPermission("android.permission.PACKET_KEEPALIVE_OFFLOAD") public android.net.SocketKeepalive createNattKeepalive(@NonNull android.net.Network, @NonNull java.io.FileDescriptor, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
+ method @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createNattKeepalive(@NonNull android.net.Network, @NonNull java.io.FileDescriptor, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
+ method @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createSocketKeepalive(@NonNull android.net.Network, @NonNull java.net.Socket, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
method public boolean getAvoidBadWifi();
method @RequiresPermission(android.Manifest.permission.LOCAL_MAC_ADDRESS) public String getCaptivePortalServerUrl();
+ method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementValue(int, boolean, @NonNull android.net.ConnectivityManager.TetheringEntitlementValueListener, @Nullable android.os.Handler);
method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported();
method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, "android.permission.NETWORK_STACK"}) public void setAirplaneMode(boolean);
+ method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(android.net.Network, android.os.Bundle);
method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback);
method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback, android.os.Handler);
method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void stopTethering(int);
@@ -3079,6 +3110,9 @@ package android.net {
field public static final int TETHERING_BLUETOOTH = 2; // 0x2
field public static final int TETHERING_USB = 1; // 0x1
field public static final int TETHERING_WIFI = 0; // 0x0
+ field public static final int TETHER_ERROR_ENTITLEMENT_UNKONWN = 13; // 0xd
+ field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0
+ field public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb
}
public abstract static class ConnectivityManager.OnStartTetheringCallback {
@@ -3087,6 +3121,11 @@ package android.net {
method public void onTetheringStarted();
}
+ public abstract static class ConnectivityManager.TetheringEntitlementValueListener {
+ ctor public ConnectivityManager.TetheringEntitlementValueListener();
+ method public void onEntitlementResult(int);
+ }
+
public final class IpPrefix implements android.os.Parcelable {
ctor public IpPrefix(java.net.InetAddress, int);
ctor public IpPrefix(String);
@@ -3186,6 +3225,10 @@ package android.net {
field public static final String EXTRA_PACKAGE_NAME = "packageName";
}
+ public class NetworkStack {
+ field public static final String PERMISSION_MAINLINE_NETWORK_STACK = "android.permission.MAINLINE_NETWORK_STACK";
+ }
+
public final class RouteInfo implements android.os.Parcelable {
ctor public RouteInfo(android.net.IpPrefix, java.net.InetAddress, String, int);
method public int getType();
@@ -4304,6 +4347,7 @@ package android.os {
}
public final class UserHandle implements android.os.Parcelable {
+ method public static int getAppId(int);
method public int getIdentifier();
method @Deprecated public boolean isOwner();
method public boolean isSystem();
@@ -5448,6 +5492,7 @@ package android.telecom {
field public static final String EXTRA_CALL_BACK_INTENT = "android.telecom.extra.CALL_BACK_INTENT";
field public static final String EXTRA_CLEAR_MISSED_CALLS_INTENT = "android.telecom.extra.CLEAR_MISSED_CALLS_INTENT";
field public static final String EXTRA_CONNECTION_SERVICE = "android.telecom.extra.CONNECTION_SERVICE";
+ field public static final String EXTRA_IS_USER_INTENT_EMERGENCY_CALL = "android.telecom.extra.IS_USER_INTENT_EMERGENCY_CALL";
field public static final int TTY_MODE_FULL = 1; // 0x1
field public static final int TTY_MODE_HCO = 2; // 0x2
field public static final int TTY_MODE_OFF = 0; // 0x0
@@ -5511,6 +5556,7 @@ package android.telephony {
method @NonNull public java.util.List<android.service.carrier.CarrierIdentifier> getExcludedCarriers();
method public int getMultiSimPolicy();
method public boolean isAllCarriersAllowed();
+ method public java.util.List<java.lang.Boolean> isCarrierIdentifiersAllowed(@NonNull java.util.List<android.service.carrier.CarrierIdentifier>);
method public void writeToParcel(android.os.Parcel, int);
field public static final int CARRIER_RESTRICTION_DEFAULT_ALLOWED = 1; // 0x1
field public static final int CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED = 0; // 0x0
@@ -6234,10 +6280,13 @@ package android.telephony {
public class SubscriptionManager {
method public java.util.List<android.telephony.SubscriptionInfo> getAvailableSubscriptionInfoList();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEnabledSubscriptionId(int);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isSubscriptionEnabled(int);
method public void requestEmbeddedSubscriptionInfoListRefresh();
method public void requestEmbeddedSubscriptionInfoListRefresh(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultDataSubId(int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDefaultSmsSubId(int);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setSubscriptionEnabled(int, boolean);
field public static final android.net.Uri ADVANCED_CALLING_ENABLED_CONTENT_URI;
field public static final int PROFILE_CLASS_DEFAULT = -1; // 0xffffffff
field public static final int PROFILE_CLASS_OPERATIONAL = 2; // 0x2
@@ -6304,12 +6353,13 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean getEmergencyCallbackMode();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimDomain();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getIsimIst();
- method @RequiresPermission("android.permission.MODIFY_PHONE_STATE") public int getPreferredNetworkType(int);
+ method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<android.util.Pair<java.lang.Integer,java.lang.Integer>> getLogicalToPhysicalSlotMapping();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getPreferredNetworkTypeBitmap();
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public int getRadioPowerState();
method public int getSimApplicationState();
method public int getSimCardState();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getSimLocale();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getSupportedRadioAccessFamily();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getSupportedRadioAccessFamily();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public java.util.List<android.telephony.TelephonyHistogram> getTelephonyHistograms();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.UiccSlotInfo[] getUiccSlotsInfo();
method @Nullable public android.os.Bundle getVisualVoicemailSettings();
@@ -6322,12 +6372,13 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isMultisimCarrierRestricted();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isOffhook();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRadioOn();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isRebootRequiredForModemConfigChange();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isRinging();
method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isVideoCallingEnabled();
method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean isVisualVoicemailEnabled(android.telecom.PhoneAccountHandle);
method public boolean needsOtaServiceProvisioning();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean rebootRadio();
- method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.MODIFY_PHONE_STATE}) public void requestCellInfoUpdate(@NonNull android.os.WorkSource, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
+ method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.MODIFY_PHONE_STATE}) public void requestCellInfoUpdate(@NonNull android.os.WorkSource, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void requestNumberVerification(@NonNull android.telephony.PhoneNumberRange, long, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.NumberVerificationCallback);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean resetRadioConfig();
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>);
@@ -6337,6 +6388,7 @@ package android.telephony {
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setMultisimCarrierRestriction(boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setPreferredNetworkTypeBitmap(long);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setRadio(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setRadioPower(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSimPowerState(int);
@@ -6350,6 +6402,7 @@ package android.telephony {
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean switchSlots(int[]);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void toggleRadioOnOff();
method public void updateServiceLocation();
+ field @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public static final String ACTION_DEBUG_EVENT = "android.telephony.action.DEBUG_EVENT";
field public static final String ACTION_SIM_APPLICATION_STATE_CHANGED = "android.telephony.action.SIM_APPLICATION_STATE_CHANGED";
field public static final String ACTION_SIM_CARD_STATE_CHANGED = "android.telephony.action.SIM_CARD_STATE_CHANGED";
field public static final String ACTION_SIM_SLOT_STATUS_CHANGED = "android.telephony.action.SIM_SLOT_STATUS_CHANGED";
@@ -6357,52 +6410,32 @@ package android.telephony {
field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
field public static final int CARRIER_PRIVILEGE_STATUS_NO_ACCESS = 0; // 0x0
field public static final int CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED = -1; // 0xffffffff
+ field public static final String EXTRA_DEBUG_EVENT_DESCRIPTION = "android.telephony.extra.DEBUG_EVENT_DESCRIPTION";
+ field public static final String EXTRA_DEBUG_EVENT_ID = "android.telephony.extra.DEBUG_EVENT_ID";
field public static final String EXTRA_SIM_STATE = "android.telephony.extra.SIM_STATE";
field public static final String EXTRA_VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL = "android.telephony.extra.VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL";
field public static final String EXTRA_VOICEMAIL_SCRAMBLED_PIN_STRING = "android.telephony.extra.VOICEMAIL_SCRAMBLED_PIN_STRING";
field public static final long MAX_NUMBER_VERIFICATION_TIMEOUT_MILLIS = 60000L; // 0xea60L
- field public static final int NETWORK_MODE_CDMA_EVDO = 4; // 0x4
- field public static final int NETWORK_MODE_CDMA_NO_EVDO = 5; // 0x5
- field public static final int NETWORK_MODE_EVDO_NO_CDMA = 6; // 0x6
- field public static final int NETWORK_MODE_GLOBAL = 7; // 0x7
- field public static final int NETWORK_MODE_GSM_ONLY = 1; // 0x1
- field public static final int NETWORK_MODE_GSM_UMTS = 3; // 0x3
- field public static final int NETWORK_MODE_LTE_CDMA_EVDO = 8; // 0x8
- field public static final int NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA = 10; // 0xa
- field public static final int NETWORK_MODE_LTE_GSM_WCDMA = 9; // 0x9
- field public static final int NETWORK_MODE_LTE_ONLY = 11; // 0xb
- field public static final int NETWORK_MODE_LTE_TDSCDMA = 15; // 0xf
- field public static final int NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = 22; // 0x16
- field public static final int NETWORK_MODE_LTE_TDSCDMA_GSM = 17; // 0x11
- field public static final int NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA = 20; // 0x14
- field public static final int NETWORK_MODE_LTE_TDSCDMA_WCDMA = 19; // 0x13
- field public static final int NETWORK_MODE_LTE_WCDMA = 12; // 0xc
- field public static final int NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = 21; // 0x15
- field public static final int NETWORK_MODE_TDSCDMA_GSM = 16; // 0x10
- field public static final int NETWORK_MODE_TDSCDMA_GSM_WCDMA = 18; // 0x12
- field public static final int NETWORK_MODE_TDSCDMA_ONLY = 13; // 0xd
- field public static final int NETWORK_MODE_TDSCDMA_WCDMA = 14; // 0xe
- field public static final int NETWORK_MODE_WCDMA_ONLY = 2; // 0x2
- field public static final int NETWORK_MODE_WCDMA_PREF = 0; // 0x0
- field public static final int NETWORK_TYPE_BITMASK_1xRTT = 128; // 0x80
- field public static final int NETWORK_TYPE_BITMASK_CDMA = 16; // 0x10
- field public static final int NETWORK_TYPE_BITMASK_EDGE = 4; // 0x4
- field public static final int NETWORK_TYPE_BITMASK_EHRPD = 16384; // 0x4000
- field public static final int NETWORK_TYPE_BITMASK_EVDO_0 = 32; // 0x20
- field public static final int NETWORK_TYPE_BITMASK_EVDO_A = 64; // 0x40
- field public static final int NETWORK_TYPE_BITMASK_EVDO_B = 4096; // 0x1000
- field public static final int NETWORK_TYPE_BITMASK_GPRS = 2; // 0x2
- field public static final int NETWORK_TYPE_BITMASK_GSM = 65536; // 0x10000
- field public static final int NETWORK_TYPE_BITMASK_HSDPA = 256; // 0x100
- field public static final int NETWORK_TYPE_BITMASK_HSPA = 1024; // 0x400
- field public static final int NETWORK_TYPE_BITMASK_HSPAP = 32768; // 0x8000
- field public static final int NETWORK_TYPE_BITMASK_HSUPA = 512; // 0x200
- field public static final int NETWORK_TYPE_BITMASK_LTE = 8192; // 0x2000
- field public static final int NETWORK_TYPE_BITMASK_LTE_CA = 524288; // 0x80000
- field public static final int NETWORK_TYPE_BITMASK_NR = 1048576; // 0x100000
- field public static final int NETWORK_TYPE_BITMASK_TD_SCDMA = 131072; // 0x20000
- field public static final int NETWORK_TYPE_BITMASK_UMTS = 8; // 0x8
- field public static final int NETWORK_TYPE_BITMASK_UNKNOWN = 1; // 0x1
+ field public static final long NETWORK_TYPE_BITMASK_1xRTT = 64L; // 0x40L
+ field public static final long NETWORK_TYPE_BITMASK_CDMA = 8L; // 0x8L
+ field public static final long NETWORK_TYPE_BITMASK_EDGE = 2L; // 0x2L
+ field public static final long NETWORK_TYPE_BITMASK_EHRPD = 8192L; // 0x2000L
+ field public static final long NETWORK_TYPE_BITMASK_EVDO_0 = 16L; // 0x10L
+ field public static final long NETWORK_TYPE_BITMASK_EVDO_A = 32L; // 0x20L
+ field public static final long NETWORK_TYPE_BITMASK_EVDO_B = 2048L; // 0x800L
+ field public static final long NETWORK_TYPE_BITMASK_GPRS = 1L; // 0x1L
+ field public static final long NETWORK_TYPE_BITMASK_GSM = 32768L; // 0x8000L
+ field public static final long NETWORK_TYPE_BITMASK_HSDPA = 128L; // 0x80L
+ field public static final long NETWORK_TYPE_BITMASK_HSPA = 512L; // 0x200L
+ field public static final long NETWORK_TYPE_BITMASK_HSPAP = 16384L; // 0x4000L
+ field public static final long NETWORK_TYPE_BITMASK_HSUPA = 256L; // 0x100L
+ field public static final long NETWORK_TYPE_BITMASK_IWLAN = 131072L; // 0x20000L
+ field public static final long NETWORK_TYPE_BITMASK_LTE = 4096L; // 0x1000L
+ field public static final long NETWORK_TYPE_BITMASK_LTE_CA = 262144L; // 0x40000L
+ field public static final long NETWORK_TYPE_BITMASK_NR = 524288L; // 0x80000L
+ field public static final long NETWORK_TYPE_BITMASK_TD_SCDMA = 65536L; // 0x10000L
+ field public static final long NETWORK_TYPE_BITMASK_UMTS = 4L; // 0x4L
+ field public static final long NETWORK_TYPE_BITMASK_UNKNOWN = 0L; // 0x0L
field public static final int RADIO_POWER_OFF = 0; // 0x0
field public static final int RADIO_POWER_ON = 1; // 0x1
field public static final int RADIO_POWER_UNAVAILABLE = 2; // 0x2
@@ -6721,6 +6754,7 @@ package android.telephony.ims {
method public int getServiceType();
method public static int getVideoStateFromCallType(int);
method public static int getVideoStateFromImsCallProfile(android.telephony.ims.ImsCallProfile);
+ method public boolean hasKnownUserIntentEmergency();
method public boolean isEmergencyCallTesting();
method public boolean isVideoCall();
method public boolean isVideoPaused();
@@ -6733,6 +6767,7 @@ package android.telephony.ims {
method public void setEmergencyCallTesting(boolean);
method public void setEmergencyServiceCategories(int);
method public void setEmergencyUrns(java.util.List<java.lang.String>);
+ method public void setHasKnownUserIntentEmergency(boolean);
method public void updateCallExtras(android.telephony.ims.ImsCallProfile);
method public void updateCallType(android.telephony.ims.ImsCallProfile);
method public void updateMediaProfile(android.telephony.ims.ImsCallProfile);
@@ -6844,6 +6879,16 @@ package android.telephony.ims {
field public final java.util.HashMap<java.lang.String,android.os.Bundle> mParticipants;
}
+ public class ImsException extends java.lang.Exception {
+ ctor public ImsException(@Nullable String);
+ ctor public ImsException(@Nullable String, int);
+ ctor public ImsException(@Nullable String, int, Throwable);
+ method public int getCode();
+ field public static final int CODE_ERROR_SERVICE_UNAVAILABLE = 1; // 0x1
+ field public static final int CODE_ERROR_UNSPECIFIED = 0; // 0x0
+ field public static final int CODE_ERROR_UNSUPPORTED_OPERATION = 2; // 0x2
+ }
+
public final class ImsExternalCallState implements android.os.Parcelable {
ctor public ImsExternalCallState(String, android.net.Uri, android.net.Uri, boolean, int, int, boolean);
method public int describeContents();
@@ -6861,7 +6906,7 @@ package android.telephony.ims {
}
public class ImsMmTelManager {
- method public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(android.content.Context, int);
+ method public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getVoWiFiModeSetting();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getVoWiFiRoamingModeSetting();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAdvancedCallingSettingEnabled();
@@ -6870,8 +6915,8 @@ package android.telephony.ims {
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isVoWiFiRoamingSettingEnabled();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isVoWiFiSettingEnabled();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isVtSettingEnabled();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback(java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.RegistrationCallback);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerMmTelCapabilityCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.CapabilityCallback);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback(java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.RegistrationCallback) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerMmTelCapabilityCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.CapabilityCallback) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setAdvancedCallingSetting(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setRttCapabilitySetting(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiModeSetting(int);
@@ -7306,11 +7351,11 @@ package android.telephony.ims {
}
public class ProvisioningManager {
- method public static android.telephony.ims.ProvisioningManager createForSubscriptionId(android.content.Context, int);
+ method public static android.telephony.ims.ProvisioningManager createForSubscriptionId(int);
method @WorkerThread @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getProvisioningIntValue(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean getProvisioningStatusForCapability(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int);
method @WorkerThread @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getProvisioningStringValue(int);
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerProvisioningChangedCallback(java.util.concurrent.Executor, @NonNull android.telephony.ims.ProvisioningManager.Callback);
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerProvisioningChangedCallback(java.util.concurrent.Executor, @NonNull android.telephony.ims.ProvisioningManager.Callback) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningIntValue(int, int);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setProvisioningStatusForCapability(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int, boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, String);
diff --git a/api/test-current.txt b/api/test-current.txt
index 1384f0b7b41e..c9d176979a65 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -608,6 +608,7 @@ package android.net {
}
public class ConnectivityManager {
+ method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(android.net.Network, android.os.Bundle);
field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC";
field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT";
}
@@ -667,6 +668,11 @@ package android.net {
method public int[] getCapabilities();
method public int[] getTransportTypes();
method public boolean satisfiedByNetworkCapabilities(android.net.NetworkCapabilities);
+ field public static final int TRANSPORT_TEST = 7; // 0x7
+ }
+
+ public class NetworkStack {
+ field public static final String PERMISSION_MAINLINE_NETWORK_STACK = "android.permission.MAINLINE_NETWORK_STACK";
}
public final class RouteInfo implements android.os.Parcelable {
@@ -1473,7 +1479,6 @@ package android.telephony {
public class TelephonyManager {
method public int getCarrierIdListVersion();
- method public boolean isRttSupported();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void refreshUiccProfile();
method public void setCarrierTestOverride(String, String, String, String, String, String, String);
field public static final int UNKNOWN_CARRIER_ID_LIST_VERSION = -1; // 0xffffffff
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index c04e61b77274..ca4a184407b4 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -174,6 +174,8 @@ public class Am extends BaseCommand {
instrument.noWindowAnimation = true;
} else if (opt.equals("--no-hidden-api-checks")) {
instrument.disableHiddenApiChecks = true;
+ } else if (opt.equals("--no-isolated-storage")) {
+ // NOTE: currently a no-op in this branch
} else if (opt.equals("--user")) {
instrument.userId = parseUserArg(nextArgRequired());
} else if (opt.equals("--abi")) {
diff --git a/cmds/bootanimation/bootanim.rc b/cmds/bootanimation/bootanim.rc
index 1b3c32b20503..469c9646a4aa 100644
--- a/cmds/bootanimation/bootanim.rc
+++ b/cmds/bootanimation/bootanim.rc
@@ -2,7 +2,6 @@ service bootanim /system/bin/bootanimation
class core animation
user graphics
group graphics audio
- updatable
disabled
oneshot
writepid /dev/stune/top-app/tasks
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 5fd148e5d5af..1d629da2f250 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -161,6 +161,8 @@ message Atom {
BluetoothBondStateChanged bluetooth_bond_state_changed = 165;
BluetoothClassicPairingEventReported bluetooth_classic_pairing_event_reported = 166;
BluetoothSmpPairingEventReported bluetooth_smp_pairing_event_reported = 167;
+ ProcessStartTime process_start_time = 169;
+ BluetoothSocketConnectionStateChanged bluetooth_socket_connection_state_changed = 171;
}
// Pulled events will start at field 10000.
@@ -1719,6 +1721,44 @@ message BluetoothSmpPairingEventReported {
}
/**
+ * Logs when a Bluetooth socket’s connection state changed
+ *
+ * Logged from:
+ * system/bt
+ */
+message BluetoothSocketConnectionStateChanged {
+ // An identifier that can be used to match events for this device.
+ // Currently, this is a salted hash of the MAC address of this Bluetooth device.
+ // Salt: Randomly generated 256 bit value
+ // Hash algorithm: HMAC-SHA256
+ // Size: 32 byte
+ // Default: null or empty if this is a server listener socket
+ optional bytes obfuscated_id = 1 [(android.os.statsd.log_mode) = MODE_BYTES];
+ // Temporary port of this socket for the current connection or session only
+ // Default 0 when unknown or don't care
+ optional int32 port = 2;
+ // Socket type as mentioned in
+ // frameworks/base/core/java/android/bluetooth/BluetoothSocket.java
+ // Default: SOCKET_TYPE_UNKNOWN
+ optional android.bluetooth.SocketTypeEnum type = 3;
+ // Socket connection state
+ // Default: SOCKET_CONNECTION_STATE_UNKNOWN
+ optional android.bluetooth.SocketConnectionstateEnum state = 4;
+ // Number of bytes sent to remote device during this connection
+ optional int64 tx_bytes = 5;
+ // Number of bytes received from remote device during this connection
+ optional int64 rx_bytes = 6;
+ // Socket owner's UID
+ optional int32 uid = 7 [(is_uid) = true];
+ // Server port of this socket, if any. When both |server_port| and |port| fields are populated,
+ // |port| must be spawned by |server_port|
+ // Default 0 when unknown or don't care
+ optional int32 server_port = 8;
+ // Whether this is a server listener socket
+ optional android.bluetooth.SocketRoleEnum is_server = 9;
+}
+
+/**
* Logs when something is plugged into or removed from the USB-C connector.
*
* Logged from:
@@ -2929,3 +2969,59 @@ message SeOmapiReported {
optional string package_name = 3;
}
+
+/*
+* Logs number of milliseconds it takes to start a process.
+* The definition of app process start time is from the app launch time to
+* the time that Zygote finished forking the app process and loaded the
+* application package's java classes.
+
+* This metric is different from AppStartOccurred which is for foreground
+* activity only.
+
+* ProcessStartTime can report all processes (both foreground and background)
+* start time.
+*
+* Logged from:
+* frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
+*/
+message ProcessStartTime {
+ // The uid of the ProcessRecord.
+ optional int32 uid = 1 [(is_uid) = true];
+
+ // The process pid.
+ optional int32 pid = 2;
+
+ // The process name.
+ // Usually package name, "system" for system server.
+ // Provided by ActivityManagerService.
+ optional string process_name = 3;
+
+ enum StartType {
+ UNKNOWN = 0;
+ WARM = 1;
+ HOT = 2;
+ COLD = 3;
+ }
+
+ // The start type.
+ optional StartType type = 4;
+
+ // The elapsed realtime at the start of the process.
+ optional int64 process_start_time_millis = 5;
+
+ // Number of milliseconds it takes to reach bind application.
+ optional int32 bind_application_delay_millis = 6;
+
+ // Number of milliseconds it takes to finish start of the process.
+ optional int32 process_start_delay_millis = 7;
+
+ // hostingType field in ProcessRecord, the component type such as "activity",
+ // "service", "content provider", "broadcast" or other strings.
+ optional string hosting_type = 8;
+
+ // hostingNameStr field in ProcessRecord. The component class name that runs
+ // in this process.
+ optional string hosting_name = 9;
+}
+
diff --git a/config/hiddenapi-force-blacklist.txt b/config/hiddenapi-force-blacklist.txt
index dca3b525c06a..b328f2ac1955 100644
--- a/config/hiddenapi-force-blacklist.txt
+++ b/config/hiddenapi-force-blacklist.txt
@@ -1,4 +1,6 @@
Ldalvik/system/VMRuntime;->setHiddenApiExemptions([Ljava/lang/String;)V
+Ldalvik/system/VMRuntime;->setTargetSdkVersion(I)V
+Ldalvik/system/VMRuntime;->setTargetSdkVersionNative(I)V
Ljava/lang/invoke/MethodHandles$Lookup;->IMPL_LOOKUP:Ljava/lang/invoke/MethodHandles$Lookup;
Ljava/lang/invoke/VarHandle;->acquireFence()V
Ljava/lang/invoke/VarHandle;->compareAndExchange([Ljava/lang/Object;)Ljava/lang/Object;
diff --git a/config/hiddenapi-greylist-max-o.txt b/config/hiddenapi-greylist-max-o.txt
index 4b6cc0ef7795..d9c1cd0313fc 100644
--- a/config/hiddenapi-greylist-max-o.txt
+++ b/config/hiddenapi-greylist-max-o.txt
@@ -109777,7 +109777,6 @@ Ldalvik/system/VMRuntime;->setHiddenApiAccessLogSamplingRate(I)V
Ldalvik/system/VMRuntime;->setNonSdkApiUsageConsumer(Ljava/util/function/Consumer;)V
Ldalvik/system/VMRuntime;->setProcessPackageName(Ljava/lang/String;)V
Ldalvik/system/VMRuntime;->setSystemDaemonThreadPriority()V
-Ldalvik/system/VMRuntime;->setTargetSdkVersionNative(I)V
Ldalvik/system/VMRuntime;->startHeapTaskProcessor()V
Ldalvik/system/VMRuntime;->startJitCompilation()V
Ldalvik/system/VMRuntime;->stopHeapTaskProcessor()V
diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt
index 3d5b32a62ca5..e166c31d578a 100644
--- a/config/hiddenapi-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -603,8 +603,6 @@ Landroid/net/IConnectivityManager;->getTetherableWifiRegexs()[Ljava/lang/String;
Landroid/net/IConnectivityManager;->getTetheredIfaces()[Ljava/lang/String;
Landroid/net/IConnectivityManager;->getTetheringErroredIfaces()[Ljava/lang/String;
Landroid/net/IConnectivityManager;->startLegacyVpn(Lcom/android/internal/net/VpnProfile;)V
-Landroid/net/INetd$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetd;
-Landroid/net/INetd;->interfaceAddAddress(Ljava/lang/String;Ljava/lang/String;I)V
Landroid/net/INetworkManagementEventObserver$Stub;-><init>()V
Landroid/net/INetworkPolicyListener$Stub;-><init>()V
Landroid/net/INetworkPolicyManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkPolicyManager;
@@ -2546,7 +2544,6 @@ Lcom/android/internal/telephony/cat/CatService;->isStkAppInstalled()Z
Lcom/android/internal/telephony/cat/CatService;->mCmdIf:Lcom/android/internal/telephony/CommandsInterface;
Lcom/android/internal/telephony/cat/CatService;->mContext:Landroid/content/Context;
Lcom/android/internal/telephony/cat/CatService;->mCurrntCmd:Lcom/android/internal/telephony/cat/CatCmdMessage;
-Lcom/android/internal/telephony/cat/CatService;->mHandlerThread:Landroid/os/HandlerThread;
Lcom/android/internal/telephony/cat/CatService;->mMenuCmd:Lcom/android/internal/telephony/cat/CatCmdMessage;
Lcom/android/internal/telephony/cat/CatService;->mMsgDecoder:Lcom/android/internal/telephony/cat/RilMessageDecoder;
Lcom/android/internal/telephony/cat/CatService;->mSlotId:I
@@ -2933,7 +2930,6 @@ Lcom/android/internal/telephony/gsm/UsimPhoneBookManager;->reset()V
Lcom/android/internal/telephony/GsmAlphabet$TextEncodingDetails;-><init>()V
Lcom/android/internal/telephony/GsmCdmaCall;->attachFake(Lcom/android/internal/telephony/Connection;Lcom/android/internal/telephony/Call$State;)V
Lcom/android/internal/telephony/GsmCdmaCallTracker;->clearDisconnected()V
-Lcom/android/internal/telephony/GsmCdmaCallTracker;->dialThreeWay(Ljava/lang/String;)Lcom/android/internal/telephony/Connection;
Lcom/android/internal/telephony/GsmCdmaCallTracker;->disableDataCallInEmergencyCall(Ljava/lang/String;)V
Lcom/android/internal/telephony/GsmCdmaCallTracker;->fakeHoldForegroundBeforeDial()V
Lcom/android/internal/telephony/GsmCdmaCallTracker;->getPhone()Lcom/android/internal/telephony/GsmCdmaPhone;
diff --git a/core/java/android/accessibilityservice/OWNERS b/core/java/android/accessibilityservice/OWNERS
new file mode 100644
index 000000000000..265674a74b7e
--- /dev/null
+++ b/core/java/android/accessibilityservice/OWNERS
@@ -0,0 +1,3 @@
+svetoslavganov@google.com
+pweaver@google.com
+rhedjao@google.com
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 0d3110cfded8..ac33c169288b 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -850,8 +850,9 @@ public final class LoadedApk {
}
}
- // /vendor/lib, /odm/lib and /product/lib are added to the native lib search
- // paths of the classloader. Note that this is done AFTER the classloader is
+ // /aepx/com.android.runtime/lib, /vendor/lib, /odm/lib and /product/lib
+ // are added to the native lib search paths of the classloader.
+ // Note that this is done AFTER the classloader is
// created by ApplicationLoaders.getDefault().getClassLoader(...). The
// reason is because if we have added the paths when creating the classloader
// above, the paths are also added to the search path of the linker namespace
@@ -868,8 +869,11 @@ public final class LoadedApk {
// System.loadLibrary(). In order to prevent the problem, we explicitly
// add the paths only to the classloader, and not to the native loader
// (linker namespace).
- List<String> extraLibPaths = new ArrayList<>(3);
+ List<String> extraLibPaths = new ArrayList<>(4);
String abiSuffix = VMRuntime.getRuntime().is64Bit() ? "64" : "";
+ if (!defaultSearchPaths.contains("/apex/com.android.runtime/lib")) {
+ extraLibPaths.add("/apex/com.android.runtime/lib" + abiSuffix);
+ }
if (!defaultSearchPaths.contains("/vendor/lib")) {
extraLibPaths.add("/vendor/lib" + abiSuffix);
}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index b2951df63ebd..e92efde236c4 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -26,7 +26,6 @@ import android.app.job.JobScheduler;
import android.app.slice.SliceManager;
import android.app.timedetector.TimeDetector;
import android.app.timezone.RulesManager;
-import android.app.timezonedetector.TimeZoneDetector;
import android.app.trust.TrustManager;
import android.app.usage.IStorageStatsManager;
import android.app.usage.IUsageStatsManager;
@@ -84,13 +83,11 @@ import android.net.IConnectivityManager;
import android.net.IEthernetManager;
import android.net.IIpMemoryStore;
import android.net.IIpSecService;
-import android.net.INetd;
import android.net.INetworkPolicyManager;
import android.net.IpMemoryStore;
import android.net.IpSecManager;
import android.net.NetworkPolicyManager;
import android.net.NetworkScoreManager;
-import android.net.NetworkStack;
import android.net.NetworkWatchlistManager;
import android.net.lowpan.ILowpanManager;
import android.net.lowpan.LowpanManager;
@@ -114,11 +111,13 @@ import android.os.BugreportManager;
import android.os.Build;
import android.os.DeviceIdleManager;
import android.os.DropBoxManager;
+import android.os.DynamicAndroidManager;
import android.os.HardwarePropertiesManager;
import android.os.IBatteryPropertiesRegistrar;
import android.os.IBinder;
import android.os.IDeviceIdleController;
import android.os.IDumpstate;
+import android.os.IDynamicAndroidService;
import android.os.IHardwarePropertiesManager;
import android.os.IPowerManager;
import android.os.IRecoverySystem;
@@ -289,21 +288,13 @@ final class SystemServiceRegistry {
return new ConnectivityManager(context, service);
}});
- registerService(Context.NETD_SERVICE, INetd.class, new StaticServiceFetcher<INetd>() {
+ registerService(Context.NETD_SERVICE, IBinder.class, new StaticServiceFetcher<IBinder>() {
@Override
- public INetd createService() throws ServiceNotFoundException {
- return INetd.Stub.asInterface(
- ServiceManager.getServiceOrThrow(Context.NETD_SERVICE));
+ public IBinder createService() throws ServiceNotFoundException {
+ return ServiceManager.getServiceOrThrow(Context.NETD_SERVICE);
}
});
- registerService(Context.NETWORK_STACK_SERVICE, NetworkStack.class,
- new StaticServiceFetcher<NetworkStack>() {
- @Override
- public NetworkStack createService() {
- return new NetworkStack();
- }});
-
registerService(Context.IP_MEMORY_STORE_SERVICE, IpMemoryStore.class,
new CachedServiceFetcher<IpMemoryStore>() {
@Override
@@ -1058,12 +1049,15 @@ final class SystemServiceRegistry {
throws ServiceNotFoundException {
return new TimeDetector();
}});
- registerService(Context.TIME_ZONE_DETECTOR_SERVICE, TimeZoneDetector.class,
- new CachedServiceFetcher<TimeZoneDetector>() {
+ registerService(Context.DYNAMIC_ANDROID_SERVICE, DynamicAndroidManager.class,
+ new CachedServiceFetcher<DynamicAndroidManager>() {
@Override
- public TimeZoneDetector createService(ContextImpl ctx)
+ public DynamicAndroidManager createService(ContextImpl ctx)
throws ServiceNotFoundException {
- return new TimeZoneDetector();
+ IBinder b = ServiceManager.getServiceOrThrow(
+ Context.DYNAMIC_ANDROID_SERVICE);
+ return new DynamicAndroidManager(
+ IDynamicAndroidService.Stub.asInterface(b));
}});
}
diff --git a/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl b/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl
deleted file mode 100644
index ef2cbab137dc..000000000000
--- a/core/java/android/app/timezonedetector/ITimeZoneDetectorService.aidl
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2018 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.app.timezonedetector;
-
-/**
- * System private API to comunicate with time zone detector service.
- *
- * <p>Used by parts of the Android system with signals associated with the device's time zone to
- * provide information to the Time Zone Detector Service.
- *
- * <p>Use the {@link android.app.timezonedetector.TimeZoneDetector} class rather than going through
- * this Binder interface directly. See {@link android.app.timezonedetector.TimeZoneDetectorService}
- * for more complete documentation.
- *
- *
- * {@hide}
- */
-interface ITimeZoneDetectorService {
- void stubbedCall();
-}
diff --git a/core/java/android/app/timezonedetector/TimeZoneDetector.java b/core/java/android/app/timezonedetector/TimeZoneDetector.java
deleted file mode 100644
index be3c7649a486..000000000000
--- a/core/java/android/app/timezonedetector/TimeZoneDetector.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2018 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.app.timezonedetector;
-
-import android.annotation.SystemService;
-import android.content.Context;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.ServiceManager.ServiceNotFoundException;
-import android.util.Log;
-
-/**
- * The interface through which system components can send signals to the TimeZoneDetectorService.
- * @hide
- */
-@SystemService(Context.TIME_ZONE_DETECTOR_SERVICE)
-public final class TimeZoneDetector {
-
- private static final String TAG = "timezonedetector.TimeZoneDetector";
- private static final boolean DEBUG = false;
-
- private final ITimeZoneDetectorService mITimeZoneDetectorService;
-
- public TimeZoneDetector() throws ServiceNotFoundException {
- mITimeZoneDetectorService = ITimeZoneDetectorService.Stub.asInterface(
- ServiceManager.getServiceOrThrow(Context.TIME_ZONE_DETECTOR_SERVICE));
-
- }
- /**
- * Does nothing.
- * TODO: Remove this when the service implementation is built out.
- */
- public void stubbedCall() {
- if (DEBUG) {
- Log.d(TAG, "stubbedCall called");
- }
- try {
- mITimeZoneDetectorService.stubbedCall();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-}
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 2803856fb3bc..23f29041487d 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -2175,8 +2175,7 @@ public final class BluetoothDevice implements Parcelable {
* encrypted.
* <p> Use this socket if an authenticated socket link is possible. Authentication refers
* to the authentication of the link key to prevent man-in-the-middle type of attacks. When a
- * secure socket connection is not possible, use {#link createInsecureLeL2capCocSocket(int,
- * int)}.
+ * secure socket connection is not possible, use {#link createInsecureL2capChannel(int)}.
*
* @param psm dynamic PSM value from remote device
* @return a CoC #BluetoothSocket ready for an outgoing connection
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 8959540ff7c3..b072740ccfdf 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -49,7 +49,6 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
-import android.net.NetworkStack;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@@ -3087,7 +3086,6 @@ public abstract class Context {
CROSS_PROFILE_APPS_SERVICE,
//@hide: SYSTEM_UPDATE_SERVICE,
//@hide: TIME_DETECTOR_SERVICE,
- //@hide: TIME_ZONE_DETECTOR_SERVICE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ServiceName {}
@@ -3515,11 +3513,10 @@ public abstract class Context {
public static final String NETD_SERVICE = "netd";
/**
- * Use with {@link #getSystemService(String)} to retrieve a
- * {@link NetworkStack} for communicating with the network stack
+ * Use with {@link android.os.ServiceManager.getService()} to retrieve a
+ * {@link NetworkStackClient} IBinder for communicating with the network stack
* @hide
- * @see #getSystemService(String)
- * @see NetworkStack
+ * @see NetworkStackClient
*/
public static final String NETWORK_STACK_SERVICE = "network_stack";
@@ -4290,19 +4287,18 @@ public abstract class Context {
/**
* Use with {@link #getSystemService(String)} to retrieve an
- * {@link android.app.timezonedetector.ITimeZoneDetectorService}.
+ * {@link android.telephony.ims.RcsManager}.
* @hide
- *
- * @see #getSystemService(String)
*/
- public static final String TIME_ZONE_DETECTOR_SERVICE = "time_zone_detector";
+ public static final String TELEPHONY_RCS_SERVICE = "ircs";
- /**
+ /**
* Use with {@link #getSystemService(String)} to retrieve an
- * {@link android.telephony.ims.RcsManager}.
+ * {@link android.os.DynamicAndroidManager}.
* @hide
*/
- public static final String TELEPHONY_RCS_SERVICE = "ircs";
+ @SystemApi
+ public static final String DYNAMIC_ANDROID_SERVICE = "dynamic_android";
/**
* Determine whether the given permission is allowed for a particular
diff --git a/core/java/android/content/DynamicAndroidClient.java b/core/java/android/content/DynamicAndroidClient.java
new file mode 100644
index 000000000000..571cba429ea9
--- /dev/null
+++ b/core/java/android/content/DynamicAndroidClient.java
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2019 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.content;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+import java.util.concurrent.Executor;
+
+/**
+ * This class contains methods and constants used to start DynamicAndroid
+ * installation, and a listener for progress update.
+ * @hide
+ */
+@SystemApi
+public class DynamicAndroidClient {
+ /** @hide */
+ @IntDef(prefix = { "STATUS_" }, value = {
+ STATUS_UNKNOWN,
+ STATUS_NOT_STARTED,
+ STATUS_IN_PROGRESS,
+ STATUS_READY,
+ STATUS_IN_USE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface InstallationStatus {}
+
+ /** @hide */
+ @IntDef(prefix = { "CAUSE_" }, value = {
+ CAUSE_NOT_SPECIFIED,
+ CAUSE_INSTALL_COMPLETED,
+ CAUSE_INSTALL_CANCELLED,
+ CAUSE_ERROR_IO,
+ CAUSE_ERROR_INVALID_URL,
+ CAUSE_ERROR_IPC,
+ CAUSE_ERROR_EXCEPTION,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface StatusChangedCause {}
+
+ private static final String TAG = "DynAndroidClient";
+
+ private static final long DEFAULT_USERDATA_SIZE = (10L << 30);
+
+
+ /** Listener for installation status update. */
+ public interface OnStatusChangedListener {
+ /**
+ * This callback is called when installation status is changed, and when the
+ * client is {@link #bind} to DynamicAndroid installation service.
+ *
+ * @param status status code, also defined in {@code DynamicAndroidClient}.
+ * @param cause cause code, also defined in {@code DynamicAndroidClient}.
+ * @param progress number of bytes installed.
+ */
+ void onStatusChanged(@InstallationStatus int status, @StatusChangedCause int cause,
+ long progress);
+ }
+
+ /*
+ * Status codes
+ */
+ /** We are bound to installation service, but failed to get its status */
+ public static final int STATUS_UNKNOWN = 0;
+
+ /** Installation is not started yet. */
+ public static final int STATUS_NOT_STARTED = 1;
+
+ /** Installation is in progress. */
+ public static final int STATUS_IN_PROGRESS = 2;
+
+ /** Installation is finished but the user has not launched it. */
+ public static final int STATUS_READY = 3;
+
+ /** Device is running in Dynamic Android. */
+ public static final int STATUS_IN_USE = 4;
+
+ /*
+ * Causes
+ */
+ /** Cause is not specified. This means the status is not changed. */
+ public static final int CAUSE_NOT_SPECIFIED = 0;
+
+ /** Status changed because installation is completed. */
+ public static final int CAUSE_INSTALL_COMPLETED = 1;
+
+ /** Status changed because installation is cancelled. */
+ public static final int CAUSE_INSTALL_CANCELLED = 2;
+
+ /** Installation failed due to IOException. */
+ public static final int CAUSE_ERROR_IO = 3;
+
+ /** Installation failed because the image URL source is not supported. */
+ public static final int CAUSE_ERROR_INVALID_URL = 4;
+
+ /** Installation failed due to IPC error. */
+ public static final int CAUSE_ERROR_IPC = 5;
+
+ /** Installation failed due to unhandled exception. */
+ public static final int CAUSE_ERROR_EXCEPTION = 6;
+
+ /*
+ * IPC Messages
+ */
+ /**
+ * Message to register listener.
+ * @hide
+ */
+ public static final int MSG_REGISTER_LISTENER = 1;
+
+ /**
+ * Message to unregister listener.
+ * @hide
+ */
+ public static final int MSG_UNREGISTER_LISTENER = 2;
+
+ /**
+ * Message for status update.
+ * @hide
+ */
+ public static final int MSG_POST_STATUS = 3;
+
+ /*
+ * Messages keys
+ */
+ /**
+ * Message key, for progress update.
+ * @hide
+ */
+ public static final String KEY_INSTALLED_SIZE = "KEY_INSTALLED_SIZE";
+
+ /*
+ * Intent Actions
+ */
+ /**
+ * Intent action: start installation.
+ * @hide
+ */
+ public static final String ACTION_START_INSTALL =
+ "android.content.action.START_INSTALL";
+
+ /**
+ * Intent action: notify user if we are currently running in Dynamic Android.
+ * @hide
+ */
+ public static final String ACTION_NOTIFY_IF_IN_USE =
+ "android.content.action.NOTIFY_IF_IN_USE";
+
+ /*
+ * Intent Keys
+ */
+ /**
+ * Intent key: URL to system image.
+ * @hide
+ */
+ public static final String KEY_SYSTEM_URL = "KEY_SYSTEM_URL";
+
+ /**
+ * Intent key: Size of system image, in bytes.
+ * @hide
+ */
+ public static final String KEY_SYSTEM_SIZE = "KEY_SYSTEM_SIZE";
+
+ /**
+ * Intent key: Number of bytes to reserve for userdata.
+ * @hide
+ */
+ public static final String KEY_USERDATA_SIZE = "KEY_USERDATA_SIZE";
+
+
+ private static class IncomingHandler extends Handler {
+ private final WeakReference<DynamicAndroidClient> mWeakClient;
+
+ IncomingHandler(DynamicAndroidClient service) {
+ super(Looper.getMainLooper());
+ mWeakClient = new WeakReference<>(service);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ DynamicAndroidClient service = mWeakClient.get();
+
+ if (service != null) {
+ service.handleMessage(msg);
+ }
+ }
+ }
+
+ private class DynAndroidServiceConnection implements ServiceConnection {
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ Slog.v(TAG, "DynAndroidService connected");
+
+ mService = new Messenger(service);
+
+ try {
+ Message msg = Message.obtain(null, MSG_REGISTER_LISTENER);
+ msg.replyTo = mMessenger;
+
+ mService.send(msg);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to get status from installation service");
+ mExecutor.execute(() -> {
+ mListener.onStatusChanged(STATUS_UNKNOWN, CAUSE_ERROR_IPC, 0);
+ });
+ }
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ Slog.v(TAG, "DynAndroidService disconnected");
+ mService = null;
+ }
+ }
+
+ private final Context mContext;
+ private final DynAndroidServiceConnection mConnection;
+ private final Messenger mMessenger;
+
+ private boolean mBound;
+ private Executor mExecutor;
+ private OnStatusChangedListener mListener;
+ private Messenger mService;
+
+ /**
+ * @hide
+ */
+ @SystemApi
+ public DynamicAndroidClient(@NonNull Context context) {
+ mContext = context;
+ mConnection = new DynAndroidServiceConnection();
+ mMessenger = new Messenger(new IncomingHandler(this));
+ }
+
+ /**
+ * This method register a listener for status change. The listener is called using
+ * the executor.
+ */
+ public void setOnStatusChangedListener(
+ @NonNull OnStatusChangedListener listener,
+ @NonNull @CallbackExecutor Executor executor) {
+ mListener = listener;
+ mExecutor = executor;
+ }
+
+ /**
+ * This method register a listener for status change. The listener is called in main
+ * thread.
+ */
+ public void setOnStatusChangedListener(
+ @NonNull OnStatusChangedListener listener) {
+ mListener = listener;
+ mExecutor = null;
+ }
+
+ /**
+ * Bind to DynamicAndroidInstallationService.
+ */
+ public void bind() {
+ Intent intent = new Intent();
+ intent.setClassName("com.android.dynandroid",
+ "com.android.dynandroid.DynamicAndroidInstallationService");
+
+ mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
+
+ mBound = true;
+ }
+
+ /**
+ * Unbind from DynamicAndroidInstallationService.
+ */
+ public void unbind() {
+ if (!mBound) {
+ return;
+ }
+
+ if (mService != null) {
+ try {
+ Message msg = Message.obtain(null, MSG_UNREGISTER_LISTENER);
+ msg.replyTo = mMessenger;
+ mService.send(msg);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to unregister from installation service");
+ }
+ }
+
+ // Detach our existing connection.
+ mContext.unbindService(mConnection);
+
+ mBound = false;
+ }
+
+ /**
+ * Start installing DynamicAndroid from URL with default userdata size.
+ *
+ * @param systemUrl A network URL or a file URL to system image.
+ * @param systemSize size of system image.
+ */
+ public void start(String systemUrl, long systemSize) {
+ start(systemUrl, systemSize, DEFAULT_USERDATA_SIZE);
+ }
+
+ /**
+ * Start installing DynamicAndroid from URL.
+ *
+ * @param systemUrl A network URL or a file URL to system image.
+ * @param systemSize size of system image.
+ * @param userdataSize bytes reserved for userdata.
+ */
+ public void start(String systemUrl, long systemSize, long userdataSize) {
+ Intent intent = new Intent();
+
+ intent.setClassName("com.android.dynandroid",
+ "com.android.dynandroid.VerificationActivity");
+
+ intent.setAction(ACTION_START_INSTALL);
+
+ intent.putExtra(KEY_SYSTEM_URL, systemUrl);
+ intent.putExtra(KEY_SYSTEM_SIZE, systemSize);
+ intent.putExtra(KEY_USERDATA_SIZE, userdataSize);
+
+ mContext.startActivity(intent);
+ }
+
+ private void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_POST_STATUS:
+ int status = msg.arg1;
+ int cause = msg.arg2;
+ // obj is non-null
+ long progress = ((Bundle) msg.obj).getLong(KEY_INSTALLED_SIZE);
+
+ if (mExecutor != null) {
+ mExecutor.execute(() -> {
+ mListener.onStatusChanged(status, cause, progress);
+ });
+ } else {
+ mListener.onStatusChanged(status, cause, progress);
+ }
+ break;
+ default:
+ // do nothing
+
+ }
+ }
+}
diff --git a/core/java/android/ddm/DdmHandleHello.java b/core/java/android/ddm/DdmHandleHello.java
index b2288fc4492d..87568e857d6d 100644
--- a/core/java/android/ddm/DdmHandleHello.java
+++ b/core/java/android/ddm/DdmHandleHello.java
@@ -16,14 +16,16 @@
package android.ddm;
-import org.apache.harmony.dalvik.ddmc.Chunk;
-import org.apache.harmony.dalvik.ddmc.ChunkHandler;
-import org.apache.harmony.dalvik.ddmc.DdmServer;
-import android.util.Log;
import android.os.Debug;
import android.os.UserHandle;
+import android.util.Log;
+
import dalvik.system.VMRuntime;
+import org.apache.harmony.dalvik.ddmc.Chunk;
+import org.apache.harmony.dalvik.ddmc.ChunkHandler;
+import org.apache.harmony.dalvik.ddmc.DdmServer;
+
import java.nio.ByteBuffer;
/**
@@ -35,6 +37,8 @@ public class DdmHandleHello extends ChunkHandler {
public static final int CHUNK_WAIT = type("WAIT");
public static final int CHUNK_FEAT = type("FEAT");
+ private static final int CLIENT_PROTOCOL_VERSION = 1;
+
private static DdmHandleHello mInstance = new DdmHandleHello();
private static final String[] FRAMEWORK_FEATURES = new String[] {
@@ -145,7 +149,7 @@ public class DdmHandleHello extends ChunkHandler {
+ vmFlags.length() * 2
+ 1);
out.order(ChunkHandler.CHUNK_ORDER);
- out.putInt(DdmServer.CLIENT_PROTOCOL_VERSION);
+ out.putInt(CLIENT_PROTOCOL_VERSION);
out.putInt(android.os.Process.myPid());
out.putInt(vmIdent.length());
out.putInt(appName.length());
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index f47ada6ae557..92b30a440d69 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -68,6 +68,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.net.InetAddress;
import java.net.InetSocketAddress;
+import java.net.Socket;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@@ -177,10 +178,10 @@ public class ConnectivityManager {
* The lookup key for a {@link NetworkInfo} object. Retrieve with
* {@link android.content.Intent#getParcelableExtra(String)}.
*
- * @deprecated Since {@link NetworkInfo} can vary based on UID, applications
- * should always obtain network information through
- * {@link #getActiveNetworkInfo()}.
- * @see #EXTRA_NETWORK_TYPE
+ * @deprecated The {@link NetworkInfo} object is deprecated, as many of its properties
+ * can't accurately represent modern network characteristics.
+ * Please obtain information about networks from the {@link NetworkCapabilities}
+ * or {@link LinkProperties} objects instead.
*/
@Deprecated
public static final String EXTRA_NETWORK_INFO = "networkInfo";
@@ -189,7 +190,11 @@ public class ConnectivityManager {
* Network type which triggered a {@link #CONNECTIVITY_ACTION} broadcast.
*
* @see android.content.Intent#getIntExtra(String, int)
+ * @deprecated The network type is not rich enough to represent the characteristics
+ * of modern networks. Please use {@link NetworkCapabilities} instead,
+ * in particular the transports.
*/
+ @Deprecated
public static final String EXTRA_NETWORK_TYPE = "networkType";
/**
@@ -673,11 +678,20 @@ public class ConnectivityManager {
@Deprecated
public static final int TYPE_VPN = 17;
+ /**
+ * A network that is exclusively meant to be used for testing
+ *
+ * @deprecated Use {@link NetworkCapabilities} instead.
+ * @hide
+ */
+ @Deprecated
+ public static final int TYPE_TEST = 18; // TODO: Remove this once NetworkTypes are unused.
+
/** {@hide} */
- public static final int MAX_RADIO_TYPE = TYPE_VPN;
+ public static final int MAX_RADIO_TYPE = TYPE_TEST;
/** {@hide} */
- public static final int MAX_NETWORK_TYPE = TYPE_VPN;
+ public static final int MAX_NETWORK_TYPE = TYPE_TEST;
private static final int MIN_NETWORK_TYPE = TYPE_MOBILE;
@@ -1243,9 +1257,13 @@ public class ConnectivityManager {
* is no current default network.
*
* {@hide}
+ * @deprecated please use {@link #getLinkProperties(Network)} on the return
+ * value of {@link #getActiveNetwork()} instead. In particular,
+ * this method will return non-null LinkProperties even if the
+ * app is blocked by policy from using this network.
*/
@RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
- @UnsupportedAppUsage
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 109783091)
public LinkProperties getActiveLinkProperties() {
try {
return mService.getActiveLinkProperties();
@@ -1816,7 +1834,7 @@ public class ConnectivityManager {
@Override
public void handleMessage(Message message) {
switch (message.what) {
- case NetworkAgent.EVENT_PACKET_KEEPALIVE:
+ case NetworkAgent.EVENT_SOCKET_KEEPALIVE:
int error = message.arg2;
try {
if (error == SUCCESS) {
@@ -1881,7 +1899,8 @@ public class ConnectivityManager {
* @param callback A {@link SocketKeepalive.Callback}. Used for notifications about keepalive
* changes. Must be extended by applications that use this API.
*
- * @return A {@link SocketKeepalive} object, which can be used to control this keepalive object.
+ * @return A {@link SocketKeepalive} object that can be used to control the keepalive on the
+ * given socket.
**/
public SocketKeepalive createSocketKeepalive(@NonNull Network network,
@NonNull UdpEncapsulationSocket socket,
@@ -1910,6 +1929,8 @@ public class ConnectivityManager {
* @param callback A {@link SocketKeepalive.Callback}. Used for notifications about keepalive
* changes. Must be extended by applications that use this API.
*
+ * @return A {@link SocketKeepalive} object that can be used to control the keepalive on the
+ * given socket.
* @hide
*/
@SystemApi
@@ -1925,6 +1946,34 @@ public class ConnectivityManager {
}
/**
+ * Request that keepalives be started on a TCP socket.
+ * The socket must be established.
+ *
+ * @param network The {@link Network} the socket is on.
+ * @param socket The socket that needs to be kept alive.
+ * @param executor The executor on which callback will be invoked. This implementation assumes
+ * the provided {@link Executor} runs the callbacks in sequence with no
+ * concurrency. Failing this, no guarantee of correctness can be made. It is
+ * the responsibility of the caller to ensure the executor provides this
+ * guarantee. A simple way of creating such an executor is with the standard
+ * tool {@code Executors.newSingleThreadExecutor}.
+ * @param callback A {@link SocketKeepalive.Callback}. Used for notifications about keepalive
+ * changes. Must be extended by applications that use this API.
+ *
+ * @return A {@link SocketKeepalive} object that can be used to control the keepalive on the
+ * given socket.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD)
+ public SocketKeepalive createSocketKeepalive(@NonNull Network network,
+ @NonNull Socket socket,
+ @NonNull Executor executor,
+ @NonNull Callback callback) {
+ return new TcpSocketKeepalive(mService, network, socket, executor, callback);
+ }
+
+ /**
* Ensure that a network route exists to deliver traffic to the specified
* host via the specified network interface. An attempt to add a route that
* already exists is ignored, but treated as successful.
@@ -2581,6 +2630,7 @@ public class ConnectivityManager {
}
/** {@hide} */
+ @SystemApi
public static final int TETHER_ERROR_NO_ERROR = 0;
/** {@hide} */
public static final int TETHER_ERROR_UNKNOWN_IFACE = 1;
@@ -2603,9 +2653,13 @@ public class ConnectivityManager {
/** {@hide} */
public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10;
/** {@hide} */
+ @SystemApi
public static final int TETHER_ERROR_PROVISION_FAILED = 11;
/** {@hide} */
public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12;
+ /** {@hide} */
+ @SystemApi
+ public static final int TETHER_ERROR_ENTITLEMENT_UNKONWN = 13;
/**
* Get a more detailed error code after a Tethering or Untethering
@@ -2628,6 +2682,65 @@ public class ConnectivityManager {
}
/**
+ * Callback for use with {@link #getLatestTetheringEntitlementValue} to find out whether
+ * entitlement succeeded.
+ * @hide
+ */
+ @SystemApi
+ public abstract static class TetheringEntitlementValueListener {
+ /**
+ * Called to notify entitlement result.
+ *
+ * @param resultCode a int value of entitlement result. It may be one of
+ * {@link #TETHER_ERROR_NO_ERROR},
+ * {@link #TETHER_ERROR_PROVISION_FAILED}, or
+ * {@link #TETHER_ERROR_ENTITLEMENT_UNKONWN}.
+ */
+ public void onEntitlementResult(int resultCode) {}
+ }
+
+ /**
+ * Get the last value of the entitlement check on this downstream. If the cached value is
+ * {@link #TETHER_ERROR_NO_ERROR} or showEntitlementUi argument is false, it just return the
+ * cached value. Otherwise, a UI-based entitlement check would be performed. It is not
+ * guaranteed that the UI-based entitlement check will complete in any specific time period
+ * and may in fact never complete. Any successful entitlement check the platform performs for
+ * any reason will update the cached value.
+ *
+ * @param type the downstream type of tethering. Must be one of
+ * {@link #TETHERING_WIFI},
+ * {@link #TETHERING_USB}, or
+ * {@link #TETHERING_BLUETOOTH}.
+ * @param showEntitlementUi a boolean indicating whether to run UI-based entitlement check.
+ * @param listener an {@link TetheringEntitlementValueListener} which will be called to notify
+ * the caller of the result of entitlement check. The listener may be called zero or
+ * one time.
+ * @param handler {@link Handler} to specify the thread upon which the listener will be invoked.
+ * {@hide}
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
+ public void getLatestTetheringEntitlementValue(int type, boolean showEntitlementUi,
+ @NonNull final TetheringEntitlementValueListener listener, @Nullable Handler handler) {
+ Preconditions.checkNotNull(listener, "TetheringEntitlementValueListener cannot be null.");
+ ResultReceiver wrappedListener = new ResultReceiver(handler) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ listener.onEntitlementResult(resultCode);
+ }
+ };
+
+ try {
+ String pkgName = mContext.getOpPackageName();
+ Log.i(TAG, "getLatestTetheringEntitlementValue:" + pkgName);
+ mService.getLatestTetheringEntitlementValue(type, wrappedListener,
+ showEntitlementUi, pkgName);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Report network connectivity status. This is currently used only
* to alter status bar UI.
* <p>This method requires the caller to hold the permission
@@ -3803,6 +3916,26 @@ public class ConnectivityManager {
}
/**
+ * Requests that the system open the captive portal app with the specified extras.
+ *
+ * <p>This endpoint is exclusively for use by the NetworkStack and is protected by the
+ * corresponding permission.
+ * @param network Network on which the captive portal was detected.
+ * @param appExtras Extras to include in the app start intent.
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ @RequiresPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK)
+ public void startCaptivePortalApp(Network network, Bundle appExtras) {
+ try {
+ mService.startCaptivePortalAppInternal(network, appExtras);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Determine whether the device is configured to avoid bad wifi.
* @hide
*/
@@ -3942,10 +4075,17 @@ public class ConnectivityManager {
@Deprecated
public static boolean setProcessDefaultNetwork(@Nullable Network network) {
int netId = (network == null) ? NETID_UNSET : network.netId;
- if (netId == NetworkUtils.getBoundNetworkForProcess()) {
- return true;
+ boolean isSameNetId = (netId == NetworkUtils.getBoundNetworkForProcess());
+
+ if (netId != NETID_UNSET) {
+ netId = network.getNetIdForResolv();
}
- if (NetworkUtils.bindProcessToNetwork(netId)) {
+
+ if (!NetworkUtils.bindProcessToNetwork(netId)) {
+ return false;
+ }
+
+ if (!isSameNetId) {
// Set HTTP proxy system properties to match network.
// TODO: Deprecate this static method and replace it with a non-static version.
try {
@@ -3959,10 +4099,9 @@ public class ConnectivityManager {
// Must flush socket pool as idle sockets will be bound to previous network and may
// cause subsequent fetches to be performed on old network.
NetworkEventDispatcher.getInstance().onNetworkConfigurationChanged();
- return true;
- } else {
- return false;
}
+
+ return true;
}
/**
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index fd7360fd4c17..83bb3a0c8c15 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -27,6 +27,7 @@ import android.net.NetworkQuotaInfo;
import android.net.NetworkRequest;
import android.net.NetworkState;
import android.net.ProxyInfo;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.Messenger;
import android.os.ParcelFileDescriptor;
@@ -167,6 +168,7 @@ interface IConnectivityManager
void setAcceptUnvalidated(in Network network, boolean accept, boolean always);
void setAvoidUnvalidated(in Network network);
void startCaptivePortalApp(in Network network);
+ void startCaptivePortalAppInternal(in Network network, in Bundle appExtras);
boolean getAvoidBadWifi();
int getMultipathPreference(in Network Network);
@@ -188,6 +190,9 @@ interface IConnectivityManager
int intervalSeconds, in Messenger messenger, in IBinder binder, String srcAddr,
String dstAddr);
+ void startTcpKeepalive(in Network network, in FileDescriptor fd, int intervalSeconds,
+ in Messenger messenger, in IBinder binder);
+
void stopKeepalive(in Network network, int slot);
String getCaptivePortalServerUrl();
@@ -197,4 +202,7 @@ interface IConnectivityManager
int getConnectionOwnerUid(in ConnectionInfo connectionInfo);
boolean isCallerCurrentAlwaysOnVpnApp();
boolean isCallerCurrentAlwaysOnVpnLockdownApp();
+
+ void getLatestTetheringEntitlementValue(int type, in ResultReceiver receiver,
+ boolean showEntitlementUi, String callerPkg);
}
diff --git a/core/java/android/net/INetworkMonitor.aidl b/core/java/android/net/INetworkMonitor.aidl
index 41f969acad6f..c94cdde15aa8 100644
--- a/core/java/android/net/INetworkMonitor.aidl
+++ b/core/java/android/net/INetworkMonitor.aidl
@@ -34,6 +34,7 @@ oneway interface INetworkMonitor {
void start();
void launchCaptivePortalApp();
+ void notifyCaptivePortalAppFinished(int response);
void forceReevaluation(int uid);
void notifyPrivateDnsChanged(in PrivateDnsConfigParcel config);
void notifyDnsResponse(int returnCode);
diff --git a/core/java/android/net/INetworkMonitorCallbacks.aidl b/core/java/android/net/INetworkMonitorCallbacks.aidl
index a8682f9ddd3b..2c61511feb72 100644
--- a/core/java/android/net/INetworkMonitorCallbacks.aidl
+++ b/core/java/android/net/INetworkMonitorCallbacks.aidl
@@ -24,7 +24,6 @@ oneway interface INetworkMonitorCallbacks {
void onNetworkMonitorCreated(in INetworkMonitor networkMonitor);
void notifyNetworkTested(int testResult, @nullable String redirectUrl);
void notifyPrivateDnsConfigResolved(in PrivateDnsConfigParcel config);
- void showProvisioningNotification(String action);
+ void showProvisioningNotification(String action, String packageName);
void hideProvisioningNotification();
- void logCaptivePortalLoginEvent(int eventId, String packageName);
} \ No newline at end of file
diff --git a/core/java/android/net/InterfaceConfiguration.java b/core/java/android/net/InterfaceConfiguration.java
index 62cf7d7ceb25..b9d49c14f6c6 100644
--- a/core/java/android/net/InterfaceConfiguration.java
+++ b/core/java/android/net/InterfaceConfiguration.java
@@ -36,8 +36,9 @@ public class InterfaceConfiguration implements Parcelable {
private LinkAddress mAddr;
private HashSet<String> mFlags = Sets.newHashSet();
- private static final String FLAG_UP = INetd.IF_STATE_UP;
- private static final String FLAG_DOWN = INetd.IF_STATE_DOWN;
+ // Must be kept in sync with constant in INetd.aidl
+ private static final String FLAG_UP = "up";
+ private static final String FLAG_DOWN = "down";
private static final String[] EMPTY_STRING_ARRAY = new String[0];
diff --git a/core/java/android/net/KeepalivePacketData.java b/core/java/android/net/KeepalivePacketData.java
index 7436ad08789e..18726f748d30 100644
--- a/core/java/android/net/KeepalivePacketData.java
+++ b/core/java/android/net/KeepalivePacketData.java
@@ -16,22 +16,20 @@
package android.net;
-import static android.net.ConnectivityManager.PacketKeepalive.*;
+import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS;
+import static android.net.SocketKeepalive.ERROR_INVALID_PORT;
+import android.net.SocketKeepalive.InvalidPacketException;
import android.net.util.IpUtils;
import android.os.Parcel;
import android.os.Parcelable;
-import android.system.OsConstants;
import android.util.Log;
-import java.net.Inet4Address;
import java.net.InetAddress;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
/**
* Represents the actual packets that are sent by the
- * {@link android.net.ConnectivityManager.PacketKeepalive} API.
+ * {@link android.net.SocketKeepalive} API.
*
* @hide
*/
@@ -53,8 +51,8 @@ public class KeepalivePacketData implements Parcelable {
/** Packet data. A raw byte string of packet data, not including the link-layer header. */
private final byte[] mPacket;
- private static final int IPV4_HEADER_LENGTH = 20;
- private static final int UDP_HEADER_LENGTH = 8;
+ protected static final int IPV4_HEADER_LENGTH = 20;
+ protected static final int UDP_HEADER_LENGTH = 8;
// This should only be constructed via static factory methods, such as
// nattKeepalivePacket
@@ -80,53 +78,10 @@ public class KeepalivePacketData implements Parcelable {
}
}
- public static class InvalidPacketException extends Exception {
- public final int error;
- public InvalidPacketException(int error) {
- this.error = error;
- }
- }
-
public byte[] getPacket() {
return mPacket.clone();
}
- public static KeepalivePacketData nattKeepalivePacket(
- InetAddress srcAddress, int srcPort, InetAddress dstAddress, int dstPort)
- throws InvalidPacketException {
-
- if (!(srcAddress instanceof Inet4Address) || !(dstAddress instanceof Inet4Address)) {
- throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
- }
-
- if (dstPort != NATT_PORT) {
- throw new InvalidPacketException(ERROR_INVALID_PORT);
- }
-
- int length = IPV4_HEADER_LENGTH + UDP_HEADER_LENGTH + 1;
- ByteBuffer buf = ByteBuffer.allocate(length);
- buf.order(ByteOrder.BIG_ENDIAN);
- buf.putShort((short) 0x4500); // IP version and TOS
- buf.putShort((short) length);
- buf.putInt(0); // ID, flags, offset
- buf.put((byte) 64); // TTL
- buf.put((byte) OsConstants.IPPROTO_UDP);
- int ipChecksumOffset = buf.position();
- buf.putShort((short) 0); // IP checksum
- buf.put(srcAddress.getAddress());
- buf.put(dstAddress.getAddress());
- buf.putShort((short) srcPort);
- buf.putShort((short) dstPort);
- buf.putShort((short) (length - 20)); // UDP length
- int udpChecksumOffset = buf.position();
- buf.putShort((short) 0); // UDP checksum
- buf.put((byte) 0xff); // NAT-T keepalive
- buf.putShort(ipChecksumOffset, IpUtils.ipChecksum(buf, 0));
- buf.putShort(udpChecksumOffset, IpUtils.udpChecksum(buf, 0, IPV4_HEADER_LENGTH));
-
- return new KeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, buf.array());
- }
-
/* Parcelable Implementation */
public int describeContents() {
return 0;
@@ -141,7 +96,7 @@ public class KeepalivePacketData implements Parcelable {
out.writeByteArray(mPacket);
}
- private KeepalivePacketData(Parcel in) {
+ protected KeepalivePacketData(Parcel in) {
srcAddress = NetworkUtils.numericToInetAddress(in.readString());
dstAddress = NetworkUtils.numericToInetAddress(in.readString());
srcPort = in.readInt();
diff --git a/core/java/android/net/NattKeepalivePacketData.java b/core/java/android/net/NattKeepalivePacketData.java
new file mode 100644
index 000000000000..bdb246f516a2
--- /dev/null
+++ b/core/java/android/net/NattKeepalivePacketData.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS;
+import static android.net.SocketKeepalive.ERROR_INVALID_PORT;
+
+import android.net.SocketKeepalive.InvalidPacketException;
+import android.net.util.IpUtils;
+import android.system.OsConstants;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/** @hide */
+public final class NattKeepalivePacketData extends KeepalivePacketData {
+
+ // This should only be constructed via static factory methods, such as
+ // nattKeepalivePacket
+ private NattKeepalivePacketData(InetAddress srcAddress, int srcPort,
+ InetAddress dstAddress, int dstPort, byte[] data) throws
+ InvalidPacketException {
+ super(srcAddress, srcPort, dstAddress, dstPort, data);
+ }
+
+ /**
+ * Factory method to create Nat-T keepalive packet structure.
+ */
+ public static NattKeepalivePacketData nattKeepalivePacket(
+ InetAddress srcAddress, int srcPort, InetAddress dstAddress, int dstPort)
+ throws InvalidPacketException {
+
+ if (!(srcAddress instanceof Inet4Address) || !(dstAddress instanceof Inet4Address)) {
+ throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
+ }
+
+ if (dstPort != NattSocketKeepalive.NATT_PORT) {
+ throw new InvalidPacketException(ERROR_INVALID_PORT);
+ }
+
+ int length = IPV4_HEADER_LENGTH + UDP_HEADER_LENGTH + 1;
+ ByteBuffer buf = ByteBuffer.allocate(length);
+ buf.order(ByteOrder.BIG_ENDIAN);
+ buf.putShort((short) 0x4500); // IP version and TOS
+ buf.putShort((short) length);
+ buf.putInt(0); // ID, flags, offset
+ buf.put((byte) 64); // TTL
+ buf.put((byte) OsConstants.IPPROTO_UDP);
+ int ipChecksumOffset = buf.position();
+ buf.putShort((short) 0); // IP checksum
+ buf.put(srcAddress.getAddress());
+ buf.put(dstAddress.getAddress());
+ buf.putShort((short) srcPort);
+ buf.putShort((short) dstPort);
+ buf.putShort((short) (length - 20)); // UDP length
+ int udpChecksumOffset = buf.position();
+ buf.putShort((short) 0); // UDP checksum
+ buf.put((byte) 0xff); // NAT-T keepalive
+ buf.putShort(ipChecksumOffset, IpUtils.ipChecksum(buf, 0));
+ buf.putShort(udpChecksumOffset, IpUtils.udpChecksum(buf, 0, IPV4_HEADER_LENGTH));
+
+ return new NattKeepalivePacketData(srcAddress, srcPort, dstAddress, dstPort, buf.array());
+ }
+}
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 99bfc140f140..7bef69012bf7 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -18,7 +18,6 @@ package android.net;
import android.annotation.UnsupportedAppUsage;
import android.content.Context;
-import android.net.ConnectivityManager.PacketKeepalive;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
@@ -154,7 +153,7 @@ public abstract class NetworkAgent extends Handler {
*
* Also used internally by ConnectivityService / KeepaliveTracker, with different semantics.
*/
- public static final int CMD_START_PACKET_KEEPALIVE = BASE + 11;
+ public static final int CMD_START_SOCKET_KEEPALIVE = BASE + 11;
/**
* Requests that the specified keepalive packet be stopped.
@@ -163,20 +162,40 @@ public abstract class NetworkAgent extends Handler {
*
* Also used internally by ConnectivityService / KeepaliveTracker, with different semantics.
*/
- public static final int CMD_STOP_PACKET_KEEPALIVE = BASE + 12;
+ public static final int CMD_STOP_SOCKET_KEEPALIVE = BASE + 12;
/**
- * Sent by the NetworkAgent to ConnectivityService to provide status on a packet keepalive
- * request. This may either be the reply to a CMD_START_PACKET_KEEPALIVE, or an asynchronous
+ * Sent by the NetworkAgent to ConnectivityService to provide status on a socket keepalive
+ * request. This may either be the reply to a CMD_START_SOCKET_KEEPALIVE, or an asynchronous
* error notification.
*
- * This is also sent by KeepaliveTracker to the app's ConnectivityManager.PacketKeepalive to
- * so that the app's PacketKeepaliveCallback methods can be called.
+ * This is also sent by KeepaliveTracker to the app's {@link SocketKeepalive},
+ * so that the app's {@link SocketKeepalive.Callback} methods can be called.
*
* arg1 = slot number of the keepalive
* arg2 = error code
*/
- public static final int EVENT_PACKET_KEEPALIVE = BASE + 13;
+ public static final int EVENT_SOCKET_KEEPALIVE = BASE + 13;
+
+ // TODO: move the above 2 constants down so they are in order once merge conflicts are resolved
+ /**
+ * Sent by the KeepaliveTracker to NetworkAgent to add a packet filter.
+ *
+ * For TCP keepalive offloads, keepalive packets are sent by the firmware. However, because the
+ * remote site will send ACK packets in response to the keepalive packets, the firmware also
+ * needs to be configured to properly filter the ACKs to prevent the system from waking up.
+ * This does not happen with UDP, so this message is TCP-specific.
+ * arg1 = slot number of the keepalive to filter for.
+ * obj = the keepalive packet to send repeatedly.
+ */
+ public static final int CMD_ADD_KEEPALIVE_PACKET_FILTER = BASE + 16;
+
+ /**
+ * Sent by the KeepaliveTracker to NetworkAgent to remove a packet filter. See
+ * {@link #CMD_ADD_KEEPALIVE_PACKET_FILTER}.
+ * arg1 = slot number of the keepalive packet filter to remove.
+ */
+ public static final int CMD_REMOVE_KEEPALIVE_PACKET_FILTER = BASE + 17;
/**
* Sent by ConnectivityService to inform this network transport of signal strength thresholds
@@ -288,12 +307,12 @@ public abstract class NetworkAgent extends Handler {
saveAcceptUnvalidated(msg.arg1 != 0);
break;
}
- case CMD_START_PACKET_KEEPALIVE: {
- startPacketKeepalive(msg);
+ case CMD_START_SOCKET_KEEPALIVE: {
+ startSocketKeepalive(msg);
break;
}
- case CMD_STOP_PACKET_KEEPALIVE: {
- stopPacketKeepalive(msg);
+ case CMD_STOP_SOCKET_KEEPALIVE: {
+ stopSocketKeepalive(msg);
break;
}
@@ -313,6 +332,14 @@ public abstract class NetworkAgent extends Handler {
preventAutomaticReconnect();
break;
}
+ case CMD_ADD_KEEPALIVE_PACKET_FILTER: {
+ addKeepalivePacketFilter(msg);
+ break;
+ }
+ case CMD_REMOVE_KEEPALIVE_PACKET_FILTER: {
+ removeKeepalivePacketFilter(msg);
+ break;
+ }
}
}
@@ -443,22 +470,40 @@ public abstract class NetworkAgent extends Handler {
/**
* Requests that the network hardware send the specified packet at the specified interval.
*/
- protected void startPacketKeepalive(Message msg) {
- onPacketKeepaliveEvent(msg.arg1, PacketKeepalive.ERROR_HARDWARE_UNSUPPORTED);
+ protected void startSocketKeepalive(Message msg) {
+ onSocketKeepaliveEvent(msg.arg1, SocketKeepalive.ERROR_HARDWARE_UNSUPPORTED);
}
/**
- * Requests that the network hardware send the specified packet at the specified interval.
+ * Requests that the network hardware stops sending keepalive packets.
+ */
+ protected void stopSocketKeepalive(Message msg) {
+ onSocketKeepaliveEvent(msg.arg1, SocketKeepalive.ERROR_HARDWARE_UNSUPPORTED);
+ }
+
+ /**
+ * Called by the network when a socket keepalive event occurs.
+ */
+ public void onSocketKeepaliveEvent(int slot, int reason) {
+ queueOrSendMessage(EVENT_SOCKET_KEEPALIVE, slot, reason);
+ }
+
+ /**
+ * Called by ConnectivityService to add specific packet filter to network hardware to block
+ * ACKs matching the sent keepalive packets. Implementations that support this feature must
+ * override this method.
*/
- protected void stopPacketKeepalive(Message msg) {
- onPacketKeepaliveEvent(msg.arg1, PacketKeepalive.ERROR_HARDWARE_UNSUPPORTED);
+ protected void addKeepalivePacketFilter(Message msg) {
+ onSocketKeepaliveEvent(msg.arg1, SocketKeepalive.ERROR_HARDWARE_UNSUPPORTED);
}
/**
- * Called by the network when a packet keepalive event occurs.
+ * Called by ConnectivityService to remove a packet filter installed with
+ * {@link #addKeepalivePacketFilter(Message)}. Implementations that support this feature
+ * must override this method.
*/
- public void onPacketKeepaliveEvent(int slot, int reason) {
- queueOrSendMessage(EVENT_PACKET_KEEPALIVE, slot, reason);
+ protected void removeKeepalivePacketFilter(Message msg) {
+ onSocketKeepaliveEvent(msg.arg1, SocketKeepalive.ERROR_HARDWARE_UNSUPPORTED);
}
/**
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 7e9bda14b199..1d2d81dc4fbe 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -597,6 +597,7 @@ public final class NetworkCapabilities implements Parcelable {
TRANSPORT_VPN,
TRANSPORT_WIFI_AWARE,
TRANSPORT_LOWPAN,
+ TRANSPORT_TEST,
})
public @interface Transport { }
@@ -635,10 +636,18 @@ public final class NetworkCapabilities implements Parcelable {
*/
public static final int TRANSPORT_LOWPAN = 6;
+ /**
+ * Indicates this network uses a Test-only virtual interface as a transport.
+ *
+ * @hide
+ */
+ @TestApi
+ public static final int TRANSPORT_TEST = 7;
+
/** @hide */
public static final int MIN_TRANSPORT = TRANSPORT_CELLULAR;
/** @hide */
- public static final int MAX_TRANSPORT = TRANSPORT_LOWPAN;
+ public static final int MAX_TRANSPORT = TRANSPORT_TEST;
/** @hide */
public static boolean isValidTransport(@Transport int transportType) {
@@ -652,7 +661,8 @@ public final class NetworkCapabilities implements Parcelable {
"ETHERNET",
"VPN",
"WIFI_AWARE",
- "LOWPAN"
+ "LOWPAN",
+ "TEST"
};
/**
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 5ab34e9aa6e8..bf272625e713 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -92,16 +92,6 @@ public class NetworkPolicyManager {
public static final int MASK_ALL_NETWORKS = 0b11110000;
public static final int FIREWALL_RULE_DEFAULT = 0;
- public static final int FIREWALL_RULE_ALLOW = INetd.FIREWALL_RULE_ALLOW;
- public static final int FIREWALL_RULE_DENY = INetd.FIREWALL_RULE_DENY;
-
- public static final int FIREWALL_TYPE_WHITELIST = INetd.FIREWALL_WHITELIST;
- public static final int FIREWALL_TYPE_BLACKLIST = INetd.FIREWALL_BLACKLIST;
-
- public static final int FIREWALL_CHAIN_NONE = INetd.FIREWALL_CHAIN_NONE;
- public static final int FIREWALL_CHAIN_DOZABLE = INetd.FIREWALL_CHAIN_DOZABLE;
- public static final int FIREWALL_CHAIN_STANDBY = INetd.FIREWALL_CHAIN_STANDBY;
- public static final int FIREWALL_CHAIN_POWERSAVE = INetd.FIREWALL_CHAIN_POWERSAVE;
public static final String FIREWALL_CHAIN_NAME_NONE = "none";
public static final String FIREWALL_CHAIN_NAME_DOZABLE = "dozable";
diff --git a/core/java/android/net/NetworkStack.java b/core/java/android/net/NetworkStack.java
index ac6bff029e8c..dbb894f92f55 100644
--- a/core/java/android/net/NetworkStack.java
+++ b/core/java/android/net/NetworkStack.java
@@ -15,240 +15,26 @@
*/
package android.net;
-import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH;
-import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemService;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.net.dhcp.DhcpServingParamsParcel;
-import android.net.dhcp.IDhcpServerCallbacks;
-import android.net.ip.IIpClientCallbacks;
-import android.os.Binder;
-import android.os.IBinder;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.UserHandle;
-import android.util.Slog;
-
-import com.android.internal.annotations.GuardedBy;
-
-import java.lang.reflect.InvocationTargetException;
-import java.util.ArrayList;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
/**
- * Service used to communicate with the network stack, which is running in a separate module.
+ *
+ * Constants for client code communicating with the network stack service.
* @hide
*/
-@SystemService(Context.NETWORK_STACK_SERVICE)
+@SystemApi
+@TestApi
public class NetworkStack {
- private static final String TAG = NetworkStack.class.getSimpleName();
-
- public static final String NETWORKSTACK_PACKAGE_NAME = "com.android.mainline.networkstack";
-
- private static final int NETWORKSTACK_TIMEOUT_MS = 10_000;
-
- @NonNull
- @GuardedBy("mPendingNetStackRequests")
- private final ArrayList<NetworkStackCallback> mPendingNetStackRequests = new ArrayList<>();
- @Nullable
- @GuardedBy("mPendingNetStackRequests")
- private INetworkStackConnector mConnector;
-
- private volatile boolean mNetworkStackStartRequested = false;
-
- private interface NetworkStackCallback {
- void onNetworkStackConnected(INetworkStackConnector connector);
- }
-
- public NetworkStack() { }
-
- /**
- * Create a DHCP server according to the specified parameters.
- *
- * <p>The server will be returned asynchronously through the provided callbacks.
- */
- public void makeDhcpServer(final String ifName, final DhcpServingParamsParcel params,
- final IDhcpServerCallbacks cb) {
- requestConnector(connector -> {
- try {
- connector.makeDhcpServer(ifName, params, cb);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
- });
- }
-
/**
- * Create an IpClient on the specified interface.
- *
- * <p>The IpClient will be returned asynchronously through the provided callbacks.
+ * Permission granted only to the NetworkStack APK, defined in NetworkStackStub with signature
+ * protection level.
+ * @hide
*/
- public void makeIpClient(String ifName, IIpClientCallbacks cb) {
- requestConnector(connector -> {
- try {
- connector.makeIpClient(ifName, cb);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
- });
- }
-
- /**
- * Create a NetworkMonitor.
- *
- * <p>The INetworkMonitor will be returned asynchronously through the provided callbacks.
- */
- public void makeNetworkMonitor(
- NetworkParcelable network, String name, INetworkMonitorCallbacks cb) {
- requestConnector(connector -> {
- try {
- connector.makeNetworkMonitor(network, name, cb);
- } catch (RemoteException e) {
- e.rethrowFromSystemServer();
- }
- });
- }
-
- private class NetworkStackConnection implements ServiceConnection {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- registerNetworkStackService(service);
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- // TODO: crash/reboot the system ?
- Slog.wtf(TAG, "Lost network stack connector");
- }
- };
-
- private void registerNetworkStackService(@NonNull IBinder service) {
- final INetworkStackConnector connector = INetworkStackConnector.Stub.asInterface(service);
-
- ServiceManager.addService(Context.NETWORK_STACK_SERVICE, service, false /* allowIsolated */,
- DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL);
-
- final ArrayList<NetworkStackCallback> requests;
- synchronized (mPendingNetStackRequests) {
- requests = new ArrayList<>(mPendingNetStackRequests);
- mPendingNetStackRequests.clear();
- mConnector = connector;
- }
-
- for (NetworkStackCallback r : requests) {
- r.onNetworkStackConnected(connector);
- }
- }
-
- /**
- * Start the network stack. Should be called only once on device startup.
- *
- * <p>This method will start the network stack either in the network stack process, or inside
- * the system server on devices that do not support the network stack module. The network stack
- * connector will then be delivered asynchronously to clients that requested it before it was
- * started.
- */
- public void start(Context context) {
- mNetworkStackStartRequested = true;
- // Try to bind in-process if the library is available
- IBinder connector = null;
- try {
- final Class service = Class.forName(
- "com.android.server.NetworkStackService",
- true /* initialize */,
- context.getClassLoader());
- connector = (IBinder) service.getMethod("makeConnector", Context.class)
- .invoke(null, context);
- } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
- Slog.wtf(TAG, "Could not create network stack connector from NetworkStackService");
- // TODO: crash/reboot system here ?
- return;
- } catch (ClassNotFoundException e) {
- // Normal behavior if stack is provided by the app: fall through
- }
-
- // In-process network stack. Add the service to the service manager here.
- if (connector != null) {
- registerNetworkStackService(connector);
- return;
- }
- // Start the network stack process. The service will be added to the service manager in
- // NetworkStackConnection.onServiceConnected().
- final Intent intent = new Intent(INetworkStackConnector.class.getName());
- final ComponentName comp = intent.resolveSystemService(context.getPackageManager(), 0);
- intent.setComponent(comp);
-
- if (comp == null || !context.bindServiceAsUser(intent, new NetworkStackConnection(),
- Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM)) {
- Slog.wtf(TAG,
- "Could not bind to network stack in-process, or in app with " + intent);
- // TODO: crash/reboot system server if no network stack after a timeout ?
- }
- }
-
- /**
- * For non-system server clients, get the connector registered by the system server.
- */
- private INetworkStackConnector getRemoteConnector() {
- // Block until the NetworkStack connector is registered in ServiceManager.
- // <p>This is only useful for non-system processes that do not have a way to be notified of
- // registration completion. Adding a callback system would be too heavy weight considering
- // that the connector is registered on boot, so it is unlikely that a client would request
- // it before it is registered.
- // TODO: consider blocking boot on registration and simplify much of the logic in this class
- IBinder connector;
- try {
- final long before = System.currentTimeMillis();
- while ((connector = ServiceManager.getService(Context.NETWORK_STACK_SERVICE)) == null) {
- Thread.sleep(20);
- if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) {
- Slog.e(TAG, "Timeout waiting for NetworkStack connector");
- return null;
- }
- }
- } catch (InterruptedException e) {
- Slog.e(TAG, "Error waiting for NetworkStack connector", e);
- return null;
- }
-
- return INetworkStackConnector.Stub.asInterface(connector);
- }
-
- private void requestConnector(@NonNull NetworkStackCallback request) {
- // TODO: PID check.
- final int caller = Binder.getCallingUid();
- if (caller != Process.SYSTEM_UID && caller != Process.BLUETOOTH_UID) {
- // Don't even attempt to obtain the connector and give a nice error message
- throw new SecurityException(
- "Only the system server should try to bind to the network stack.");
- }
-
- if (!mNetworkStackStartRequested) {
- // The network stack is not being started in this process, e.g. this process is not
- // the system server. Get a remote connector registered by the system server.
- final INetworkStackConnector connector = getRemoteConnector();
- synchronized (mPendingNetStackRequests) {
- mConnector = connector;
- }
- request.onNetworkStackConnected(connector);
- return;
- }
-
- final INetworkStackConnector connector;
- synchronized (mPendingNetStackRequests) {
- connector = mConnector;
- if (connector == null) {
- mPendingNetStackRequests.add(request);
- return;
- }
- }
+ @SystemApi
+ @TestApi
+ public static final String PERMISSION_MAINLINE_NETWORK_STACK =
+ "android.permission.MAINLINE_NETWORK_STACK";
- request.onNetworkStackConnected(connector);
- }
+ private NetworkStack() {}
}
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 07668a9e3a82..0ae29b125149 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -71,6 +71,18 @@ public class NetworkUtils {
throws SocketException;
/**
+ * Attaches a socket filter that drops all of incoming packets.
+ * @param fd the socket's {@link FileDescriptor}.
+ */
+ public static native void attachDropAllBPFFilter(FileDescriptor fd) throws SocketException;
+
+ /**
+ * Detaches a socket filter.
+ * @param fd the socket's {@link FileDescriptor}.
+ */
+ public static native void detachBPFFilter(FileDescriptor fd) throws SocketException;
+
+ /**
* Configures a socket for receiving ICMPv6 router solicitations and sending advertisements.
* @param fd the socket's {@link FileDescriptor}.
* @param ifIndex the interface index.
@@ -170,6 +182,16 @@ public class NetworkUtils {
private static native void addArpEntry(byte[] ethAddr, byte[] netAddr, String ifname,
FileDescriptor fd) throws IOException;
+
+ /**
+ * Get the tcp repair window associated with the {@code fd}.
+ *
+ * @param fd the tcp socket's {@link FileDescriptor}.
+ * @return a {@link TcpRepairWindow} object indicates tcp window size.
+ */
+ public static native TcpRepairWindow getTcpRepairWindow(FileDescriptor fd)
+ throws ErrnoException;
+
/**
* @see Inet4AddressUtils#intToInet4AddressHTL(int)
* @deprecated Use either {@link Inet4AddressUtils#intToInet4AddressHTH(int)}
diff --git a/core/java/android/net/SocketKeepalive.java b/core/java/android/net/SocketKeepalive.java
index 97d50f4bac05..07728beb9c64 100644
--- a/core/java/android/net/SocketKeepalive.java
+++ b/core/java/android/net/SocketKeepalive.java
@@ -19,6 +19,7 @@ package android.net;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
@@ -109,13 +110,53 @@ public abstract class SocketKeepalive implements AutoCloseable {
**/
public static final int MAX_INTERVAL_SEC = 3600;
+ /**
+ * An exception that embarks an error code.
+ * @hide
+ */
+ public static class ErrorCodeException extends Exception {
+ public final int error;
+ public ErrorCodeException(final int error, final Throwable e) {
+ super(e);
+ this.error = error;
+ }
+ public ErrorCodeException(final int error) {
+ this.error = error;
+ }
+ }
+
+ /**
+ * This socket is invalid.
+ * See the error code for details, and the optional cause.
+ * @hide
+ */
+ public static class InvalidSocketException extends ErrorCodeException {
+ public InvalidSocketException(final int error, final Throwable e) {
+ super(error, e);
+ }
+ public InvalidSocketException(final int error) {
+ super(error);
+ }
+ }
+
+ /**
+ * This packet is invalid.
+ * See the error code for details.
+ * @hide
+ */
+ public static class InvalidPacketException extends ErrorCodeException {
+ public InvalidPacketException(final int error) {
+ super(error);
+ }
+ }
+
@NonNull final IConnectivityManager mService;
@NonNull final Network mNetwork;
@NonNull private final Executor mExecutor;
@NonNull private final SocketKeepalive.Callback mCallback;
@NonNull private final Looper mLooper;
@NonNull final Messenger mMessenger;
- @NonNull Integer mSlot;
+ @Nullable Integer mSlot;
SocketKeepalive(@NonNull IConnectivityManager service, @NonNull Network network,
@NonNull Executor executor, @NonNull Callback callback) {
@@ -135,7 +176,7 @@ public abstract class SocketKeepalive implements AutoCloseable {
@Override
public void handleMessage(Message message) {
switch (message.what) {
- case NetworkAgent.EVENT_PACKET_KEEPALIVE:
+ case NetworkAgent.EVENT_SOCKET_KEEPALIVE:
final int status = message.arg2;
try {
if (status == SUCCESS) {
diff --git a/core/java/android/net/TcpKeepalivePacketData.java b/core/java/android/net/TcpKeepalivePacketData.java
new file mode 100644
index 000000000000..f07dfb64cd01
--- /dev/null
+++ b/core/java/android/net/TcpKeepalivePacketData.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net;
+
+import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.SocketKeepalive.InvalidPacketException;
+import android.net.util.IpUtils;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.system.OsConstants;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Objects;
+
+/**
+ * Represents the actual tcp keep alive packets which will be used for hardware offload.
+ * @hide
+ */
+public class TcpKeepalivePacketData extends KeepalivePacketData implements Parcelable {
+ private static final String TAG = "TcpKeepalivePacketData";
+
+ /** TCP sequence number. */
+ public final int tcpSeq;
+
+ /** TCP ACK number. */
+ public final int tcpAck;
+
+ /** TCP RCV window. */
+ public final int tcpWnd;
+
+ /** TCP RCV window scale. */
+ public final int tcpWndScale;
+
+ private static final int IPV4_HEADER_LENGTH = 20;
+ private static final int IPV6_HEADER_LENGTH = 40;
+ private static final int TCP_HEADER_LENGTH = 20;
+
+ // This should only be constructed via static factory methods, such as
+ // tcpKeepalivePacket.
+ private TcpKeepalivePacketData(TcpSocketInfo tcpDetails, byte[] data)
+ throws InvalidPacketException {
+ super(tcpDetails.srcAddress, tcpDetails.srcPort, tcpDetails.dstAddress,
+ tcpDetails.dstPort, data);
+ tcpSeq = tcpDetails.seq;
+ tcpAck = tcpDetails.ack;
+ // In the packet, the window is shifted right by the window scale.
+ tcpWnd = tcpDetails.rcvWnd;
+ tcpWndScale = tcpDetails.rcvWndScale;
+ }
+
+ /**
+ * Factory method to create tcp keepalive packet structure.
+ */
+ public static TcpKeepalivePacketData tcpKeepalivePacket(
+ TcpSocketInfo tcpDetails) throws InvalidPacketException {
+ final byte[] packet;
+ if ((tcpDetails.srcAddress instanceof Inet4Address)
+ && (tcpDetails.dstAddress instanceof Inet4Address)) {
+ packet = buildV4Packet(tcpDetails);
+ } else {
+ // TODO: support ipv6
+ throw new InvalidPacketException(ERROR_INVALID_IP_ADDRESS);
+ }
+
+ return new TcpKeepalivePacketData(tcpDetails, packet);
+ }
+
+ /**
+ * Build ipv4 tcp keepalive packet, not including the link-layer header.
+ */
+ // TODO : if this code is ever moved to the network stack, factorize constants with the ones
+ // over there.
+ private static byte[] buildV4Packet(TcpSocketInfo tcpDetails) {
+ final int length = IPV4_HEADER_LENGTH + TCP_HEADER_LENGTH;
+ ByteBuffer buf = ByteBuffer.allocate(length);
+ buf.order(ByteOrder.BIG_ENDIAN);
+ // IP version and TOS. TODO : fetch this from getsockopt(SOL_IP, IP_TOS)
+ buf.putShort((short) 0x4500);
+ buf.putShort((short) length);
+ buf.putInt(0x4000); // ID, flags=DF, offset
+ // TODO : fetch TTL from getsockopt(SOL_IP, IP_TTL)
+ buf.put((byte) 64);
+ buf.put((byte) OsConstants.IPPROTO_TCP);
+ final int ipChecksumOffset = buf.position();
+ buf.putShort((short) 0); // IP checksum
+ buf.put(tcpDetails.srcAddress.getAddress());
+ buf.put(tcpDetails.dstAddress.getAddress());
+ buf.putShort((short) tcpDetails.srcPort);
+ buf.putShort((short) tcpDetails.dstPort);
+ buf.putInt(tcpDetails.seq); // Sequence Number
+ buf.putInt(tcpDetails.ack); // ACK
+ buf.putShort((short) 0x5010); // TCP length=5, flags=ACK
+ buf.putShort((short) (tcpDetails.rcvWnd >> tcpDetails.rcvWndScale)); // Window size
+ final int tcpChecksumOffset = buf.position();
+ buf.putShort((short) 0); // TCP checksum
+ // URG is not set therefore the urgent pointer is not included
+ buf.putShort(ipChecksumOffset, IpUtils.ipChecksum(buf, 0));
+ buf.putShort(tcpChecksumOffset, IpUtils.tcpChecksum(
+ buf, 0, IPV4_HEADER_LENGTH, TCP_HEADER_LENGTH));
+
+ return buf.array();
+ }
+
+ // TODO: add buildV6Packet.
+
+ /** Represents tcp/ip information. */
+ // TODO: Replace TcpSocketInfo with TcpKeepalivePacketDataParcelable.
+ public static class TcpSocketInfo {
+ public final InetAddress srcAddress;
+ public final InetAddress dstAddress;
+ public final int srcPort;
+ public final int dstPort;
+ public final int seq;
+ public final int ack;
+ public final int rcvWnd;
+ public final int rcvWndScale;
+
+ public TcpSocketInfo(InetAddress sAddr, int sPort, InetAddress dAddr,
+ int dPort, int writeSeq, int readSeq, int rWnd, int rWndScale) {
+ srcAddress = sAddr;
+ dstAddress = dAddr;
+ srcPort = sPort;
+ dstPort = dPort;
+ seq = writeSeq;
+ ack = readSeq;
+ rcvWnd = rWnd;
+ rcvWndScale = rWndScale;
+ }
+ }
+
+ @Override
+ public boolean equals(@Nullable final Object o) {
+ if (!(o instanceof TcpKeepalivePacketData)) return false;
+ final TcpKeepalivePacketData other = (TcpKeepalivePacketData) o;
+ return this.srcAddress.equals(other.srcAddress)
+ && this.dstAddress.equals(other.dstAddress)
+ && this.srcPort == other.srcPort
+ && this.dstPort == other.dstPort
+ && this.tcpAck == other.tcpAck
+ && this.tcpSeq == other.tcpSeq
+ && this.tcpWnd == other.tcpWnd
+ && this.tcpWndScale == other.tcpWndScale;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(srcAddress, dstAddress, srcPort, dstPort, tcpAck, tcpSeq, tcpWnd,
+ tcpWndScale);
+ }
+
+ /* Parcelable Implementation. */
+ /* Note that this object implements parcelable (and needs to keep doing this as it inherits
+ * from a class that does), but should usually be parceled as a stable parcelable using
+ * the toStableParcelable() and fromStableParcelable() methods.
+ */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Write to parcel. */
+ public void writeToParcel(Parcel out, int flags) {
+ super.writeToParcel(out, flags);
+ out.writeInt(tcpSeq);
+ out.writeInt(tcpAck);
+ out.writeInt(tcpWnd);
+ out.writeInt(tcpWndScale);
+ }
+
+ private TcpKeepalivePacketData(Parcel in) {
+ super(in);
+ tcpSeq = in.readInt();
+ tcpAck = in.readInt();
+ tcpWnd = in.readInt();
+ tcpWndScale = in.readInt();
+ }
+
+ /** Parcelable Creator. */
+ public static final Parcelable.Creator<TcpKeepalivePacketData> CREATOR =
+ new Parcelable.Creator<TcpKeepalivePacketData>() {
+ public TcpKeepalivePacketData createFromParcel(Parcel in) {
+ return new TcpKeepalivePacketData(in);
+ }
+
+ public TcpKeepalivePacketData[] newArray(int size) {
+ return new TcpKeepalivePacketData[size];
+ }
+ };
+
+ /**
+ * Convert this TcpKeepalivePacketData to a TcpKeepalivePacketDataParcelable.
+ */
+ @NonNull
+ public TcpKeepalivePacketDataParcelable toStableParcelable() {
+ final TcpKeepalivePacketDataParcelable parcel = new TcpKeepalivePacketDataParcelable();
+ parcel.srcAddress = srcAddress.getAddress();
+ parcel.srcPort = srcPort;
+ parcel.dstAddress = dstAddress.getAddress();
+ parcel.dstPort = dstPort;
+ parcel.seq = tcpSeq;
+ parcel.ack = tcpAck;
+ return parcel;
+ }
+
+ @Override
+ public String toString() {
+ return "saddr: " + srcAddress
+ + " daddr: " + dstAddress
+ + " sport: " + srcPort
+ + " dport: " + dstPort
+ + " seq: " + tcpSeq
+ + " ack: " + tcpAck
+ + " wnd: " + tcpWnd
+ + " wndScale: " + tcpWndScale;
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsPart.java b/core/java/android/net/TcpKeepalivePacketDataParcelable.aidl
index da501738a0bf..7329c63b09be 100644
--- a/telephony/java/android/telephony/ims/RcsPart.java
+++ b/core/java/android/net/TcpKeepalivePacketDataParcelable.aidl
@@ -13,13 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.telephony.ims;
-import android.os.Parcelable;
+package android.net;
-/**
- * A part of a composite {@link RcsMessage}.
- * @hide - TODO(sahinc) make this public
- */
-public abstract class RcsPart implements Parcelable {
+parcelable TcpKeepalivePacketDataParcelable {
+ byte[] srcAddress;
+ int srcPort;
+ byte[] dstAddress;
+ int dstPort;
+ int seq;
+ int ack;
}
diff --git a/core/java/android/net/TcpRepairWindow.java b/core/java/android/net/TcpRepairWindow.java
new file mode 100644
index 000000000000..86034f0a76ed
--- /dev/null
+++ b/core/java/android/net/TcpRepairWindow.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2019 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;
+
+/**
+ * Corresponds to C's {@code struct tcp_repair_window} from
+ * include/uapi/linux/tcp.h
+ *
+ * @hide
+ */
+public final class TcpRepairWindow {
+ public final int sndWl1;
+ public final int sndWnd;
+ public final int maxWindow;
+ public final int rcvWnd;
+ public final int rcvWup;
+ public final int rcvWndScale;
+
+ /**
+ * Constructs an instance with the given field values.
+ */
+ public TcpRepairWindow(final int sndWl1, final int sndWnd, final int maxWindow,
+ final int rcvWnd, final int rcvWup, final int rcvWndScale) {
+ this.sndWl1 = sndWl1;
+ this.sndWnd = sndWnd;
+ this.maxWindow = maxWindow;
+ this.rcvWnd = rcvWnd;
+ this.rcvWup = rcvWup;
+ this.rcvWndScale = rcvWndScale;
+ }
+}
diff --git a/core/java/android/net/TcpSocketKeepalive.java b/core/java/android/net/TcpSocketKeepalive.java
new file mode 100644
index 000000000000..8f6ee7bf2950
--- /dev/null
+++ b/core/java/android/net/TcpSocketKeepalive.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.annotation.NonNull;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.net.Socket;
+import java.util.concurrent.Executor;
+
+/** @hide */
+final class TcpSocketKeepalive extends SocketKeepalive {
+
+ private final Socket mSocket;
+
+ TcpSocketKeepalive(@NonNull IConnectivityManager service,
+ @NonNull Network network,
+ @NonNull Socket socket,
+ @NonNull Executor executor,
+ @NonNull Callback callback) {
+ super(service, network, executor, callback);
+ mSocket = socket;
+ }
+
+ /**
+ * Starts keepalives. {@code mSocket} must be a connected TCP socket.
+ *
+ * - The application must not write to or read from the socket after calling this method, until
+ * onDataReceived, onStopped, or onError are called. If it does, the keepalive will fail
+ * with {@link #ERROR_SOCKET_NOT_IDLE}, or {@code #ERROR_INVALID_SOCKET} if the socket
+ * experienced an error (as in poll(2) returned POLLERR); if this happens, the data received
+ * from the socket may be invalid, and the socket can't be recovered.
+ * - If the socket has data in the send or receive buffer, then this call will fail with
+ * {@link #ERROR_SOCKET_NOT_IDLE} and can be retried after the data has been processed.
+ * An app could ensure this by using an application-layer protocol where it can receive
+ * acknowledgement that it will go into keepalive mode. It could then go into keepalive
+ * mode after having read the acknowledgement, draining the socket.
+ */
+ @Override
+ void startImpl(int intervalSec) {
+ try {
+ final FileDescriptor fd = mSocket.getFileDescriptor$();
+ mService.startTcpKeepalive(mNetwork, fd, intervalSec, mMessenger, new Binder());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error starting packet keepalive: ", e);
+ stopLooper();
+ }
+ }
+
+ @Override
+ void stopImpl() {
+ try {
+ if (mSlot != null) {
+ mService.stopKeepalive(mNetwork, mSlot);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error stopping packet keepalive: ", e);
+ stopLooper();
+ }
+ }
+}
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index dc099a46aa2a..784f23311103 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -791,6 +791,27 @@ public class VpnService extends Service {
}
/**
+ * Marks the VPN network as metered. A VPN network is classified as metered when the user is
+ * sensitive to heavy data usage due to monetary costs and/or data limitations. In such
+ * cases, you should set this to {@code true} so that apps on the system can avoid doing
+ * large data transfers. Otherwise, set this to {@code false}. Doing so would cause VPN
+ * network to inherit its meteredness from its underlying networks.
+ *
+ * <p>VPN apps targeting {@link android.os.Build.VERSION_CODES#Q} or above will be
+ * considered metered by default.
+ *
+ * @param isMetered {@code true} if VPN network should be treated as metered regardless of
+ * underlying network meteredness
+ * @return this {@link Builder} object to facilitate chaining method calls
+ * @see #setUnderlyingNetworks(Networks[])
+ * @see ConnectivityManager#isActiveNetworkMetered()
+ */
+ public Builder setMetered(boolean isMetered) {
+ mConfig.isMetered = isMetered;
+ return this;
+ }
+
+ /**
* Create a VPN interface using the parameters supplied to this
* builder. The interface works on IP packets, and a file descriptor
* is returned for the application to access them. Each read
diff --git a/core/java/android/net/ip/IIpClient.aidl b/core/java/android/net/ip/IIpClient.aidl
index 7769ec2b65ac..a4a80e1efe6f 100644
--- a/core/java/android/net/ip/IIpClient.aidl
+++ b/core/java/android/net/ip/IIpClient.aidl
@@ -17,6 +17,7 @@ package android.net.ip;
import android.net.ProxyInfoParcelable;
import android.net.ProvisioningConfigurationParcelable;
+import android.net.TcpKeepalivePacketDataParcelable;
/** @hide */
oneway interface IIpClient {
@@ -29,4 +30,6 @@ oneway interface IIpClient {
void setTcpBufferSizes(in String tcpBufferSizes);
void setHttpProxy(in ProxyInfoParcelable proxyInfo);
void setMulticastFilter(boolean enabled);
+ void addKeepalivePacketFilter(int slot, in TcpKeepalivePacketDataParcelable pkt);
+ void removeKeepalivePacketFilter(int slot);
}
diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java
index 141d33bc4145..a851e04e78ec 100644
--- a/core/java/android/os/AsyncTask.java
+++ b/core/java/android/os/AsyncTask.java
@@ -19,14 +19,16 @@ package android.os;
import android.annotation.MainThread;
import android.annotation.Nullable;
import android.annotation.WorkerThread;
+
import java.util.ArrayDeque;
-import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.FutureTask;
import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.RejectedExecutionHandler;
+import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@@ -70,7 +72,7 @@ import java.util.concurrent.atomic.AtomicInteger;
* protected Long doInBackground(URL... urls) {
* int count = urls.length;
* long totalSize = 0;
- * for (int i = 0; i < count; i++) {
+ * for (int i = 0; i &lt; count; i++) {
* totalSize += Downloader.downloadFile(urls[i]);
* publishProgress((int) ((i / (float) count) * 100));
* // Escape early if cancel() is called
@@ -158,13 +160,22 @@ import java.util.concurrent.atomic.AtomicInteger;
* </ul>
*
* <h2>Memory observability</h2>
- * <p>AsyncTask guarantees that all callback calls are synchronized in such a way that the following
- * operations are safe without explicit synchronizations.</p>
+ * <p>AsyncTask guarantees that all callback calls are synchronized to ensure the following
+ * without explicit synchronizations.</p>
* <ul>
- * <li>Set member fields in the constructor or {@link #onPreExecute}, and refer to them
- * in {@link #doInBackground}.
- * <li>Set member fields in {@link #doInBackground}, and refer to them in
- * {@link #onProgressUpdate} and {@link #onPostExecute}.
+ * <li>The memory effects of {@link #onPreExecute}, and anything else
+ * executed before the call to {@link #execute}, including the construction
+ * of the AsyncTask object, are visible to {@link #doInBackground}.
+ * <li>The memory effects of {@link #doInBackground} are visible to
+ * {@link #onPostExecute}.
+ * <li>Any memory effects of {@link #doInBackground} preceding a call
+ * to {@link #publishProgress} are visible to the corresponding
+ * {@link #onProgressUpdate} call. (But {@link #doInBackground} continues to
+ * run, and care needs to be taken that later updates in {@link #doInBackground}
+ * do not interfere with an in-progress {@link #onProgressUpdate} call.)
+ * <li>Any memory effects preceding a call to {@link #cancel} are visible
+ * after a call to {@link #isCancelled} that returns true as a result, or
+ * during and after a resulting call to {@link #onCancelled}.
* </ul>
*
* <h2>Order of execution</h2>
@@ -180,13 +191,19 @@ import java.util.concurrent.atomic.AtomicInteger;
public abstract class AsyncTask<Params, Progress, Result> {
private static final String LOG_TAG = "AsyncTask";
- private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
- // We want at least 2 threads and at most 4 threads in the core pool,
- // preferring to have 1 less than the CPU count to avoid saturating
- // the CPU with background work
- private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
- private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
- private static final int KEEP_ALIVE_SECONDS = 30;
+ // We keep only a single pool thread around all the time.
+ // We let the pool grow to a fairly large number of threads if necessary,
+ // but let them time out quickly. In the unlikely case that we run out of threads,
+ // we fall back to a simple unbounded-queue executor.
+ // This combination ensures that:
+ // 1. We normally keep few threads (1) around.
+ // 2. We queue only after launching a significantly larger, but still bounded, set of threads.
+ // 3. We keep the total number of threads bounded, but still allow an unbounded set
+ // of tasks to be queued.
+ private static final int CORE_POOL_SIZE = 1;
+ private static final int MAXIMUM_POOL_SIZE = 20;
+ private static final int BACKUP_POOL_SIZE = 5;
+ private static final int KEEP_ALIVE_SECONDS = 3;
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
@@ -196,8 +213,29 @@ public abstract class AsyncTask<Params, Progress, Result> {
}
};
- private static final BlockingQueue<Runnable> sPoolWorkQueue =
- new LinkedBlockingQueue<Runnable>(128);
+ // Used only for rejected executions.
+ // Initialization protected by sRunOnSerialPolicy lock.
+ private static ThreadPoolExecutor sBackupExecutor;
+ private static LinkedBlockingQueue<Runnable> sBackupExecutorQueue;
+
+ private static final RejectedExecutionHandler sRunOnSerialPolicy =
+ new RejectedExecutionHandler() {
+ public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
+ android.util.Log.w(LOG_TAG, "Exceeded ThreadPoolExecutor pool size");
+ // As a last ditch fallback, run it on an executor with an unbounded queue.
+ // Create this executor lazily, hopefully almost never.
+ synchronized (this) {
+ if (sBackupExecutor == null) {
+ sBackupExecutorQueue = new LinkedBlockingQueue<Runnable>();
+ sBackupExecutor = new ThreadPoolExecutor(
+ BACKUP_POOL_SIZE, BACKUP_POOL_SIZE, KEEP_ALIVE_SECONDS,
+ TimeUnit.SECONDS, sBackupExecutorQueue, sThreadFactory);
+ sBackupExecutor.allowCoreThreadTimeOut(true);
+ }
+ }
+ sBackupExecutor.execute(r);
+ }
+ };
/**
* An {@link Executor} that can be used to execute tasks in parallel.
@@ -207,8 +245,8 @@ public abstract class AsyncTask<Params, Progress, Result> {
static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
- sPoolWorkQueue, sThreadFactory);
- threadPoolExecutor.allowCoreThreadTimeOut(true);
+ new SynchronousQueue<Runnable>(), sThreadFactory);
+ threadPoolExecutor.setRejectedExecutionHandler(sRunOnSerialPolicy);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
@@ -388,6 +426,10 @@ public abstract class AsyncTask<Params, Progress, Result> {
* specified parameters are the parameters passed to {@link #execute}
* by the caller of this task.
*
+ * This will normally run on a background thread. But to better
+ * support testing frameworks, it is recommended that this also tolerates
+ * direct execution on the foreground thread, as part of the {@link #execute} call.
+ *
* This method can call {@link #publishProgress} to publish updates
* on the UI thread.
*
@@ -404,6 +446,8 @@ public abstract class AsyncTask<Params, Progress, Result> {
/**
* Runs on the UI thread before {@link #doInBackground}.
+ * Invoked directly by {@link #execute} or {@link #executeOnExecutor}.
+ * The default version does nothing.
*
* @see #onPostExecute
* @see #doInBackground
@@ -414,7 +458,10 @@ public abstract class AsyncTask<Params, Progress, Result> {
/**
* <p>Runs on the UI thread after {@link #doInBackground}. The
- * specified result is the value returned by {@link #doInBackground}.</p>
+ * specified result is the value returned by {@link #doInBackground}.
+ * To better support testing frameworks, it is recommended that this be
+ * written to tolerate direct execution as part of the execute() call.
+ * The default version does nothing.</p>
*
* <p>This method won't be invoked if the task was cancelled.</p>
*
@@ -432,6 +479,7 @@ public abstract class AsyncTask<Params, Progress, Result> {
/**
* Runs on the UI thread after {@link #publishProgress} is invoked.
* The specified values are the values passed to {@link #publishProgress}.
+ * The default version does nothing.
*
* @param values The values indicating progress.
*
@@ -466,7 +514,8 @@ public abstract class AsyncTask<Params, Progress, Result> {
/**
* <p>Applications should preferably override {@link #onCancelled(Object)}.
* This method is invoked by the default implementation of
- * {@link #onCancelled(Object)}.</p>
+ * {@link #onCancelled(Object)}.
+ * The default version does nothing.</p>
*
* <p>Runs on the UI thread after {@link #cancel(boolean)} is invoked and
* {@link #doInBackground(Object[])} has finished.</p>
@@ -504,12 +553,16 @@ public abstract class AsyncTask<Params, Progress, Result> {
* an attempt to stop the task.</p>
*
* <p>Calling this method will result in {@link #onCancelled(Object)} being
- * invoked on the UI thread after {@link #doInBackground(Object[])}
- * returns. Calling this method guarantees that {@link #onPostExecute(Object)}
- * is never invoked. After invoking this method, you should check the
- * value returned by {@link #isCancelled()} periodically from
- * {@link #doInBackground(Object[])} to finish the task as early as
- * possible.</p>
+ * invoked on the UI thread after {@link #doInBackground(Object[])} returns.
+ * Calling this method guarantees that onPostExecute(Object) is never
+ * subsequently invoked, even if <tt>cancel</tt> returns false, but
+ * {@link #onPostExecute} has not yet run. To finish the
+ * task as early as possible, check {@link #isCancelled()} periodically from
+ * {@link #doInBackground(Object[])}.</p>
+ *
+ * <p>This only requests cancellation. It never waits for a running
+ * background task to terminate, even if <tt>mayInterruptIfRunning</tt> is
+ * true.</p>
*
* @param mayInterruptIfRunning <tt>true</tt> if the thread executing this
* task should be interrupted; otherwise, in-progress tasks are allowed
diff --git a/core/java/android/os/BugreportManager.java b/core/java/android/os/BugreportManager.java
index 3a5b8a86204e..27f7e2296e7f 100644
--- a/core/java/android/os/BugreportManager.java
+++ b/core/java/android/os/BugreportManager.java
@@ -24,7 +24,8 @@ import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.content.Context;
-import android.os.IBinder.DeathRecipient;
+
+import com.android.internal.util.Preconditions;
import java.io.FileDescriptor;
import java.lang.annotation.Retention;
@@ -127,13 +128,16 @@ public class BugreportManager {
@NonNull BugreportParams params,
@NonNull @CallbackExecutor Executor executor,
@NonNull BugreportCallback callback) {
- // TODO(b/111441001): Enforce android.Manifest.permission.DUMP if necessary.
+ Preconditions.checkNotNull(bugreportFd);
+ Preconditions.checkNotNull(params);
+ Preconditions.checkNotNull(executor);
+ Preconditions.checkNotNull(callback);
DumpstateListener dsListener = new DumpstateListener(executor, callback);
try {
// Note: mBinder can get callingUid from the binder transaction.
mBinder.startBugreport(-1 /* callingUid */,
mContext.getOpPackageName(),
- (bugreportFd != null ? bugreportFd.getFileDescriptor() : new FileDescriptor()),
+ bugreportFd.getFileDescriptor(),
(screenshotFd != null
? screenshotFd.getFileDescriptor() : new FileDescriptor()),
params.getMode(), dsListener);
@@ -154,8 +158,7 @@ public class BugreportManager {
}
}
- private final class DumpstateListener extends IDumpstateListener.Stub
- implements DeathRecipient {
+ private final class DumpstateListener extends IDumpstateListener.Stub {
private final Executor mExecutor;
private final BugreportCallback mCallback;
@@ -165,11 +168,6 @@ public class BugreportManager {
}
@Override
- public void binderDied() {
- // TODO(b/111441001): implement
- }
-
- @Override
public void onProgress(int progress) throws RemoteException {
final long identity = Binder.clearCallingIdentity();
try {
diff --git a/core/java/android/os/DynamicAndroidManager.java b/core/java/android/os/DynamicAndroidManager.java
new file mode 100644
index 000000000000..5238896016ee
--- /dev/null
+++ b/core/java/android/os/DynamicAndroidManager.java
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2019 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.os;
+
+import android.annotation.RequiresPermission;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.gsi.GsiProgress;
+
+/**
+ * The DynamicAndroidManager offers a mechanism to use a new Android image temporarily. After the
+ * installation, the device can reboot into this image with a new created /data. This image will
+ * last until the next reboot and then the device will go back to the original image. However the
+ * installed image and the new created /data are not deleted but disabled. Thus the application can
+ * either re-enable the installed image by calling {@link #toggle} or use the {@link #remove} to
+ * delete it completely. In other words, there are three device states: no installation, installed
+ * and running. The procedure to install a DynamicAndroid starts with a {@link #startInstallation},
+ * followed by a series of {@link #write} and ends with a {@link commit}. Once the installation is
+ * complete, the device state changes from no installation to the installed state and a followed
+ * reboot will change its state to running. Note one instance of dynamic android can exist on a
+ * given device thus the {@link #startInstallation} will fail if the device is currently running a
+ * DynamicAndroid.
+ *
+ * @hide
+ */
+@SystemService(Context.DYNAMIC_ANDROID_SERVICE)
+public class DynamicAndroidManager {
+ private static final String TAG = "DynamicAndroidManager";
+
+ private final IDynamicAndroidService mService;
+
+ /** {@hide} */
+ public DynamicAndroidManager(IDynamicAndroidService service) {
+ mService = service;
+ }
+
+ /** The DynamicAndroidManager.Session represents a started session for the installation. */
+ public class Session {
+ private Session() {}
+ /**
+ * Write a chunk of the DynamicAndroid system image
+ *
+ * @return {@code true} if the call succeeds. {@code false} if there is any native runtime
+ * error.
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_ANDROID)
+ public boolean write(byte[] buf) {
+ try {
+ return mService.write(buf);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e.toString());
+ }
+ }
+
+ /**
+ * Finish write and make device to boot into the it after reboot.
+ *
+ * @return {@code true} if the call succeeds. {@code false} if there is any native runtime
+ * error.
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_ANDROID)
+ public boolean commit() {
+ try {
+ return mService.commit();
+ } catch (RemoteException e) {
+ throw new RuntimeException(e.toString());
+ }
+ }
+ }
+ /**
+ * Start DynamicAndroid installation. This call may take an unbounded amount of time. The caller
+ * may use another thread to call the getStartProgress() to get the progress.
+ *
+ * @param systemSize system size in bytes
+ * @param userdataSize userdata size in bytes
+ * @return {@code true} if the call succeeds. {@code false} either the device does not contain
+ * enough space or a DynamicAndroid is currently in use where the {@link #isInUse} would be
+ * true.
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_ANDROID)
+ public Session startInstallation(long systemSize, long userdataSize) {
+ try {
+ if (mService.startInstallation(systemSize, userdataSize)) {
+ return new Session();
+ } else {
+ return null;
+ }
+ } catch (RemoteException e) {
+ throw new RuntimeException(e.toString());
+ }
+ }
+
+ /**
+ * Query the progress of the current installation operation. This can be called while the
+ * installation is in progress.
+ *
+ * @return GsiProgress GsiProgress { int status; long bytes_processed; long total_bytes; } The
+ * status field can be IGsiService.STATUS_NO_OPERATION, IGsiService.STATUS_WORKING or
+ * IGsiService.STATUS_COMPLETE.
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_ANDROID)
+ public GsiProgress getInstallationProgress() {
+ try {
+ return mService.getInstallationProgress();
+ } catch (RemoteException e) {
+ throw new RuntimeException(e.toString());
+ }
+ }
+
+ /**
+ * Abort the installation process. Note this method must be called in a thread other than the
+ * one calling the startInstallation method as the startInstallation method will not return
+ * until it is finished.
+ *
+ * @return {@code true} if the call succeeds. {@code false} if there is no installation
+ * currently.
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_ANDROID)
+ public boolean abort() {
+ try {
+ return mService.abort();
+ } catch (RemoteException e) {
+ throw new RuntimeException(e.toString());
+ }
+ }
+
+ /** @return {@code true} if the device is running a dynamic android */
+ @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_ANDROID)
+ public boolean isInUse() {
+ try {
+ return mService.isInUse();
+ } catch (RemoteException e) {
+ throw new RuntimeException(e.toString());
+ }
+ }
+
+ /** @return {@code true} if the device has a dynamic android installed */
+ @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_ANDROID)
+ public boolean isInstalled() {
+ try {
+ return mService.isInstalled();
+ } catch (RemoteException e) {
+ throw new RuntimeException(e.toString());
+ }
+ }
+
+ /**
+ * Remove DynamicAndroid installation if present
+ *
+ * @return {@code true} if the call succeeds. {@code false} if there is no installed image.
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_ANDROID)
+ public boolean remove() {
+ try {
+ return mService.remove();
+ } catch (RemoteException e) {
+ throw new RuntimeException(e.toString());
+ }
+ }
+
+ /**
+ * Enable DynamicAndroid when it's not enabled, otherwise, disable it.
+ *
+ * @return {@code true} if the call succeeds. {@code false} if there is no installed image.
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_ANDROID)
+ public boolean toggle() {
+ try {
+ return mService.toggle();
+ } catch (RemoteException e) {
+ throw new RuntimeException(e.toString());
+ }
+ }
+}
diff --git a/core/java/android/os/IDynamicAndroidService.aidl b/core/java/android/os/IDynamicAndroidService.aidl
new file mode 100644
index 000000000000..0b28799c8dd0
--- /dev/null
+++ b/core/java/android/os/IDynamicAndroidService.aidl
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2019 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.os;
+
+import android.gsi.GsiProgress;
+
+/** {@hide} */
+interface IDynamicAndroidService
+{
+ /**
+ * Start DynamicAndroid installation. This call may take 60~90 seconds. The caller
+ * may use another thread to call the getStartProgress() to get the progress.
+ *
+ * @param systemSize system size in bytes
+ * @param userdataSize userdata size in bytes
+ * @return true if the call succeeds
+ */
+ boolean startInstallation(long systemSize, long userdataSize);
+
+ /**
+ * Query the progress of the current installation operation. This can be called while
+ * the installation is in progress.
+ *
+ * @return GsiProgress
+ */
+ GsiProgress getInstallationProgress();
+
+ /**
+ * Abort the installation process. Note this method must be called in a thread other
+ * than the one calling the startInstallation method as the startInstallation
+ * method will not return until it is finished.
+ *
+ * @return true if the call succeeds
+ */
+ boolean abort();
+
+ /**
+ * @return true if the device is running an DynamicAnroid image
+ */
+ boolean isInUse();
+
+ /**
+ * @return true if the device has an DynamicAndroid image installed
+ */
+ boolean isInstalled();
+
+ /**
+ * Remove DynamicAndroid installation if present
+ *
+ * @return true if the call succeeds
+ */
+ boolean remove();
+
+ /**
+ * Enable DynamicAndroid when it's not enabled, otherwise, disable it.
+ *
+ * @return true if the call succeeds
+ */
+ boolean toggle();
+
+ /**
+ * Write a chunk of the DynamicAndroid system image
+ *
+ * @return true if the call succeeds
+ */
+ boolean write(in byte[] buf);
+
+ /**
+ * Finish write and make device to boot into the it after reboot.
+ *
+ * @return true if the call succeeds
+ */
+ boolean commit();
+}
diff --git a/core/java/android/os/ParcelFileDescriptor.aidl b/core/java/android/os/ParcelFileDescriptor.aidl
deleted file mode 100644
index c07b98055d5a..000000000000
--- a/core/java/android/os/ParcelFileDescriptor.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/* //device/java/android/android/os/ParcelFileDescriptor.aidl
-**
-** Copyright 2007, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-package android.os;
-
-parcelable ParcelFileDescriptor cpp_header "binder/ParcelFileDescriptor.h";
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 8492b0cf58e2..3ee54ceebaa9 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -32,6 +32,7 @@ import android.content.pm.PackageManager;
import android.provider.Settings;
import android.telephony.euicc.EuiccManager;
import android.text.TextUtils;
+import android.text.format.DateFormat;
import android.util.Log;
import android.view.Display;
import android.view.WindowManager;
@@ -762,7 +763,8 @@ public class RecoverySystem {
String reasonArg = null;
if (!TextUtils.isEmpty(reason)) {
- reasonArg = "--reason=" + sanitizeArg(reason);
+ String timeStamp = DateFormat.format("yyyy-MM-ddTHH:mm:ssZ", System.currentTimeMillis()).toString();
+ reasonArg = "--reason=" + sanitizeArg(reason + "," + timeStamp);
}
final String localeArg = "--locale=" + Locale.getDefault().toLanguageTag() ;
diff --git a/core/java/android/os/UserHandle.java b/core/java/android/os/UserHandle.java
index 648c022d8673..de41ce2e08c5 100644
--- a/core/java/android/os/UserHandle.java
+++ b/core/java/android/os/UserHandle.java
@@ -226,6 +226,7 @@ public final class UserHandle implements Parcelable {
* @hide
*/
@TestApi
+ @SystemApi
public static @AppIdInt int getAppId(int uid) {
return uid % PER_USER_RANGE;
}
diff --git a/core/java/android/os/connectivity/CellularBatteryStats.java b/core/java/android/os/connectivity/CellularBatteryStats.java
index 2593c85cff12..c99ecb32d418 100644
--- a/core/java/android/os/connectivity/CellularBatteryStats.java
+++ b/core/java/android/os/connectivity/CellularBatteryStats.java
@@ -44,6 +44,7 @@ public final class CellularBatteryStats implements Parcelable {
private long[] mTimeInRatMs;
private long[] mTimeInRxSignalStrengthLevelMs;
private long[] mTxTimeMs;
+ private long mMonitoredRailChargeConsumedMaMs;
public static final Parcelable.Creator<CellularBatteryStats> CREATOR = new
Parcelable.Creator<CellularBatteryStats>() {
@@ -74,6 +75,7 @@ public final class CellularBatteryStats implements Parcelable {
out.writeLongArray(mTimeInRatMs);
out.writeLongArray(mTimeInRxSignalStrengthLevelMs);
out.writeLongArray(mTxTimeMs);
+ out.writeLong(mMonitoredRailChargeConsumedMaMs);
}
public void readFromParcel(Parcel in) {
@@ -90,6 +92,7 @@ public final class CellularBatteryStats implements Parcelable {
in.readLongArray(mTimeInRatMs);
in.readLongArray(mTimeInRxSignalStrengthLevelMs);
in.readLongArray(mTxTimeMs);
+ mMonitoredRailChargeConsumedMaMs = in.readLong();
}
public long getLoggingDurationMs() {
@@ -144,6 +147,10 @@ public final class CellularBatteryStats implements Parcelable {
return mTxTimeMs;
}
+ public long getMonitoredRailChargeConsumedMaMs() {
+ return mMonitoredRailChargeConsumedMaMs;
+ }
+
public void setLoggingDurationMs(long t) {
mLoggingDurationMs = t;
return;
@@ -211,6 +218,11 @@ public final class CellularBatteryStats implements Parcelable {
return;
}
+ public void setMonitoredRailChargeConsumedMaMs(long monitoredRailEnergyConsumedMaMs) {
+ mMonitoredRailChargeConsumedMaMs = monitoredRailEnergyConsumedMaMs;
+ return;
+ }
+
public int describeContents() {
return 0;
}
@@ -237,6 +249,7 @@ public final class CellularBatteryStats implements Parcelable {
Arrays.fill(mTimeInRxSignalStrengthLevelMs, 0);
mTxTimeMs = new long[ModemActivityInfo.TX_POWER_LEVELS];
Arrays.fill(mTxTimeMs, 0);
+ mMonitoredRailChargeConsumedMaMs = 0;
return;
}
} \ No newline at end of file
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index de54a8aa5548..f63c0adbdf4b 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -772,19 +772,6 @@ public class CallLog {
* @param callBlockReason The reason why the call is blocked.
* @param callScreeningAppName The call screening application name which block the call.
* @param callScreeningComponentName The call screening component name which block the call.
- * @param callIdPackageName The package name of the
- * {@link android.telecom.CallScreeningService} which provided
- * {@link CallIdentification}.
- * @param callIdAppName The app name of the {@link android.telecom.CallScreeningService}
- * which provided {@link CallIdentification}.
- * @param callIdName The caller name provided by the
- * {@link android.telecom.CallScreeningService}.
- * @param callIdDescription The caller description provided by the
- * {@link android.telecom.CallScreeningService}.
- * @param callIdDetails The caller details provided by the
- * {@link android.telecom.CallScreeningService}.
- * @param callIdCallType The caller type provided by the
- * {@link android.telecom.CallScreeningService}.
*
* @result The URI of the call log entry belonging to the user that made or received this
* call. This could be of the shadow provider. Do not return it to non-system apps,
@@ -803,37 +790,10 @@ public class CallLog {
number, userToBeInsertedTo, addForAllUsers));
}
final ContentResolver resolver = context.getContentResolver();
- int numberPresentation = PRESENTATION_ALLOWED;
- TelecomManager tm = null;
- try {
- tm = TelecomManager.from(context);
- } catch (UnsupportedOperationException e) {}
-
- String accountAddress = null;
- if (tm != null && accountHandle != null) {
- PhoneAccount account = tm.getPhoneAccount(accountHandle);
- if (account != null) {
- Uri address = account.getSubscriptionAddress();
- if (address != null) {
- accountAddress = address.getSchemeSpecificPart();
- }
- }
- }
+ String accountAddress = getLogAccountAddress(context, accountHandle);
- // Remap network specified number presentation types
- // PhoneConstants.PRESENTATION_xxx to calllog number presentation types
- // Calls.PRESENTATION_xxx, in order to insulate the persistent calllog
- // from any future radio changes.
- // If the number field is empty set the presentation type to Unknown.
- if (presentation == PhoneConstants.PRESENTATION_RESTRICTED) {
- numberPresentation = PRESENTATION_RESTRICTED;
- } else if (presentation == PhoneConstants.PRESENTATION_PAYPHONE) {
- numberPresentation = PRESENTATION_PAYPHONE;
- } else if (TextUtils.isEmpty(number)
- || presentation == PhoneConstants.PRESENTATION_UNKNOWN) {
- numberPresentation = PRESENTATION_UNKNOWN;
- }
+ int numberPresentation = getLogNumberPresentation(number, presentation);
if (numberPresentation != PRESENTATION_ALLOWED) {
number = "";
if (ci != null) {
@@ -1138,8 +1098,7 @@ public class CallLog {
if (TextUtils.isEmpty(countryIso)) {
return;
}
- final String normalizedNumber = PhoneNumberUtils.formatNumberToE164(number,
- getCurrentCountryIso(context));
+ final String normalizedNumber = PhoneNumberUtils.formatNumberToE164(number, countryIso);
if (TextUtils.isEmpty(normalizedNumber)) {
return;
}
@@ -1148,6 +1107,54 @@ public class CallLog {
resolver.update(Data.CONTENT_URI, values, Data._ID + "=?", new String[] {dataId});
}
+ /**
+ * Remap network specified number presentation types
+ * PhoneConstants.PRESENTATION_xxx to calllog number presentation types
+ * Calls.PRESENTATION_xxx, in order to insulate the persistent calllog
+ * from any future radio changes.
+ * If the number field is empty set the presentation type to Unknown.
+ */
+ private static int getLogNumberPresentation(String number, int presentation) {
+ if (presentation == PhoneConstants.PRESENTATION_RESTRICTED) {
+ return presentation;
+ }
+
+ if (presentation == PhoneConstants.PRESENTATION_PAYPHONE) {
+ return presentation;
+ }
+
+ if (TextUtils.isEmpty(number)
+ || presentation == PhoneConstants.PRESENTATION_UNKNOWN) {
+ return PRESENTATION_UNKNOWN;
+ }
+
+ return PRESENTATION_ALLOWED;
+ }
+
+ private static String getLogAccountAddress(Context context,
+ PhoneAccountHandle accountHandle) {
+ TelecomManager tm = null;
+ try {
+ tm = TelecomManager.from(context);
+ } catch (UnsupportedOperationException e) {
+ if (VERBOSE_LOG) {
+ Log.v(LOG_TAG, "No TelecomManager found to get account address.");
+ }
+ }
+
+ String accountAddress = null;
+ if (tm != null && accountHandle != null) {
+ PhoneAccount account = tm.getPhoneAccount(accountHandle);
+ if (account != null) {
+ Uri address = account.getSubscriptionAddress();
+ if (address != null) {
+ accountAddress = address.getSchemeSpecificPart();
+ }
+ }
+ }
+ return accountAddress;
+ }
+
private static String getCurrentCountryIso(Context context) {
String countryIso = null;
final CountryDetector detector = (CountryDetector) context.getSystemService(
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index e904b0713e24..abd7c89a0301 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -12862,6 +12862,19 @@ public final class Settings {
"user_preferred_sub2","user_preferred_sub3"};
/**
+ * Which subscription is enabled for a physical slot.
+ * @hide
+ */
+ public static final String ENABLED_SUBSCRIPTION_FOR_SLOT = "enabled_subscription_for_slot";
+
+ /**
+ * Whether corresponding logical modem is enabled for a physical slot.
+ * The value 1 - enable, 0 - disable
+ * @hide
+ */
+ public static final String MODEM_STACK_ENABLED_FOR_SLOT = "modem_stack_enabled_for_slot";
+
+ /**
* Whether to enable new contacts aggregator or not.
* The value 1 - enable, 0 - disable
* @hide
diff --git a/core/java/android/service/euicc/EuiccService.java b/core/java/android/service/euicc/EuiccService.java
index b84e556cc552..53cc25b14285 100644
--- a/core/java/android/service/euicc/EuiccService.java
+++ b/core/java/android/service/euicc/EuiccService.java
@@ -76,6 +76,11 @@ import java.util.concurrent.atomic.AtomicInteger;
* filter with the appropriate action, the {@link #CATEGORY_EUICC_UI} category, and a non-zero
* priority.
*
+ * <p>Old implementations of EuiccService may support passing in slot IDs equal to
+ * {@link android.telephony.SubscriptionManager#INVALID_SIM_SLOT_INDEX}, which allows the LPA to
+ * decide which eUICC to target when there are multiple eUICCs. This behavior is not supported in
+ * Android Q or later.
+ *
* @hide
*/
@SystemApi
@@ -520,7 +525,7 @@ public abstract class EuiccService extends Service {
int resultCode = EuiccService.this.onDownloadSubscription(
slotId, subscription, switchAfterDownload, forceDeactivateSim);
result = new DownloadSubscriptionResult(resultCode,
- 0 /* resolvableErrors */, TelephonyManager.INVALID_CARD_ID);
+ 0 /* resolvableErrors */, TelephonyManager.UNSUPPORTED_CARD_ID);
}
try {
callback.onComplete(result);
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index fda0a3ee4147..15c14e7bd1ef 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -30,8 +30,7 @@ import android.icu.text.Edits;
import android.icu.util.ULocale;
import android.os.Parcel;
import android.os.Parcelable;
-import android.os.SystemProperties;
-import android.provider.Settings;
+import android.sysprop.DisplayProperties;
import android.text.style.AbsoluteSizeSpan;
import android.text.style.AccessibilityClickableSpan;
import android.text.style.AccessibilityURLSpan;
@@ -2001,7 +2000,7 @@ public class TextUtils {
return ((locale != null && !locale.equals(Locale.ROOT)
&& ULocale.forLocale(locale).isRightToLeft())
// If forcing into RTL layout mode, return RTL as default
- || SystemProperties.getBoolean(Settings.Global.DEVELOPMENT_FORCE_RTL, false))
+ || DisplayProperties.debug_force_rtl().orElse(false))
? View.LAYOUT_DIRECTION_RTL
: View.LAYOUT_DIRECTION_LTR;
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f228773aebf2..efff6d617376 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -74,8 +74,8 @@ import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.os.SystemProperties;
import android.os.Trace;
+import android.sysprop.DisplayProperties;
import android.text.InputType;
import android.text.TextUtils;
import android.util.AttributeSet;
@@ -790,14 +790,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
protected static final String VIEW_LOG_TAG = "View";
/**
- * When set to true, apps will draw debugging information about their layouts.
- *
- * @hide
- */
- @UnsupportedAppUsage
- public static final String DEBUG_LAYOUT_PROPERTY = "debug.layout";
-
- /**
* When set to true, this view will save its attribute data.
*
* @hide
@@ -26855,7 +26847,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
/**
* Show where the margins, bounds and layout bounds are for each view.
*/
- boolean mDebugLayout = SystemProperties.getBoolean(DEBUG_LAYOUT_PROPERTY, false);
+ boolean mDebugLayout = DisplayProperties.debug_layout().orElse(false);
/**
* Point used to compute visible regions.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index fd11ef13f9ac..798401d87b5a 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -70,6 +70,7 @@ import android.os.RemoteException;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.Trace;
+import android.sysprop.DisplayProperties;
import android.util.AndroidRuntimeException;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -6829,7 +6830,7 @@ public final class ViewRootImpl implements ViewParent,
}
// Layout debugging
- boolean layout = SystemProperties.getBoolean(View.DEBUG_LAYOUT_PROPERTY, false);
+ boolean layout = DisplayProperties.debug_layout().orElse(false);
if (layout != mAttachInfo.mDebugLayout) {
mAttachInfo.mDebugLayout = layout;
if (!mHandler.hasMessages(MSG_INVALIDATE_WORLD)) {
diff --git a/core/java/android/view/accessibility/OWNERS b/core/java/android/view/accessibility/OWNERS
new file mode 100644
index 000000000000..265674a74b7e
--- /dev/null
+++ b/core/java/android/view/accessibility/OWNERS
@@ -0,0 +1,3 @@
+svetoslavganov@google.com
+pweaver@google.com
+rhedjao@google.com
diff --git a/core/java/android/webkit/OWNERS b/core/java/android/webkit/OWNERS
index 00e540a46ab2..b33da57c42e3 100644
--- a/core/java/android/webkit/OWNERS
+++ b/core/java/android/webkit/OWNERS
@@ -1,3 +1,4 @@
changwan@google.com
+ntfschr@google.com
tobiasjs@google.com
torne@google.com
diff --git a/core/java/com/android/internal/net/VpnConfig.java b/core/java/com/android/internal/net/VpnConfig.java
index da8605e645b4..65b974ba8b42 100644
--- a/core/java/com/android/internal/net/VpnConfig.java
+++ b/core/java/com/android/internal/net/VpnConfig.java
@@ -104,6 +104,7 @@ public class VpnConfig implements Parcelable {
public boolean allowBypass;
public boolean allowIPv4;
public boolean allowIPv6;
+ public boolean isMetered = true;
public Network[] underlyingNetworks;
public ProxyInfo proxyInfo;
@@ -165,6 +166,7 @@ public class VpnConfig implements Parcelable {
out.writeInt(allowBypass ? 1 : 0);
out.writeInt(allowIPv4 ? 1 : 0);
out.writeInt(allowIPv6 ? 1 : 0);
+ out.writeInt(isMetered ? 1 : 0);
out.writeTypedArray(underlyingNetworks, flags);
out.writeParcelable(proxyInfo, flags);
}
@@ -191,6 +193,7 @@ public class VpnConfig implements Parcelable {
config.allowBypass = in.readInt() != 0;
config.allowIPv4 = in.readInt() != 0;
config.allowIPv6 = in.readInt() != 0;
+ config.isMetered = in.readInt() != 0;
config.underlyingNetworks = in.createTypedArray(Network.CREATOR);
config.proxyInfo = in.readParcelable(null);
return config;
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 1048cb4e6e3a..56eb128558e8 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -93,6 +93,11 @@ public final class Zygote {
*/
public static final int PROFILE_SYSTEM_SERVER = 1 << 14;
+ /*
+ * Enable using the ART app image startup cache
+ */
+ public static final int USE_APP_IMAGE_STARTUP_CACHE = 1 << 16;
+
/** No external storage should be mounted. */
public static final int MOUNT_EXTERNAL_NONE = IVold.REMOUNT_MODE_NONE;
/** Default external storage should be mounted. */
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 2bb075989f35..d067ae792e2d 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -315,9 +315,14 @@ class ZygoteConnection {
}
}
- private class HiddenApiUsageLogger implements VMRuntime.HiddenApiUsageLogger {
+ private static class HiddenApiUsageLogger implements VMRuntime.HiddenApiUsageLogger {
private final MetricsLogger mMetricsLogger = new MetricsLogger();
+ private static HiddenApiUsageLogger sInstance = new HiddenApiUsageLogger();
+
+ public static HiddenApiUsageLogger getInstance() {
+ return HiddenApiUsageLogger.sInstance;
+ }
public void hiddenApiUsed(String packageName, String signature,
int accessMethod, boolean accessDenied) {
@@ -351,7 +356,7 @@ class ZygoteConnection {
private void handleHiddenApiAccessLogSampleRate(int samplingRate) {
try {
ZygoteInit.setHiddenApiAccessLogSampleRate(samplingRate);
- ZygoteInit.setHiddenApiUsageLogger(new HiddenApiUsageLogger());
+ ZygoteInit.setHiddenApiUsageLogger(HiddenApiUsageLogger.getInstance());
mSocketOutStream.writeInt(0);
} catch (IOException ioe) {
throw new IllegalStateException("Error writing to command socket", ioe);
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 9f23797d6ccc..e132abd7e4cb 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -41,6 +41,7 @@ import android.system.OsConstants;
import android.system.StructCapUserData;
import android.system.StructCapUserHeader;
import android.text.Hyphenator;
+import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
@@ -84,6 +85,8 @@ public class ZygoteInit {
private static final String PROPERTY_DISABLE_OPENGL_PRELOADING = "ro.zygote.disable_gl_preload";
private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0";
+ private static final String PROPERTY_USE_APP_IMAGE_STARTUP_CACHE =
+ "persist.device_config.runtime_native.use_app_image_startup_cache";
private static final int LOG_BOOT_PROGRESS_PRELOAD_START = 3020;
private static final int LOG_BOOT_PROGRESS_PRELOAD_END = 3030;
@@ -705,6 +708,13 @@ public class ZygoteInit {
parsedArgs.mRuntimeFlags |= Zygote.PROFILE_SYSTEM_SERVER;
}
+ String use_app_image_cache = SystemProperties.get(
+ PROPERTY_USE_APP_IMAGE_STARTUP_CACHE, "");
+ // Property defaults to true currently.
+ if (!TextUtils.isEmpty(use_app_image_cache) && !use_app_image_cache.equals("false")) {
+ parsedArgs.mRuntimeFlags |= Zygote.USE_APP_IMAGE_STARTUP_CACHE;
+ }
+
/* Request to fork the system server process */
pid = Zygote.forkSystemServer(
parsedArgs.mUid, parsedArgs.mGid,
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index c81a77d4c16e..c385ca11611c 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -41,7 +41,7 @@ cc_library_shared {
"com_google_android_gles_jni_EGLImpl.cpp",
"com_google_android_gles_jni_GLImpl.cpp", // TODO: .arm
"android_app_Activity.cpp",
- "android_app_ActivityThread.cpp",
+ "android_app_ActivityThread.cpp",
"android_app_NativeActivity.cpp",
"android_app_admin_SecurityLog.cpp",
"android_opengl_EGL14.cpp",
@@ -225,6 +225,7 @@ cc_library_shared {
],
static_libs: [
+ "libasync_safe",
"libgif",
"libseccomp_policy",
"libgrallocusage",
@@ -278,8 +279,8 @@ cc_library_shared {
"libsoundtrigger",
"libminikin",
"libprocessgroup",
- "libnativebridge",
- "libnativeloader",
+ "libnativebridge_lazy",
+ "libnativeloader_lazy",
"libmemunreachable",
"libhidlbase",
"libhidltransport",
diff --git a/core/jni/OWNERS b/core/jni/OWNERS
index 86342c47c455..7ff15f2e182d 100644
--- a/core/jni/OWNERS
+++ b/core/jni/OWNERS
@@ -3,7 +3,7 @@ per-file *Camera*,*camera* = cychen@google.com, epeev@google.com, etalvala@googl
per-file *Camera*,*camera* = shuzhenwang@google.com, yinchiayeh@google.com, zhijunhe@google.com
# Connectivity
-per-file android_net_* = ek@google.com, lorenzo@google.com, satk@google.com
+per-file android_net_* = codewiz@google.com, jchalard@google.com, lorenzo@google.com, reminv@google.com, satk@google.com
# Zygote
-per-file com_android_inernal_os_Zygote.*,fd_utils.* = chriswailes@google.com, ngeoffray@google.com, sehr@google.com, narayan@google.com, maco@google.com
+per-file com_android_internal_os_Zygote.*,fd_utils.* = chriswailes@google.com, ngeoffray@google.com, sehr@google.com, narayan@google.com, maco@google.com
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 7eddcfe425d3..cfb2dd199f39 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -29,6 +29,7 @@
#include <net/if.h>
#include <linux/filter.h>
#include <linux/if_arp.h>
+#include <linux/tcp.h>
#include <netinet/ether.h>
#include <netinet/icmp6.h>
#include <netinet/ip.h>
@@ -226,6 +227,34 @@ static void android_net_utils_attachControlPacketFilter(
}
}
+static void android_net_utils_attachDropAllBPFFilter(JNIEnv *env, jobject clazz, jobject javaFd)
+{
+ struct sock_filter filter_code[] = {
+ // Reject all.
+ BPF_STMT(BPF_RET | BPF_K, 0)
+ };
+ struct sock_fprog filter = {
+ sizeof(filter_code) / sizeof(filter_code[0]),
+ filter_code,
+ };
+
+ int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) {
+ jniThrowExceptionFmt(env, "java/net/SocketException",
+ "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno));
+ }
+}
+
+static void android_net_utils_detachBPFFilter(JNIEnv *env, jobject clazz, jobject javaFd)
+{
+ int dummy = 0;
+ int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ if (setsockopt(fd, SOL_SOCKET, SO_DETACH_FILTER, &dummy, sizeof(dummy)) != 0) {
+ jniThrowExceptionFmt(env, "java/net/SocketException",
+ "setsockopt(SO_DETACH_FILTER): %s", strerror(errno));
+ }
+
+}
static void android_net_utils_setupRaSocket(JNIEnv *env, jobject clazz, jobject javaFd,
jint ifIndex)
{
@@ -458,6 +487,41 @@ static jbyteArray android_net_utils_resNetworkResult(JNIEnv *env, jobject thiz,
return answer;
}
+static jobject android_net_utils_getTcpRepairWindow(JNIEnv *env, jobject thiz, jobject javaFd) {
+ if (javaFd == NULL) {
+ jniThrowNullPointerException(env, NULL);
+ return NULL;
+ }
+
+ int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ struct tcp_repair_window trw = {};
+ socklen_t size = sizeof(trw);
+
+ // Obtain the parameters of the TCP repair window.
+ int rc = getsockopt(fd, IPPROTO_TCP, TCP_REPAIR_WINDOW, &trw, &size);
+ if (rc == -1) {
+ throwErrnoException(env, "getsockopt : TCP_REPAIR_WINDOW", errno);
+ return NULL;
+ }
+
+ struct tcp_info tcpinfo = {};
+ socklen_t tcpinfo_size = sizeof(tcp_info);
+
+ // Obtain the window scale from the tcp info structure. This contains a scale factor that
+ // should be applied to the window size.
+ rc = getsockopt(fd, IPPROTO_TCP, TCP_INFO, &tcpinfo, &tcpinfo_size);
+ if (rc == -1) {
+ throwErrnoException(env, "getsockopt : TCP_INFO", errno);
+ return NULL;
+ }
+
+ jclass class_TcpRepairWindow = env->FindClass("android/net/TcpRepairWindow");
+ jmethodID ctor = env->GetMethodID(class_TcpRepairWindow, "<init>", "(IIIIII)V");
+
+ return env->NewObject(class_TcpRepairWindow, ctor, trw.snd_wl1, trw.snd_wnd, trw.max_window,
+ trw.rcv_wnd, trw.rcv_wup, tcpinfo.tcpi_rcv_wscale);
+}
+
// ----------------------------------------------------------------------------
/*
@@ -475,6 +539,9 @@ static const JNINativeMethod gNetworkUtilMethods[] = {
{ "attachDhcpFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDhcpFilter },
{ "attachRaFilter", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_attachRaFilter },
{ "attachControlPacketFilter", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_attachControlPacketFilter },
+ { "attachDropAllBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDropAllBPFFilter },
+ { "detachBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_detachBPFFilter },
+ { "getTcpRepairWindow", "(Ljava/io/FileDescriptor;)Landroid/net/TcpRepairWindow;", (void*) android_net_utils_getTcpRepairWindow },
{ "setupRaSocket", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_setupRaSocket },
{ "resNetworkSend", "(I[BII)Ljava/io/FileDescriptor;", (void*) android_net_utils_resNetworkSend },
{ "resNetworkQuery", "(ILjava/lang/String;III)Ljava/io/FileDescriptor;", (void*) android_net_utils_resNetworkQuery },
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 978254175da4..32ddad1791f2 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -346,30 +346,34 @@ static void parse_cpuset_cpus(char *cpus, cpu_set_t *cpu_set) {
static void get_cpuset_cores_for_policy(SchedPolicy policy, cpu_set_t *cpu_set)
{
FILE *file;
- const char *filename;
+ std::string filename;
CPU_ZERO(cpu_set);
switch (policy) {
case SP_BACKGROUND:
- filename = "/dev/cpuset/background/cpus";
+ if (!CgroupGetAttributePath("LowCapacityCPUs", &filename)) {
+ return;
+ }
break;
case SP_FOREGROUND:
case SP_AUDIO_APP:
case SP_AUDIO_SYS:
case SP_RT_APP:
- filename = "/dev/cpuset/foreground/cpus";
+ if (!CgroupGetAttributePath("HighCapacityCPUs", &filename)) {
+ return;
+ }
break;
case SP_TOP_APP:
- filename = "/dev/cpuset/top-app/cpus";
+ if (!CgroupGetAttributePath("MaxCapacityCPUs", &filename)) {
+ return;
+ }
break;
default:
- filename = NULL;
+ return;
}
- if (!filename) return;
-
- file = fopen(filename, "re");
+ file = fopen(filename.c_str(), "re");
if (file != NULL) {
// Parse cpus string
char *line = NULL;
@@ -379,7 +383,7 @@ static void get_cpuset_cores_for_policy(SchedPolicy policy, cpu_set_t *cpu_set)
if (num_read > 0) {
parse_cpuset_cpus(line, cpu_set);
} else {
- ALOGE("Failed to read %s", filename);
+ ALOGE("Failed to read %s", filename.c_str());
}
free(line);
}
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 1448d7b97eb1..bc1332adacbc 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -25,6 +25,8 @@
#define LOG_TAG "Zygote"
+#include <async_safe/log.h>
+
// sys/mount.h has to come before linux/fs.h due to redefinition of MS_RDONLY, MS_BIND, etc
#include <sys/mount.h>
#include <linux/fs.h>
@@ -296,27 +298,23 @@ static void SigChldHandler(int /*signal_number*/) {
int saved_errno = errno;
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
- // Log process-death status that we care about. In general it is
- // not safe to call LOG(...) from a signal handler because of
- // possible reentrancy. However, we know a priori that the
- // current implementation of LOG() is safe to call from a SIGCHLD
- // handler in the zygote process. If the LOG() implementation
- // changes its locking strategy or its use of syscalls within the
- // lazy-init critical section, its use here may become unsafe.
+ // Log process-death status that we care about.
if (WIFEXITED(status)) {
- ALOGI("Process %d exited cleanly (%d)", pid, WEXITSTATUS(status));
+ async_safe_format_log(ANDROID_LOG_INFO, LOG_TAG,
+ "Process %d exited cleanly (%d)", pid, WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
- ALOGI("Process %d exited due to signal (%d)", pid, WTERMSIG(status));
- if (WCOREDUMP(status)) {
- ALOGI("Process %d dumped core.", pid);
- }
+ async_safe_format_log(ANDROID_LOG_INFO, LOG_TAG,
+ "Process %d exited due to signal %d (%s)%s", pid,
+ WTERMSIG(status), strsignal(WTERMSIG(status)),
+ WCOREDUMP(status) ? "; core dumped" : "");
}
// If the just-crashed process is the system_server, bring down zygote
// so that it is restarted by init and system server will be restarted
// from there.
if (pid == gSystemServerPid) {
- ALOGE("Exit zygote because system server (%d) has terminated", pid);
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+ "Exit zygote because system server (pid %d) has terminated", pid);
kill(getpid(), SIGKILL);
}
@@ -329,14 +327,17 @@ static void SigChldHandler(int /*signal_number*/) {
// Note that we shouldn't consider ECHILD an error because
// the secondary zygote might have no children left to wait for.
if (pid < 0 && errno != ECHILD) {
- ALOGW("Zygote SIGCHLD error in waitpid: %s", strerror(errno));
+ async_safe_format_log(ANDROID_LOG_WARN, LOG_TAG,
+ "Zygote SIGCHLD error in waitpid: %s", strerror(errno));
}
if (blastulas_removed > 0) {
if (write(gBlastulaPoolEventFD, &blastulas_removed, sizeof(blastulas_removed)) == -1) {
// If this write fails something went terribly wrong. We will now kill
// the zygote and let the system bring it back up.
- ALOGE("Zygote failed to write to blastula pool event FD: %s", strerror(errno));
+ async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
+ "Zygote failed to write to blastula pool event FD: %s",
+ strerror(errno));
kill(getpid(), SIGKILL);
}
}
@@ -1115,8 +1116,8 @@ static jlong CalculateCapabilities(JNIEnv* env, jint uid, jint gid, jintArray gi
RuntimeAbort(env, __LINE__, "Bad gids array");
}
- for (int gid_index = gids_num; --gids_num >= 0;) {
- if (native_gid_proxy[gid_index] == AID_WAKELOCK) {
+ for (int gids_index = 0; gids_index < gids_num; ++gids_index) {
+ if (native_gid_proxy[gids_index] == AID_WAKELOCK) {
gid_wakelock_found = true;
break;
}
@@ -1305,15 +1306,12 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer(
RuntimeAbort(env, __LINE__, "System server process has died. Restarting Zygote!");
}
- bool low_ram_device = GetBoolProperty("ro.config.low_ram", false);
- bool per_app_memcg = GetBoolProperty("ro.config.per_app_memcg", low_ram_device);
- if (per_app_memcg) {
+ if (UsePerAppMemcg()) {
// Assign system_server to the correct memory cgroup.
- // Not all devices mount /dev/memcg so check for the file first
+ // Not all devices mount memcg so check if it is mounted first
// to avoid unnecessarily printing errors and denials in the logs.
- if (!access("/dev/memcg/system/tasks", F_OK) &&
- !WriteStringToFile(StringPrintf("%d", pid), "/dev/memcg/system/tasks")) {
- ALOGE("couldn't write %d to /dev/memcg/system/tasks", pid);
+ if (!SetTaskProfiles(pid, std::vector<std::string>{"SystemMemoryProcess"})) {
+ ALOGE("couldn't add process %d into system memcg group", pid);
}
}
}
diff --git a/core/proto/android/bluetooth/enums.proto b/core/proto/android/bluetooth/enums.proto
index a88a06cf091c..b4f3d1ea5ae4 100644
--- a/core/proto/android/bluetooth/enums.proto
+++ b/core/proto/android/bluetooth/enums.proto
@@ -110,3 +110,31 @@ enum UnbondReasonEnum {
UNBOND_REASON_REMOTE_AUTH_CANCELED = 8;
UNBOND_REASON_REMOVED = 9;
}
+
+enum SocketTypeEnum {
+ SOCKET_TYPE_UNKNOWN = 0;
+ SOCKET_TYPE_RFCOMM = 1;
+ SOCKET_TYPE_SCO = 2;
+ SOCKET_TYPE_L2CAP_BREDR = 3;
+ SOCKET_TYPE_L2CAP_LE = 4;
+}
+
+enum SocketConnectionstateEnum {
+ SOCKET_CONNECTION_STATE_UNKNOWN = 0;
+ // Socket acts as a server waiting for connection
+ SOCKET_CONNECTION_STATE_LISTENING = 1;
+ // Socket acts as a client trying to connect
+ SOCKET_CONNECTION_STATE_CONNECTING = 2;
+ // Socket is connected
+ SOCKET_CONNECTION_STATE_CONNECTED = 3;
+ // Socket tries to disconnect from remote
+ SOCKET_CONNECTION_STATE_DISCONNECTING = 4;
+ // This socket is closed
+ SOCKET_CONNECTION_STATE_DISCONNECTED = 5;
+}
+
+enum SocketRoleEnum {
+ SOCKET_ROLE_UNKNOWN = 0;
+ SOCKET_ROLE_LISTEN = 1;
+ SOCKET_ROLE_CONNECTION = 2;
+}
diff --git a/core/proto/android/server/connectivity/Android.bp b/core/proto/android/server/connectivity/Android.bp
new file mode 100644
index 000000000000..c0ac2cb8f800
--- /dev/null
+++ b/core/proto/android/server/connectivity/Android.bp
@@ -0,0 +1,25 @@
+// Copyright (C) 2019 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.
+
+java_library_static {
+ name: "datastallprotosnano",
+ proto: {
+ type: "nano",
+ },
+ srcs: [
+ "data_stall_event.proto",
+ ],
+ sdk_version: "system_current",
+ no_framework_libs: true,
+} \ No newline at end of file
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 2f3c1db4f775..7590675a9f9d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -481,6 +481,7 @@
<protected-broadcast android:name="android.security.action.TRUST_STORE_CHANGED" />
<protected-broadcast android:name="android.security.action.KEYCHAIN_CHANGED" />
<protected-broadcast android:name="android.security.action.KEY_ACCESS_CHANGED" />
+ <protected-broadcast android:name="android.telecom.action.NUISANCE_CALL_STATUS_CHANGED" />
<protected-broadcast android:name="android.telecom.action.PHONE_ACCOUNT_REGISTERED" />
<protected-broadcast android:name="android.telecom.action.PHONE_ACCOUNT_UNREGISTERED" />
<protected-broadcast android:name="android.telecom.action.SHOW_MISSED_CALLS_NOTIFICATION" />
@@ -1554,7 +1555,7 @@
<permission android:name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"
android:protectionLevel="signature|privileged" />
- <!-- Allows a system application to access hardware packet offload capabilities.
+ <!-- @SystemApi Allows a system application to access hardware packet offload capabilities.
@hide -->
<permission android:name="android.permission.PACKET_KEEPALIVE_OFFLOAD"
android:protectionLevel="signature|privileged" />
@@ -1681,6 +1682,10 @@
<permission android:name="android.permission.HARDWARE_TEST"
android:protectionLevel="signature" />
+ <!-- @hide Allows an application to manage DynamicAndroid image -->
+ <permission android:name="android.permission.MANAGE_DYNAMIC_ANDROID"
+ android:protectionLevel="signature" />
+
<!-- @SystemApi Allows access to Broadcast Radio
@hide This is not a third-party API (intended for system apps).-->
<permission android:name="android.permission.ACCESS_BROADCAST_RADIO"
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 212c7235f27b..94ba172fc676 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -505,7 +505,9 @@ public class SettingsBackupTest {
Settings.Global.OVERRIDE_SETTINGS_PROVIDER_RESTORE_ANY_VERSION,
Settings.Global.CHAINED_BATTERY_ATTRIBUTION_ENABLED,
Settings.Global.HIDDEN_API_BLACKLIST_EXEMPTIONS,
- Settings.Global.BACKUP_AGENT_TIMEOUT_PARAMETERS);
+ Settings.Global.BACKUP_AGENT_TIMEOUT_PARAMETERS,
+ Settings.Global.ENABLED_SUBSCRIPTION_FOR_SLOT,
+ Settings.Global.MODEM_STACK_ENABLED_FOR_SLOT);
private static final Set<String> BACKUP_BLACKLISTED_SECURE_SETTINGS =
newHashSet(
Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index 035ee108c6da..bb4765835890 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -58,6 +58,14 @@ prebuilt_etc {
}
prebuilt_etc {
+ name: "privapp_whitelist_com.android.dialer",
+ product_specific: true,
+ sub_dir: "permissions",
+ src: "com.android.dialer.xml",
+ filename_from_src: true,
+}
+
+prebuilt_etc {
name: "privapp_whitelist_com.android.launcher3",
product_specific: true,
sub_dir: "permissions",
diff --git a/data/etc/com.android.dialer.xml b/data/etc/com.android.dialer.xml
new file mode 100644
index 000000000000..ccdb21fa5040
--- /dev/null
+++ b/data/etc/com.android.dialer.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 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
+ -->
+<permissions>
+ <privapp-permissions package="com.android.dialer">
+ <permission name="android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK"/>
+ <permission name="android.permission.CONTROL_INCALL_EXPERIENCE"/>
+ <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
+ <permission name="android.permission.MODIFY_PHONE_STATE"/>
+ <permission name="android.permission.STATUS_BAR"/>
+ <permission name="android.permission.STOP_APP_SWITCHES"/>
+ <permission name="com.android.voicemail.permission.READ_VOICEMAIL"/>
+ <permission name="com.android.voicemail.permission.WRITE_VOICEMAIL"/>
+ </privapp-permissions>
+</permissions>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index c9f0f108ab44..78bbcf137f2a 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -48,17 +48,6 @@ applications that come with the platform
<permission name="android.permission.WRITE_MEDIA_STORAGE"/>
</privapp-permissions>
- <privapp-permissions package="com.android.dialer">
- <permission name="android.permission.ALLOW_ANY_CODEC_FOR_PLAYBACK"/>
- <permission name="android.permission.CONTROL_INCALL_EXPERIENCE"/>
- <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
- <permission name="android.permission.MODIFY_PHONE_STATE"/>
- <permission name="android.permission.STATUS_BAR"/>
- <permission name="android.permission.STOP_APP_SWITCHES"/>
- <permission name="com.android.voicemail.permission.READ_VOICEMAIL"/>
- <permission name="com.android.voicemail.permission.WRITE_VOICEMAIL"/>
- </privapp-permissions>
-
<privapp-permissions package="com.android.emergency">
<!-- Required to place emergency calls from emergency info screen. -->
<permission name="android.permission.CALL_PRIVILEGED"/>
@@ -169,6 +158,7 @@ applications that come with the platform
<permission name="android.permission.WRITE_APN_SETTINGS"/>
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
<permission name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"/>
+ <permission name="android.permission.READ_PRECISE_PHONE_STATE"/>
<permission name="com.android.voicemail.permission.READ_VOICEMAIL"/>
<permission name="com.android.voicemail.permission.WRITE_VOICEMAIL"/>
</privapp-permissions>
@@ -210,23 +200,16 @@ applications that come with the platform
<permission name="android.permission.USE_RESERVED_DISK"/>
</privapp-permissions>
- <privapp-permissions package="com.android.mainline.networkstack">
+ <privapp-permissions package="com.android.networkstack">
<permission name="android.permission.ACCESS_NETWORK_CONDITIONS"/>
- <permission name="android.permission.CHANGE_BACKGROUND_DATA_SETTING"/>
<permission name="android.permission.CONNECTIVITY_INTERNAL"/>
<permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/>
<permission name="android.permission.CONTROL_VPN"/>
+ <permission name="android.permission.INTERACT_ACROSS_USERS"/>
<permission name="android.permission.LOCAL_MAC_ADDRESS"/>
- <permission name="android.permission.MANAGE_IPSEC_TUNNELS"/>
- <permission name="android.permission.MANAGE_NETWORK_POLICY"/>
<permission name="android.permission.MANAGE_SUBSCRIPTION_PLANS"/>
<permission name="android.permission.MANAGE_USB"/>
- <permission name="android.permission.NETWORK_BYPASS_PRIVATE_DNS"/>
- <permission name="android.permission.NETWORK_SETTINGS"/>
- <permission name="android.permission.NETWORK_STACK" />
- <permission name="android.permission.NET_TUNNELING"/>
<permission name="android.permission.PACKET_KEEPALIVE_OFFLOAD"/>
- <permission name="android.permission.PEERS_MAC_ADDRESS"/>
<permission name="android.permission.READ_NETWORK_USAGE_HISTORY"/>
<permission name="android.permission.READ_PRECISE_PHONE_STATE"/>
<permission name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
@@ -264,6 +247,7 @@ applications that come with the platform
<permission name="android.permission.CHANGE_LOWPAN_STATE"/>
<permission name="android.permission.CHANGE_OVERLAY_PACKAGES"/>
<permission name="android.permission.CLEAR_APP_CACHE"/>
+ <permission name="android.permission.ACCESS_INSTANT_APPS" />
<permission name="android.permission.CONNECTIVITY_INTERNAL"/>
<permission name="android.permission.DELETE_CACHE_FILES"/>
<permission name="android.permission.DELETE_PACKAGES"/>
diff --git a/keystore/java/android/security/OWNERS b/keystore/java/android/security/OWNERS
new file mode 100644
index 000000000000..ed30587418dd
--- /dev/null
+++ b/keystore/java/android/security/OWNERS
@@ -0,0 +1 @@
+per-file *.java,*.aidl = eranm@google.com,pgrafov@google.com,rubinxu@google.com
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
index aa2917484a05..3dc884eb38ad 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKeyGeneratorSpi.java
@@ -17,7 +17,6 @@
package android.security.keystore;
import android.security.Credentials;
-import android.security.GateKeeper;
import android.security.KeyStore;
import android.security.keymaster.KeyCharacteristics;
import android.security.keymaster.KeymasterArguments;
@@ -204,7 +203,12 @@ public abstract class AndroidKeyStoreKeyGeneratorSpi extends KeyGeneratorSpi {
}
}
}
-
+ if (mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_3DES) {
+ if (mKeySizeBits != 168) {
+ throw new InvalidAlgorithmParameterException(
+ "3DES key size must be 168 bits.");
+ }
+ }
if (mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) {
if (mKeySizeBits < 64) {
throw new InvalidAlgorithmParameterException(
diff --git a/keystore/tests/OWNERS b/keystore/tests/OWNERS
new file mode 100644
index 000000000000..9e65f88b3366
--- /dev/null
+++ b/keystore/tests/OWNERS
@@ -0,0 +1,5 @@
+# Android Enterprise security team
+eranm@google.com
+irinaid@google.com
+pgrafov@google.com
+rubinxu@google.com
diff --git a/libs/hwui/FrameMetricsObserver.h b/libs/hwui/FrameMetricsObserver.h
index ba72e937095f..0b9ae5cb4b7b 100644
--- a/libs/hwui/FrameMetricsObserver.h
+++ b/libs/hwui/FrameMetricsObserver.h
@@ -23,7 +23,7 @@ namespace uirenderer {
class FrameMetricsObserver : public VirtualLightRefBase {
public:
- virtual void notify(const int64_t* buffer);
+ virtual void notify(const int64_t* buffer) = 0;
};
}; // namespace uirenderer
diff --git a/media/OWNERS b/media/OWNERS
index 03b751c07c6c..eb26367d3d29 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -11,3 +11,6 @@ lajos@google.com
marcone@google.com
sungsoo@google.com
wjia@google.com
+
+# For maintaining sync with AndroidX code
+per-file ExifInterface.java = jinpark@google.com, sungsoo@google.com
diff --git a/media/java/android/media/MediaHTTPConnection.java b/media/java/android/media/MediaHTTPConnection.java
index ad25a06fabe2..5f324f7f97ed 100644
--- a/media/java/android/media/MediaHTTPConnection.java
+++ b/media/java/android/media/MediaHTTPConnection.java
@@ -16,6 +16,8 @@
package android.media;
+import static android.media.MediaPlayer.MEDIA_ERROR_UNSUPPORTED;
+
import android.annotation.UnsupportedAppUsage;
import android.net.NetworkUtils;
import android.os.IBinder;
@@ -23,21 +25,19 @@ import android.os.StrictMode;
import android.util.Log;
import java.io.BufferedInputStream;
-import java.io.InputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.net.CookieHandler;
-import java.net.CookieManager;
-import java.net.Proxy;
-import java.net.URL;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.NoRouteToHostException;
import java.net.ProtocolException;
+import java.net.Proxy;
+import java.net.URL;
import java.net.UnknownServiceException;
import java.util.HashMap;
import java.util.Map;
-
-import static android.media.MediaPlayer.MEDIA_ERROR_UNSUPPORTED;
+import java.util.concurrent.atomic.AtomicBoolean;
/** @hide */
public class MediaHTTPConnection extends IMediaHTTPConnection.Stub {
@@ -67,6 +67,7 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub {
// from com.squareup.okhttp.internal.http
private final static int HTTP_TEMP_REDIRECT = 307;
private final static int MAX_REDIRECTS = 20;
+ private AtomicBoolean mIsConnected = new AtomicBoolean(false);
@UnsupportedAppUsage
public MediaHTTPConnection() {
@@ -90,6 +91,7 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub {
mAllowCrossDomainRedirect = true;
mURL = new URL(uri);
mHeaders = convertHeaderStringToMap(headers);
+ mIsConnected.set(true);
} catch (MalformedURLException e) {
return null;
}
@@ -140,7 +142,14 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub {
@Override
@UnsupportedAppUsage
public void disconnect() {
- teardownConnection();
+ if (mIsConnected.getAndSet(false)) {
+ (new Thread() {
+ @Override
+ public void run() {
+ teardownConnection();
+ }
+ }).start();
+ }
mHeaders = null;
mURL = null;
}
@@ -325,7 +334,14 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub {
@Override
@UnsupportedAppUsage
public int readAt(long offset, int size) {
- return native_readAt(offset, size);
+ if (!mIsConnected.get()) {
+ return -1;
+ }
+ int result = native_readAt(offset, size);
+ if (!mIsConnected.get()) {
+ return -1;
+ }
+ return result;
}
private int readAt(long offset, byte[] data, int size) {
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 4c563dbbe979..25c7b5cc5cd6 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -51,6 +51,7 @@ cc_library_shared {
"libmtp",
"libexif",
"libpiex",
+ "libprocessgroup",
"libandroidfw",
"libhidlallocatorutils",
"libhidlbase",
@@ -131,6 +132,7 @@ cc_library_shared {
"libcutils",
"libdexfile",
"liblzma",
+ "libjsoncpp",
"libmedia_helper",
"libmedia_player2_util",
"libmediadrm",
@@ -140,6 +142,7 @@ cc_library_shared {
"libmediautils",
"libnativehelper",
"libnetd_client",
+ "libprocessgroup",
"libstagefright_esds",
"libstagefright_foundation",
"libstagefright_httplive",
diff --git a/packages/CaptivePortalLogin/Android.bp b/packages/CaptivePortalLogin/Android.bp
new file mode 100644
index 000000000000..9c31b4d4374f
--- /dev/null
+++ b/packages/CaptivePortalLogin/Android.bp
@@ -0,0 +1,27 @@
+//
+// Copyright (C) 2019 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.
+//
+
+android_app {
+ name: "CaptivePortalLogin",
+ srcs: ["src/**/*.java"],
+ sdk_version: "system_current",
+ certificate: "networkstack",
+ static_libs: [
+ "android-support-v4",
+ "metrics-constants-protos",
+ ],
+ manifest: "AndroidManifest.xml",
+}
diff --git a/packages/CaptivePortalLogin/Android.mk b/packages/CaptivePortalLogin/Android.mk
deleted file mode 100644
index 7dc23ff8e8b8..000000000000
--- a/packages/CaptivePortalLogin/Android.mk
+++ /dev/null
@@ -1,13 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := CaptivePortalLogin
-LOCAL_PRIVATE_PLATFORM_APIS := true
-LOCAL_CERTIFICATE := platform
-
-include $(BUILD_PACKAGE)
diff --git a/packages/CaptivePortalLogin/AndroidManifest.xml b/packages/CaptivePortalLogin/AndroidManifest.xml
index c84f3ec68178..0894ee576a2d 100644
--- a/packages/CaptivePortalLogin/AndroidManifest.xml
+++ b/packages/CaptivePortalLogin/AndroidManifest.xml
@@ -21,9 +21,10 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
- <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
- <uses-permission android:name="android.permission.NETWORK_SETTINGS" />
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.NETWORK_BYPASS_PRIVATE_DNS" />
+ <uses-permission android:name="android.permission.MAINLINE_NETWORK_STACK" />
<application android:label="@string/app_name"
android:usesCleartextTraffic="true"
diff --git a/packages/CaptivePortalLogin/res/layout/ssl_error_msg.xml b/packages/CaptivePortalLogin/res/layout/ssl_error_msg.xml
deleted file mode 100644
index d460041e59ae..000000000000
--- a/packages/CaptivePortalLogin/res/layout/ssl_error_msg.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 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.
--->
-
-<TextView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/ssl_error_msg"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:layout_marginStart="20dip"
- android:layout_marginEnd="20dip"
- android:gravity="center_vertical"
- android:layout_marginBottom="4dip"
- android:layout_marginTop="4dip" />
-
diff --git a/packages/CaptivePortalLogin/res/layout/ssl_warning.xml b/packages/CaptivePortalLogin/res/layout/ssl_warning.xml
index ffd57a430662..ce05e78757a1 100644
--- a/packages/CaptivePortalLogin/res/layout/ssl_warning.xml
+++ b/packages/CaptivePortalLogin/res/layout/ssl_warning.xml
@@ -78,7 +78,18 @@
android:id="@+id/certificate_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:orientation="vertical"
android:layout_marginBottom="16dip" >
+ <TextView
+ android:id="@+id/ssl_error_msg"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ android:layout_marginStart="20dip"
+ android:layout_marginEnd="20dip"
+ android:gravity="center_vertical"
+ android:layout_marginBottom="4dip"
+ android:layout_marginTop="16dip" />
</LinearLayout>
</ScrollView>
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index 08b22c7d518a..2c9b6eb72b4d 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -20,7 +20,7 @@ import static android.net.ConnectivityManager.EXTRA_CAPTIVE_PORTAL_PROBE_SPEC;
import android.app.Activity;
import android.app.AlertDialog;
-import android.app.LoadedApk;
+import android.app.Application;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -37,8 +37,9 @@ import android.net.captiveportal.CaptivePortalProbeSpec;
import android.net.http.SslCertificate;
import android.net.http.SslError;
import android.net.wifi.WifiInfo;
-import android.os.Build;
+import android.net.wifi.WifiManager;
import android.os.Bundle;
+import android.os.SystemProperties;
import android.support.v4.widget.SwipeRefreshLayout;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -95,6 +96,7 @@ public class CaptivePortalLoginActivity extends Activity {
private CaptivePortal mCaptivePortal;
private NetworkCallback mNetworkCallback;
private ConnectivityManager mCm;
+ private WifiManager mWifiManager;
private boolean mLaunchBrowser = false;
private MyWebViewClient mWebViewClient;
private SwipeRefreshLayout mSwipeRefreshLayout;
@@ -108,7 +110,8 @@ public class CaptivePortalLoginActivity extends Activity {
mCaptivePortal = getIntent().getParcelableExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL);
logMetricsEvent(MetricsEvent.ACTION_CAPTIVE_PORTAL_LOGIN_ACTIVITY);
- mCm = ConnectivityManager.from(this);
+ mCm = getSystemService(ConnectivityManager.class);
+ mWifiManager = getSystemService(WifiManager.class);
mNetwork = getIntent().getParcelableExtra(ConnectivityManager.EXTRA_NETWORK);
mUserAgent =
getIntent().getStringExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_USER_AGENT);
@@ -150,7 +153,6 @@ public class CaptivePortalLoginActivity extends Activity {
// Also initializes proxy system properties.
mNetwork = mNetwork.getPrivateDnsBypassingCopy();
mCm.bindProcessToNetwork(mNetwork);
- mCm.setProcessDefaultNetworkForHostResolution(mNetwork);
// Proxy system properties must be initialized before setContentView is called because
// setContentView initializes the WebView logic which in turn reads the system properties.
@@ -172,6 +174,7 @@ public class CaptivePortalLoginActivity extends Activity {
webSettings.setSupportZoom(true);
webSettings.setBuiltInZoomControls(true);
webSettings.setDisplayZoomControls(false);
+ webSettings.setDomStorageEnabled(true);
mWebViewClient = new MyWebViewClient();
webview.setWebViewClient(mWebViewClient);
webview.setWebChromeClient(new MyWebChromeClient());
@@ -189,9 +192,12 @@ public class CaptivePortalLoginActivity extends Activity {
// Find WebView's proxy BroadcastReceiver and prompt it to read proxy system properties.
private void setWebViewProxy() {
- LoadedApk loadedApk = getApplication().mLoadedApk;
+ // TODO: migrate to androidx WebView proxy setting API as soon as it is finalized
try {
- Field receiversField = LoadedApk.class.getDeclaredField("mReceivers");
+ final Field loadedApkField = Application.class.getDeclaredField("mLoadedApk");
+ final Class<?> loadedApkClass = loadedApkField.getType();
+ final Object loadedApk = loadedApkField.get(getApplication());
+ Field receiversField = loadedApkClass.getDeclaredField("mReceivers");
receiversField.setAccessible(true);
ArrayMap receivers = (ArrayMap) receiversField.get(loadedApk);
for (Object receiverMap : receivers.values()) {
@@ -332,7 +338,11 @@ public class CaptivePortalLoginActivity extends Activity {
private static String sanitizeURL(URL url) {
// In non-Debug build, only show host to avoid leaking private info.
- return Build.IS_DEBUGGABLE ? Objects.toString(url) : host(url);
+ return isDebuggable() ? Objects.toString(url) : host(url);
+ }
+
+ private static boolean isDebuggable() {
+ return SystemProperties.getInt("ro.debuggable", 0) == 1;
}
private void testForCaptivePortal() {
@@ -585,19 +595,18 @@ public class CaptivePortalLoginActivity extends Activity {
}
private void setViewSecurityCertificate(LinearLayout certificateLayout, SslError error) {
+ ((TextView) certificateLayout.findViewById(R.id.ssl_error_msg))
+ .setText(sslErrorMessage(error));
SslCertificate cert = error.getCertificate();
-
- View certificateView = cert.inflateCertificateView(CaptivePortalLoginActivity.this);
- final LinearLayout placeholder = (LinearLayout) certificateView
- .findViewById(com.android.internal.R.id.placeholder);
- LayoutInflater factory = LayoutInflater.from(CaptivePortalLoginActivity.this);
-
- TextView textView = (TextView) factory.inflate(
- R.layout.ssl_error_msg, placeholder, false);
- textView.setText(sslErrorMessage(error));
- placeholder.addView(textView);
-
- certificateLayout.addView(certificateView);
+ // TODO: call the method directly once inflateCertificateView is @SystemApi
+ try {
+ final View certificateView = (View) SslCertificate.class.getMethod(
+ "inflateCertificateView", Context.class)
+ .invoke(cert, CaptivePortalLoginActivity.this);
+ certificateLayout.addView(certificateView);
+ } catch (ReflectiveOperationException | SecurityException e) {
+ Log.e(TAG, "Could not create certificate view", e);
+ }
}
}
@@ -618,11 +627,30 @@ public class CaptivePortalLoginActivity extends Activity {
private String getHeaderTitle() {
NetworkCapabilities nc = mCm.getNetworkCapabilities(mNetwork);
- if (nc == null || TextUtils.isEmpty(nc.getSSID())
- || !nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
+ final String ssid = getSsid();
+ if (TextUtils.isEmpty(ssid)
+ || nc == null || !nc.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
return getString(R.string.action_bar_label);
}
- return getString(R.string.action_bar_title, WifiInfo.removeDoubleQuotes(nc.getSSID()));
+ return getString(R.string.action_bar_title, ssid);
+ }
+
+ // TODO: remove once SSID is obtained from NetworkCapabilities
+ private String getSsid() {
+ if (mWifiManager == null) {
+ return null;
+ }
+ final WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
+ return removeDoubleQuotes(wifiInfo.getSSID());
+ }
+
+ private static String removeDoubleQuotes(String string) {
+ if (string == null) return null;
+ final int length = string.length();
+ if ((length > 1) && (string.charAt(0) == '"') && (string.charAt(length - 1) == '"')) {
+ return string.substring(1, length - 1);
+ }
+ return string;
}
private String getHeaderSubtitle(URL url) {
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
index 4f67350b5adc..31549fa0d1bb 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
@@ -106,6 +106,7 @@ public class CaptivePortalLoginActivity extends Activity {
webSettings.setLoadWithOverviewMode(true);
webSettings.setSupportZoom(true);
webSettings.setBuiltInZoomControls(true);
+ webSettings.setDomStorageEnabled(true);
mWebViewClient = new MyWebViewClient();
mWebView.setWebViewClient(mWebViewClient);
mWebView.setWebChromeClient(new MyWebChromeClient());
diff --git a/packages/NetworkStack/Android.bp b/packages/NetworkStack/Android.bp
index b0522f2a99a0..b700bf324817 100644
--- a/packages/NetworkStack/Android.bp
+++ b/packages/NetworkStack/Android.bp
@@ -35,10 +35,12 @@ java_library {
android_app {
name: "NetworkStack",
sdk_version: "system_current",
- certificate: "platform",
+ certificate: "networkstack",
privileged: true,
static_libs: [
"NetworkStackLib"
],
+ jarjar_rules: "jarjar-rules-shared.txt",
manifest: "AndroidManifest.xml",
+ required: ["NetworkStackPermissionStub"],
} \ No newline at end of file
diff --git a/packages/NetworkStack/AndroidManifest.xml b/packages/NetworkStack/AndroidManifest.xml
index 5ab833bda66d..52c209e5f247 100644
--- a/packages/NetworkStack/AndroidManifest.xml
+++ b/packages/NetworkStack/AndroidManifest.xml
@@ -17,18 +17,19 @@
*/
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.mainline.networkstack"
+ package="com.android.networkstack"
android:sharedUserId="android.uid.networkstack">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
- <uses-permission android:name="android.permission.NETWORK_SETTINGS" />
- <!-- Launch captive portal app as specific user -->
- <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
- <uses-permission android:name="android.permission.NETWORK_STACK" />
+ <!-- Signature permission defined in NetworkStackStub -->
+ <uses-permission android:name="android.permission.MAINLINE_NETWORK_STACK" />
+ <!-- Send latency broadcast as current user -->
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
+ <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
<application
android:label="NetworkStack"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/NetworkStack/OWNERS b/packages/NetworkStack/OWNERS
new file mode 100644
index 000000000000..a395465e5f21
--- /dev/null
+++ b/packages/NetworkStack/OWNERS
@@ -0,0 +1,5 @@
+codewiz@google.com
+jchalard@google.com
+lorenzo@google.com
+reminv@google.com
+satk@google.com
diff --git a/packages/NetworkStack/jarjar-rules-shared.txt b/packages/NetworkStack/jarjar-rules-shared.txt
new file mode 100644
index 000000000000..a8c712a3336d
--- /dev/null
+++ b/packages/NetworkStack/jarjar-rules-shared.txt
@@ -0,0 +1,19 @@
+rule com.android.internal.util.** android.net.networkstack.util.@1
+
+rule android.net.shared.Inet4AddressUtils* android.net.networkstack.shared.Inet4AddressUtils@1
+rule android.net.shared.InetAddressUtils* android.net.networkstack.shared.InetAddressUtils@1
+
+# Ignore DhcpResultsParcelable, but jarjar DhcpResults
+# TODO: move DhcpResults into services.net and delete from here
+rule android.net.DhcpResultsParcelable* @0
+rule android.net.DhcpResults* android.net.networkstack.DhcpResults@1
+rule android.net.LocalLog* android.net.networkstack.LocalLog@1
+
+# TODO: remove from framework dependencies, then remove here
+rule android.net.InterfaceConfigurationParcel* android.net.networkstack.InterfaceConfigurationParcel@1
+rule android.net.TetherStatsParcel* android.net.networkstack.TetherStatsParcel@1
+
+# Used by UidRange, which is used by framework classes such as NetworkCapabilities.
+rule android.net.UidRangeParcel* android.net.networkstack.UidRangeParcel@1
+# TODO: move TcpKeepalivePacketData to services.net and delete
+rule android.net.TcpKeepalivePacketDataParcelable* android.net.networkstack.TcpKeepalivePacketDataParcelable@1 \ No newline at end of file
diff --git a/packages/NetworkStack/src/android/net/apf/ApfFilter.java b/packages/NetworkStack/src/android/net/apf/ApfFilter.java
index 4fa7d6462092..923f162c92a6 100644
--- a/packages/NetworkStack/src/android/net/apf/ApfFilter.java
+++ b/packages/NetworkStack/src/android/net/apf/ApfFilter.java
@@ -23,6 +23,7 @@ import static android.system.OsConstants.ETH_P_ARP;
import static android.system.OsConstants.ETH_P_IP;
import static android.system.OsConstants.ETH_P_IPV6;
import static android.system.OsConstants.IPPROTO_ICMPV6;
+import static android.system.OsConstants.IPPROTO_TCP;
import static android.system.OsConstants.IPPROTO_UDP;
import static android.system.OsConstants.SOCK_RAW;
@@ -38,6 +39,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.net.LinkAddress;
import android.net.LinkProperties;
+import android.net.TcpKeepalivePacketDataParcelable;
import android.net.apf.ApfGenerator.IllegalInstructionException;
import android.net.apf.ApfGenerator.Register;
import android.net.ip.IpClient.IpClientCallbacksWrapper;
@@ -55,6 +57,7 @@ import android.system.Os;
import android.text.format.DateUtils;
import android.util.Log;
import android.util.Pair;
+import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -149,7 +152,9 @@ public class ApfFilter {
DROPPED_IPV6_NON_ICMP_MULTICAST,
DROPPED_802_3_FRAME,
DROPPED_ETHERTYPE_BLACKLISTED,
- DROPPED_ARP_REPLY_SPA_NO_HOST;
+ DROPPED_ARP_REPLY_SPA_NO_HOST,
+ DROPPED_IPV4_KEEPALIVE_ACK,
+ DROPPED_IPV6_KEEPALIVE_ACK;
// Returns the negative byte offset from the end of the APF data segment for
// a given counter.
@@ -285,6 +290,7 @@ public class ApfFilter {
private static final int IPV4_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 16;
private static final int IPV4_ANY_HOST_ADDRESS = 0;
private static final int IPV4_BROADCAST_ADDRESS = -1; // 255.255.255.255
+ private static final int IPV4_HEADER_LEN = 20; // Without options
// Traffic class and Flow label are not byte aligned. Luckily we
// don't care about either value so we'll consider bytes 1-3 of the
@@ -305,6 +311,8 @@ public class ApfFilter {
private static final int UDP_DESTINATION_PORT_OFFSET = ETH_HEADER_LEN + 2;
private static final int UDP_HEADER_LEN = 8;
+ private static final int TCP_HEADER_SIZE_OFFSET = 12;
+
private static final int DHCP_CLIENT_PORT = 68;
// NOTE: this must be added to the IPv4 header length in IPV4_HEADER_SIZE_MEMORY_SLOT
private static final int DHCP_CLIENT_MAC_OFFSET = ETH_HEADER_LEN + UDP_HEADER_LEN + 28;
@@ -788,7 +796,7 @@ public class ApfFilter {
boolean isExpired() {
// TODO: We may want to handle 0 lifetime RAs differently, if they are common. We'll
- // have to calculte the filter lifetime specially as a fraction of 0 is still 0.
+ // have to calculate the filter lifetime specially as a fraction of 0 is still 0.
return currentLifetime() <= 0;
}
@@ -847,11 +855,147 @@ public class ApfFilter {
}
}
+ // A class to hold keepalive ack information.
+ private abstract static class TcpKeepaliveAck {
+ // Note that the offset starts from IP header.
+ // These must be added ether header length when generating program.
+ static final int IP_HEADER_OFFSET = 0;
+
+ protected static class TcpKeepaliveAckData {
+ public final byte[] srcAddress;
+ public final int srcPort;
+ public final byte[] dstAddress;
+ public final int dstPort;
+ public final int seq;
+ public final int ack;
+ // Create the characteristics of the ack packet from the sent keepalive packet.
+ TcpKeepaliveAckData(final TcpKeepalivePacketDataParcelable sentKeepalivePacket) {
+ srcAddress = sentKeepalivePacket.dstAddress;
+ srcPort = sentKeepalivePacket.dstPort;
+ dstAddress = sentKeepalivePacket.srcAddress;
+ dstPort = sentKeepalivePacket.srcPort;
+ seq = sentKeepalivePacket.ack;
+ ack = sentKeepalivePacket.seq + 1;
+ }
+ }
+
+ protected final TcpKeepaliveAckData mPacket;
+ protected final byte[] mSrcDstAddr;
+
+ TcpKeepaliveAck(final TcpKeepaliveAckData packet, final byte[] srcDstAddr) {
+ mPacket = packet;
+ mSrcDstAddr = srcDstAddr;
+ }
+
+ static byte[] concatArrays(final byte[]... arr) {
+ int size = 0;
+ for (byte[] a : arr) {
+ size += a.length;
+ }
+ final byte[] result = new byte[size];
+ int offset = 0;
+ for (byte[] a : arr) {
+ System.arraycopy(a, 0, result, offset, a.length);
+ offset += a.length;
+ }
+ return result;
+ }
+
+ public String toString() {
+ return String.format("%s(%d) -> %s(%d), seq=%d, ack=%d",
+ mPacket.srcAddress,
+ mPacket.srcPort,
+ mPacket.dstAddress,
+ mPacket.dstPort,
+ mPacket.seq,
+ mPacket.ack);
+ }
+
+ // Append a filter for this keepalive ack to {@code gen}.
+ // Jump to drop if it matches the keepalive ack.
+ // Jump to the next filter if packet doesn't match the keepalive ack.
+ abstract void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException;
+ }
+
+ private class TcpKeepaliveAckV4 extends TcpKeepaliveAck {
+ private static final int IPV4_SRC_ADDR_OFFSET = IP_HEADER_OFFSET + 12;
+ private static final int IPV4_TCP_SRC_PORT_OFFSET = 0;
+ private static final int IPV4_TCP_DST_PORT_OFFSET = 2;
+ private static final int IPV4_TCP_SEQ_OFFSET = 4;
+ private static final int IPV4_TCP_ACK_OFFSET = 8;
+
+ TcpKeepaliveAckV4(final TcpKeepalivePacketDataParcelable sentKeepalivePacket) {
+ this(new TcpKeepaliveAckData(sentKeepalivePacket));
+ }
+ TcpKeepaliveAckV4(final TcpKeepaliveAckData packet) {
+ super(packet, concatArrays(packet.srcAddress, packet.dstAddress) /* srcDstAddr */);
+ }
+
+ @Override
+ void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
+ final String nextFilterLabel = "keepalive_ack" + getUniqueNumberLocked();
+ gen.addLoad8(Register.R0, IPV4_PROTOCOL_OFFSET);
+ gen.addJumpIfR0NotEquals(IPPROTO_TCP, nextFilterLabel);
+ gen.addLoadImmediate(Register.R0, ETH_HEADER_LEN + IPV4_SRC_ADDR_OFFSET);
+ gen.addJumpIfBytesNotEqual(Register.R0, mSrcDstAddr, nextFilterLabel);
+
+ // Pass the packet if it's not zero-sized :
+ // Load the IP header size into R1
+ gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
+ // Load the TCP header size into R0 (it's indexed by R1)
+ gen.addLoad8Indexed(Register.R0, ETH_HEADER_LEN + TCP_HEADER_SIZE_OFFSET);
+ // Size offset is in the top nibble, but it must be multiplied by 4, and the two
+ // top bits of the low nibble are guaranteed to be zeroes. Right-shift R0 by 2.
+ gen.addRightShift(2);
+ // R0 += R1 -> R0 contains TCP + IP headers lenght
+ gen.addAddR1();
+ // Add the Ethernet header length to R0.
+ gen.addLoadImmediate(Register.R1, ETH_HEADER_LEN);
+ gen.addAddR1();
+ // Compare total length of headers to the size of the packet.
+ gen.addLoadFromMemory(Register.R1, gen.PACKET_SIZE_MEMORY_SLOT);
+ gen.addNeg(Register.R0);
+ gen.addAddR1();
+ gen.addJumpIfR0NotEquals(0, nextFilterLabel);
+
+ // Add IPv4 header length
+ gen.addLoadFromMemory(Register.R1, gen.IPV4_HEADER_SIZE_MEMORY_SLOT);
+ gen.addLoad16Indexed(Register.R0, ETH_HEADER_LEN + IPV4_TCP_SRC_PORT_OFFSET);
+ gen.addJumpIfR0NotEquals(mPacket.srcPort, nextFilterLabel);
+ gen.addLoad16Indexed(Register.R0, ETH_HEADER_LEN + IPV4_TCP_DST_PORT_OFFSET);
+ gen.addJumpIfR0NotEquals(mPacket.dstPort, nextFilterLabel);
+ gen.addLoad32Indexed(Register.R0, ETH_HEADER_LEN + IPV4_TCP_SEQ_OFFSET);
+ gen.addJumpIfR0NotEquals(mPacket.seq, nextFilterLabel);
+ gen.addLoad32Indexed(Register.R0, ETH_HEADER_LEN + IPV4_TCP_ACK_OFFSET);
+ gen.addJumpIfR0NotEquals(mPacket.ack, nextFilterLabel);
+
+ maybeSetupCounter(gen, Counter.DROPPED_IPV4_KEEPALIVE_ACK);
+ gen.addJump(mCountAndDropLabel);
+ gen.defineLabel(nextFilterLabel);
+ }
+ }
+
+ private class TcpKeepaliveAckV6 extends TcpKeepaliveAck {
+ TcpKeepaliveAckV6(final TcpKeepalivePacketDataParcelable sentKeepalivePacket) {
+ this(new TcpKeepaliveAckData(sentKeepalivePacket));
+ }
+ TcpKeepaliveAckV6(final TcpKeepaliveAckData packet) {
+ super(packet, concatArrays(packet.srcAddress, packet.dstAddress) /* srcDstAddr */);
+ }
+
+ @Override
+ void generateFilterLocked(ApfGenerator gen) throws IllegalInstructionException {
+ throw new UnsupportedOperationException("IPv6 Keepalive is not supported yet");
+ }
+ }
+
// Maximum number of RAs to filter for.
private static final int MAX_RAS = 10;
@GuardedBy("this")
- private ArrayList<Ra> mRas = new ArrayList<Ra>();
+ private ArrayList<Ra> mRas = new ArrayList<>();
+ @GuardedBy("this")
+ private SparseArray<TcpKeepaliveAck> mKeepaliveAcks = new SparseArray<>();
// There is always some marginal benefit to updating the installed APF program when an RA is
// seen because we can extend the program's lifetime slightly, but there is some cost to
@@ -980,6 +1124,8 @@ public class ApfFilter {
// drop
// if it's IPv4 broadcast:
// drop
+ // if keepalive ack
+ // drop
// pass
if (mMulticastFilter) {
@@ -1023,6 +1169,9 @@ public class ApfFilter {
gen.addJumpIfR0Equals(broadcastAddr, mCountAndDropLabel);
}
+ // If any keepalive filters,
+ generateKeepaliveFilter(gen);
+
// If L2 broadcast packet, drop.
// TODO: can we invert this condition to fall through to the common pass case below?
maybeSetupCounter(gen, Counter.PASSED_IPV4_UNICAST);
@@ -1030,6 +1179,8 @@ public class ApfFilter {
gen.addJumpIfBytesNotEqual(Register.R0, ETH_BROADCAST_MAC_ADDRESS, mCountAndPassLabel);
maybeSetupCounter(gen, Counter.DROPPED_IPV4_L2_BROADCAST);
gen.addJump(mCountAndDropLabel);
+ } else {
+ generateKeepaliveFilter(gen);
}
// Otherwise, pass
@@ -1037,6 +1188,13 @@ public class ApfFilter {
gen.addJump(mCountAndPassLabel);
}
+ private void generateKeepaliveFilter(ApfGenerator gen) throws IllegalInstructionException {
+ // Drop IPv4 Keepalive acks
+ for (int i = 0; i < mKeepaliveAcks.size(); ++i) {
+ final TcpKeepaliveAck ack = mKeepaliveAcks.valueAt(i);
+ if (ack instanceof TcpKeepaliveAckV4) ack.generateFilterLocked(gen);
+ }
+ }
/**
* Generate filter code to process IPv6 packets. Execution of this code ends in either the
@@ -1057,6 +1215,8 @@ public class ApfFilter {
// drop
// if it's ICMPv6 NA to ff02::1:
// drop
+ // if keepalive ack
+ // drop
gen.addLoad8(Register.R0, IPV6_NEXT_HEADER_OFFSET);
@@ -1112,6 +1272,12 @@ public class ApfFilter {
maybeSetupCounter(gen, Counter.DROPPED_IPV6_MULTICAST_NA);
gen.addJump(mCountAndDropLabel);
gen.defineLabel(skipUnsolicitedMulticastNALabel);
+
+ // Drop IPv6 Keepalive acks
+ for (int i = 0; i < mKeepaliveAcks.size(); ++i) {
+ final TcpKeepaliveAck ack = mKeepaliveAcks.valueAt(i);
+ if (ack instanceof TcpKeepaliveAckV6) ack.generateFilterLocked(gen);
+ }
}
/**
@@ -1489,6 +1655,36 @@ public class ApfFilter {
installNewProgramLocked();
}
+ /**
+ * Add keepalive ack packet filter.
+ * This will add a filter to drop acks to the keepalive packet passed as an argument.
+ *
+ * @param slot The index used to access the filter.
+ * @param sentKeepalivePacket The attributes of the sent keepalive packet.
+ */
+ public synchronized void addKeepalivePacketFilter(final int slot,
+ final TcpKeepalivePacketDataParcelable sentKeepalivePacket) {
+ log("Adding keepalive ack(" + slot + ")");
+ if (null != mKeepaliveAcks.get(slot)) {
+ throw new IllegalArgumentException("Keepalive slot " + slot + " is occupied");
+ }
+ final int ipVersion = sentKeepalivePacket.srcAddress.length == 4 ? 4 : 6;
+ mKeepaliveAcks.put(slot, (ipVersion == 4)
+ ? new TcpKeepaliveAckV4(sentKeepalivePacket)
+ : new TcpKeepaliveAckV6(sentKeepalivePacket));
+ installNewProgramLocked();
+ }
+
+ /**
+ * Remove keepalive packet filter.
+ *
+ * @param slot The index used to access the filter.
+ */
+ public synchronized void removeKeepalivePacketFilter(int slot) {
+ mKeepaliveAcks.remove(slot);
+ installNewProgramLocked();
+ }
+
static public long counterValue(byte[] data, Counter counter)
throws ArrayIndexOutOfBoundsException {
// Follow the same wrap-around addressing scheme of the interpreter.
@@ -1541,6 +1737,17 @@ public class ApfFilter {
}
pw.decreaseIndent();
+ pw.println("Keepalive filter:");
+ pw.increaseIndent();
+ for (int i = 0; i < mKeepaliveAcks.size(); ++i) {
+ final TcpKeepaliveAck keepaliveAck = mKeepaliveAcks.valueAt(i);
+ pw.print("Slot ");
+ pw.print(mKeepaliveAcks.keyAt(i));
+ pw.print(" : ");
+ pw.println(keepaliveAck);
+ }
+ pw.decreaseIndent();
+
if (DBG) {
pw.println("Last program:");
pw.increaseIndent();
diff --git a/packages/NetworkStack/src/android/net/apf/ApfGenerator.java b/packages/NetworkStack/src/android/net/apf/ApfGenerator.java
index 87a1b5ea8b4d..809327a0b79d 100644
--- a/packages/NetworkStack/src/android/net/apf/ApfGenerator.java
+++ b/packages/NetworkStack/src/android/net/apf/ApfGenerator.java
@@ -476,7 +476,7 @@ public class ApfGenerator {
/**
* Add an instruction to the end of the program to load 16-bits from the packet into
- * {@code register}. The offset of the loaded 16-bits from the begining of the packet is
+ * {@code register}. The offset of the loaded 16-bits from the beginning of the packet is
* the sum of {@code offset} and the value in register R1.
*/
public ApfGenerator addLoad16Indexed(Register register, int offset) {
@@ -488,7 +488,7 @@ public class ApfGenerator {
/**
* Add an instruction to the end of the program to load 32-bits from the packet into
- * {@code register}. The offset of the loaded 32-bits from the begining of the packet is
+ * {@code register}. The offset of the loaded 32-bits from the beginning of the packet is
* the sum of {@code offset} and the value in register R1.
*/
public ApfGenerator addLoad32Indexed(Register register, int offset) {
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java b/packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java
index 96d1a287ef09..97d26c7c9c1f 100644
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java
+++ b/packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java
@@ -18,7 +18,7 @@ package android.net.dhcp;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.net.shared.FdEventsReader;
+import android.net.util.FdEventsReader;
import android.os.Handler;
import android.system.Os;
diff --git a/packages/NetworkStack/src/android/net/ip/IpClient.java b/packages/NetworkStack/src/android/net/ip/IpClient.java
index 12fe8c507db4..b1f6d246563e 100644
--- a/packages/NetworkStack/src/android/net/ip/IpClient.java
+++ b/packages/NetworkStack/src/android/net/ip/IpClient.java
@@ -23,6 +23,7 @@ import static android.net.shared.LinkPropertiesParcelableUtil.toStableParcelable
import static com.android.server.util.PermissionUtil.checkNetworkStackCallingPermission;
+import android.annotation.NonNull;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.DhcpResults;
@@ -34,6 +35,7 @@ import android.net.ProvisioningConfigurationParcelable;
import android.net.ProxyInfo;
import android.net.ProxyInfoParcelable;
import android.net.RouteInfo;
+import android.net.TcpKeepalivePacketDataParcelable;
import android.net.apf.ApfCapabilities;
import android.net.apf.ApfFilter;
import android.net.dhcp.DhcpClient;
@@ -44,6 +46,7 @@ import android.net.shared.ProvisioningConfiguration;
import android.net.util.InterfaceParams;
import android.net.util.SharedLog;
import android.os.ConditionVariable;
+import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.os.SystemClock;
@@ -292,6 +295,8 @@ public class IpClient extends StateMachine {
private static final int EVENT_PROVISIONING_TIMEOUT = 10;
private static final int EVENT_DHCPACTION_TIMEOUT = 11;
private static final int EVENT_READ_PACKET_FILTER_COMPLETE = 12;
+ private static final int CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF = 13;
+ private static final int CMD_REMOVE_KEEPALIVE_PACKET_FILTER_FROM_APF = 14;
// Internal commands to use instead of trying to call transitionTo() inside
// a given State's enter() method. Calling transitionTo() from enter/exit
@@ -376,6 +381,13 @@ public class IpClient extends StateMachine {
public InterfaceParams getInterfaceParams(String ifname) {
return InterfaceParams.getByName(ifname);
}
+
+ /**
+ * Get a INetd connector.
+ */
+ public INetd getNetd(Context context) {
+ return INetd.Stub.asInterface((IBinder) context.getSystemService(Context.NETD_SERVICE));
+ }
}
public IpClient(Context context, String ifName, IIpClientCallbacks callback,
@@ -409,7 +421,7 @@ public class IpClient extends StateMachine {
// TODO: Consider creating, constructing, and passing in some kind of
// InterfaceController.Dependencies class.
- mNetd = mContext.getSystemService(INetd.class);
+ mNetd = deps.getNetd(mContext);
mInterfaceCtrl = new InterfaceController(mInterfaceName, mNetd, mLog);
mLinkObserver = new IpClientLinkObserver(
@@ -522,6 +534,16 @@ public class IpClient extends StateMachine {
checkNetworkStackCallingPermission();
IpClient.this.setMulticastFilter(enabled);
}
+ @Override
+ public void addKeepalivePacketFilter(int slot, TcpKeepalivePacketDataParcelable pkt) {
+ checkNetworkStackCallingPermission();
+ IpClient.this.addKeepalivePacketFilter(slot, pkt);
+ }
+ @Override
+ public void removeKeepalivePacketFilter(int slot) {
+ checkNetworkStackCallingPermission();
+ IpClient.this.removeKeepalivePacketFilter(slot);
+ }
}
public String getInterfaceName() {
@@ -644,6 +666,22 @@ public class IpClient extends StateMachine {
}
/**
+ * Called by WifiStateMachine to add keepalive packet filter before setting up
+ * keepalive offload.
+ */
+ public void addKeepalivePacketFilter(int slot, @NonNull TcpKeepalivePacketDataParcelable pkt) {
+ sendMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF, slot, 0 /* Unused */, pkt);
+ }
+
+ /**
+ * Called by WifiStateMachine to remove keepalive packet filter after stopping keepalive
+ * offload.
+ */
+ public void removeKeepalivePacketFilter(int slot) {
+ sendMessage(CMD_REMOVE_KEEPALIVE_PACKET_FILTER_FROM_APF, slot, 0 /* Unused */);
+ }
+
+ /**
* Dump logs of this IpClient.
*/
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
@@ -1512,6 +1550,23 @@ public class IpClient extends StateMachine {
break;
}
+ case CMD_ADD_KEEPALIVE_PACKET_FILTER_TO_APF: {
+ final int slot = msg.arg1;
+ if (mApfFilter != null) {
+ mApfFilter.addKeepalivePacketFilter(slot,
+ (TcpKeepalivePacketDataParcelable) msg.obj);
+ }
+ break;
+ }
+
+ case CMD_REMOVE_KEEPALIVE_PACKET_FILTER_FROM_APF: {
+ final int slot = msg.arg1;
+ if (mApfFilter != null) {
+ mApfFilter.removeKeepalivePacketFilter(slot);
+ }
+ break;
+ }
+
case EVENT_DHCPACTION_TIMEOUT:
stopDhcpAction();
break;
diff --git a/core/java/android/net/shared/FdEventsReader.java b/packages/NetworkStack/src/android/net/util/FdEventsReader.java
index bffbfb115886..1380ea720d40 100644
--- a/core/java/android/net/shared/FdEventsReader.java
+++ b/packages/NetworkStack/src/android/net/util/FdEventsReader.java
@@ -14,14 +14,13 @@
* limitations under the License.
*/
-package android.net.shared;
+package android.net.util;
import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR;
import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.net.util.SocketUtils;
import android.os.Handler;
import android.os.Looper;
import android.os.MessageQueue;
diff --git a/packages/NetworkStack/src/android/net/util/PacketReader.java b/packages/NetworkStack/src/android/net/util/PacketReader.java
index 94b1e9f2e14e..4aec6b6753a6 100644
--- a/packages/NetworkStack/src/android/net/util/PacketReader.java
+++ b/packages/NetworkStack/src/android/net/util/PacketReader.java
@@ -18,7 +18,6 @@ package android.net.util;
import static java.lang.Math.max;
-import android.net.shared.FdEventsReader;
import android.os.Handler;
import android.system.Os;
diff --git a/packages/NetworkStack/src/com/android/server/NetworkStackService.java b/packages/NetworkStack/src/com/android/server/NetworkStackService.java
index cedcb84e9d08..90db207c9902 100644
--- a/packages/NetworkStack/src/com/android/server/NetworkStackService.java
+++ b/packages/NetworkStack/src/com/android/server/NetworkStackService.java
@@ -114,7 +114,8 @@ public class NetworkStackService extends Service {
NetworkStackConnector(Context context) {
mContext = context;
- mNetd = (INetd) context.getSystemService(Context.NETD_SERVICE);
+ mNetd = INetd.Stub.asInterface(
+ (IBinder) context.getSystemService(Context.NETD_SERVICE));
mObserverRegistry = new NetworkObserverRegistry();
mCm = context.getSystemService(ConnectivityManager.class);
@@ -246,6 +247,12 @@ public class NetworkStackService extends Service {
}
@Override
+ public void notifyCaptivePortalAppFinished(int response) {
+ checkNetworkStackCallingPermission();
+ mNm.notifyCaptivePortalAppFinished(response);
+ }
+
+ @Override
public void forceReevaluation(int uid) {
checkNetworkStackCallingPermission();
mNm.forceReevaluation(uid);
diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
index b34efc46f3b4..ec4a47930fad 100644
--- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
+++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
@@ -39,9 +39,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.net.CaptivePortal;
import android.net.ConnectivityManager;
-import android.net.ICaptivePortal;
import android.net.INetworkMonitor;
import android.net.INetworkMonitorCallbacks;
import android.net.LinkProperties;
@@ -61,20 +59,15 @@ import android.net.util.SharedLog;
import android.net.util.Stopwatch;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
+import android.os.Bundle;
import android.os.Message;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
-import android.telephony.CellIdentityCdma;
-import android.telephony.CellIdentityGsm;
-import android.telephony.CellIdentityLte;
-import android.telephony.CellIdentityWcdma;
-import android.telephony.CellInfo;
-import android.telephony.CellInfoCdma;
-import android.telephony.CellInfoGsm;
-import android.telephony.CellInfoLte;
-import android.telephony.CellInfoWcdma;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.NetworkRegistrationState;
+import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
@@ -109,6 +102,8 @@ public class NetworkMonitor extends StateMachine {
private static final boolean DBG = true;
private static final boolean VDBG = false;
private static final boolean VDBG_STALL = Log.isLoggable(TAG, Log.DEBUG);
+ // TODO: use another permission for CaptivePortalLoginActivity once it has its own certificate
+ private static final String PERMISSION_NETWORK_SETTINGS = "android.permission.NETWORK_SETTINGS";
// Default configuration values for captive portal detection probes.
// TODO: append a random length parameter to the default HTTPS url.
// TODO: randomize browser version ids in the default User-Agent String.
@@ -463,6 +458,13 @@ public class NetworkMonitor extends StateMachine {
sendMessage(CMD_LAUNCH_CAPTIVE_PORTAL_APP);
}
+ /**
+ * Notify that the captive portal app was closed with the provided response code.
+ */
+ public void notifyCaptivePortalAppFinished(int response) {
+ sendMessage(CMD_CAPTIVE_PORTAL_APP_FINISHED, response);
+ }
+
@Override
protected void log(String s) {
if (DBG) Log.d(TAG + "/" + mNetwork.toString(), s);
@@ -497,7 +499,7 @@ public class NetworkMonitor extends StateMachine {
private void showProvisioningNotification(String action) {
try {
- mCallback.showProvisioningNotification(action);
+ mCallback.showProvisioningNotification(action, mContext.getPackageName());
} catch (RemoteException e) {
Log.e(TAG, "Error showing provisioning notification", e);
}
@@ -672,42 +674,19 @@ public class NetworkMonitor extends StateMachine {
public boolean processMessage(Message message) {
switch (message.what) {
case CMD_LAUNCH_CAPTIVE_PORTAL_APP:
- final Intent intent = new Intent(
- ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN);
+ final Bundle appExtras = new Bundle();
// OneAddressPerFamilyNetwork is not parcelable across processes.
- intent.putExtra(ConnectivityManager.EXTRA_NETWORK, new Network(mNetwork));
- intent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL,
- new CaptivePortal(new ICaptivePortal.Stub() {
- @Override
- public void appResponse(int response) {
- if (response == APP_RETURN_WANTED_AS_IS) {
- mContext.enforceCallingPermission(
- android.Manifest.permission.CONNECTIVITY_INTERNAL,
- "CaptivePortal");
- }
- sendMessage(CMD_CAPTIVE_PORTAL_APP_FINISHED, response);
- }
-
- @Override
- public void logEvent(int eventId, String packageName)
- throws RemoteException {
- mContext.enforceCallingPermission(
- android.Manifest.permission.CONNECTIVITY_INTERNAL,
- "CaptivePortal");
- mCallback.logCaptivePortalLoginEvent(eventId, packageName);
- }
- }));
+ final Network network = new Network(mNetwork);
+ appExtras.putParcelable(ConnectivityManager.EXTRA_NETWORK, network);
final CaptivePortalProbeResult probeRes = mLastPortalProbeResult;
- intent.putExtra(EXTRA_CAPTIVE_PORTAL_URL, probeRes.detectUrl);
+ appExtras.putString(EXTRA_CAPTIVE_PORTAL_URL, probeRes.detectUrl);
if (probeRes.probeSpec != null) {
final String encodedSpec = probeRes.probeSpec.getEncodedSpec();
- intent.putExtra(EXTRA_CAPTIVE_PORTAL_PROBE_SPEC, encodedSpec);
+ appExtras.putString(EXTRA_CAPTIVE_PORTAL_PROBE_SPEC, encodedSpec);
}
- intent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_USER_AGENT,
+ appExtras.putString(ConnectivityManager.EXTRA_CAPTIVE_PORTAL_USER_AGENT,
mCaptivePortalUserAgent);
- intent.setFlags(
- Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
- mContext.startActivityAsUser(intent, UserHandle.CURRENT);
+ mCm.startCaptivePortalApp(network, appExtras);
return HANDLED;
default:
return NOT_HANDLED;
@@ -1311,6 +1290,7 @@ public class NetworkMonitor extends StateMachine {
urlConnection.setInstanceFollowRedirects(probeType == ValidationProbeEvent.PROBE_PAC);
urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
+ urlConnection.setRequestProperty("Connection", "close");
urlConnection.setUseCaches(false);
if (mCaptivePortalUserAgent != null) {
urlConnection.setRequestProperty("User-Agent", mCaptivePortalUserAgent);
@@ -1484,10 +1464,6 @@ public class NetworkMonitor extends StateMachine {
*/
private void sendNetworkConditionsBroadcast(boolean responseReceived, boolean isCaptivePortal,
long requestTimestampMs, long responseTimestampMs) {
- if (!mWifiManager.isScanAlwaysAvailable()) {
- return;
- }
-
if (!mSystemReady) {
return;
}
@@ -1495,6 +1471,10 @@ public class NetworkMonitor extends StateMachine {
Intent latencyBroadcast =
new Intent(NetworkMonitorUtils.ACTION_NETWORK_CONDITIONS_MEASURED);
if (mNetworkCapabilities.hasTransport(TRANSPORT_WIFI)) {
+ if (!mWifiManager.isScanAlwaysAvailable()) {
+ return;
+ }
+
WifiInfo currentWifiInfo = mWifiManager.getConnectionInfo();
if (currentWifiInfo != null) {
// NOTE: getSSID()'s behavior changed in API 17; before that, SSIDs were not
@@ -1514,39 +1494,21 @@ public class NetworkMonitor extends StateMachine {
}
latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CONNECTIVITY_TYPE, TYPE_WIFI);
} else if (mNetworkCapabilities.hasTransport(TRANSPORT_CELLULAR)) {
+ // TODO(b/123893112): Support multi-sim.
latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_NETWORK_TYPE,
mTelephonyManager.getNetworkType());
- List<CellInfo> info = mTelephonyManager.getAllCellInfo();
- if (info == null) return;
- int numRegisteredCellInfo = 0;
- for (CellInfo cellInfo : info) {
- if (cellInfo.isRegistered()) {
- numRegisteredCellInfo++;
- if (numRegisteredCellInfo > 1) {
- if (VDBG) {
- logw("more than one registered CellInfo."
- + " Can't tell which is active. Bailing.");
- }
- return;
- }
- if (cellInfo instanceof CellInfoCdma) {
- CellIdentityCdma cellId = ((CellInfoCdma) cellInfo).getCellIdentity();
- latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CELL_ID, cellId);
- } else if (cellInfo instanceof CellInfoGsm) {
- CellIdentityGsm cellId = ((CellInfoGsm) cellInfo).getCellIdentity();
- latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CELL_ID, cellId);
- } else if (cellInfo instanceof CellInfoLte) {
- CellIdentityLte cellId = ((CellInfoLte) cellInfo).getCellIdentity();
- latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CELL_ID, cellId);
- } else if (cellInfo instanceof CellInfoWcdma) {
- CellIdentityWcdma cellId = ((CellInfoWcdma) cellInfo).getCellIdentity();
- latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CELL_ID, cellId);
- } else {
- if (VDBG) logw("Registered cellinfo is unrecognized");
- return;
- }
- }
+ final ServiceState dataSs = mTelephonyManager.getServiceState();
+ if (dataSs == null) {
+ logw("failed to retrieve ServiceState");
+ return;
}
+ // See if the data sub is registered for PS services on cell.
+ final NetworkRegistrationState nrs = dataSs.getNetworkRegistrationState(
+ NetworkRegistrationState.DOMAIN_PS,
+ AccessNetworkConstants.TransportType.WWAN);
+ latencyBroadcast.putExtra(
+ NetworkMonitorUtils.EXTRA_CELL_ID,
+ nrs == null ? null : nrs.getCellIdentity());
latencyBroadcast.putExtra(NetworkMonitorUtils.EXTRA_CONNECTIVITY_TYPE, TYPE_MOBILE);
} else {
return;
diff --git a/packages/NetworkStack/src/com/android/server/util/PermissionUtil.java b/packages/NetworkStack/src/com/android/server/util/PermissionUtil.java
index 82bf038073c7..f6eb900c4910 100644
--- a/packages/NetworkStack/src/com/android/server/util/PermissionUtil.java
+++ b/packages/NetworkStack/src/com/android/server/util/PermissionUtil.java
@@ -19,6 +19,7 @@ package com.android.server.util;
import static android.os.Binder.getCallingUid;
import android.os.Process;
+import android.os.UserHandle;
/**
* Utility class to check calling permissions on the network stack.
@@ -32,7 +33,7 @@ public final class PermissionUtil {
public static void checkNetworkStackCallingPermission() {
// TODO: check that the calling PID is the system server.
final int caller = getCallingUid();
- if (caller != Process.SYSTEM_UID && caller != Process.BLUETOOTH_UID) {
+ if (caller != Process.SYSTEM_UID && UserHandle.getAppId(caller) != Process.BLUETOOTH_UID) {
throw new SecurityException("Invalid caller: " + caller);
}
}
diff --git a/packages/NetworkStack/tests/Android.bp b/packages/NetworkStack/tests/Android.bp
index 45fa2dc2f383..5c7b514834cb 100644
--- a/packages/NetworkStack/tests/Android.bp
+++ b/packages/NetworkStack/tests/Android.bp
@@ -18,6 +18,7 @@ android_test {
name: "NetworkStackTests",
certificate: "platform",
srcs: ["src/**/*.java"],
+ test_suites: ["device-tests"],
resource_dirs: ["res"],
static_libs: [
"android-support-test",
@@ -49,6 +50,7 @@ android_test {
"libhidlbase",
"libhidltransport",
"libhwbinder",
+ "libjsoncpp",
"liblog",
"liblzma",
"libnativehelper",
diff --git a/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java b/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java
index f76e41217c2a..3414397d73c3 100644
--- a/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java
+++ b/packages/NetworkStack/tests/src/android/net/apf/ApfTest.java
@@ -22,6 +22,7 @@ import static android.system.OsConstants.ETH_P_ARP;
import static android.system.OsConstants.ETH_P_IP;
import static android.system.OsConstants.ETH_P_IPV6;
import static android.system.OsConstants.IPPROTO_ICMPV6;
+import static android.system.OsConstants.IPPROTO_TCP;
import static android.system.OsConstants.IPPROTO_UDP;
import static android.system.OsConstants.SOCK_STREAM;
@@ -39,13 +40,12 @@ import static org.mockito.Mockito.verify;
import android.content.Context;
import android.net.LinkAddress;
import android.net.LinkProperties;
+import android.net.TcpKeepalivePacketDataParcelable;
import android.net.apf.ApfFilter.ApfConfiguration;
import android.net.apf.ApfGenerator.IllegalInstructionException;
import android.net.apf.ApfGenerator.Register;
import android.net.ip.IIpClientCallbacks;
-import android.net.ip.IpClient;
import android.net.ip.IpClient.IpClientCallbacksWrapper;
-import android.net.ip.IpClientCallbacks;
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.RaEvent;
import android.net.util.InterfaceParams;
@@ -1003,15 +1003,32 @@ public class ApfTest {
private static final byte[] ETH_BROADCAST_MAC_ADDRESS =
{(byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
+ private static final int IPV4_HEADER_LEN = 20;
private static final int IPV4_VERSION_IHL_OFFSET = ETH_HEADER_LEN + 0;
+ private static final int IPV4_TOTAL_LENGTH_OFFSET = ETH_HEADER_LEN + 2;
private static final int IPV4_PROTOCOL_OFFSET = ETH_HEADER_LEN + 9;
+ private static final int IPV4_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 12;
private static final int IPV4_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 16;
+ private static final int IPV4_TCP_HEADER_LEN = 20;
+ private static final int IPV4_TCP_HEADER_OFFSET = ETH_HEADER_LEN + IPV4_HEADER_LEN;
+ private static final int IPV4_TCP_SRC_PORT_OFFSET = IPV4_TCP_HEADER_OFFSET + 0;
+ private static final int IPV4_TCP_DEST_PORT_OFFSET = IPV4_TCP_HEADER_OFFSET + 2;
+ private static final int IPV4_TCP_SEQ_NUM_OFFSET = IPV4_TCP_HEADER_OFFSET + 4;
+ private static final int IPV4_TCP_ACK_NUM_OFFSET = IPV4_TCP_HEADER_OFFSET + 8;
+ private static final int IPV4_TCP_HEADER_LENGTH_OFFSET = IPV4_TCP_HEADER_OFFSET + 12;
+ private static final int IPV4_TCP_HEADER_FLAG_OFFSET = IPV4_TCP_HEADER_OFFSET + 13;
private static final byte[] IPV4_BROADCAST_ADDRESS =
{(byte) 255, (byte) 255, (byte) 255, (byte) 255};
private static final int IPV6_NEXT_HEADER_OFFSET = ETH_HEADER_LEN + 6;
private static final int IPV6_HEADER_LEN = 40;
+ private static final int IPV6_SRC_ADDR_OFFSET = ETH_HEADER_LEN + 8;
private static final int IPV6_DEST_ADDR_OFFSET = ETH_HEADER_LEN + 24;
+ private static final int IPV6_TCP_HEADER_OFFSET = ETH_HEADER_LEN + IPV6_HEADER_LEN;
+ private static final int IPV6_TCP_SRC_PORT_OFFSET = IPV6_TCP_HEADER_OFFSET + 0;
+ private static final int IPV6_TCP_DEST_PORT_OFFSET = IPV6_TCP_HEADER_OFFSET + 2;
+ private static final int IPV6_TCP_SEQ_NUM_OFFSET = IPV6_TCP_HEADER_OFFSET + 4;
+ private static final int IPV6_TCP_ACK_NUM_OFFSET = IPV6_TCP_HEADER_OFFSET + 8;
// The IPv6 all nodes address ff02::1
private static final byte[] IPV6_ALL_NODES_ADDRESS =
{ (byte) 0xff, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
@@ -1491,6 +1508,211 @@ public class ApfTest {
return packet.array();
}
+ private static final byte[] IPV4_KEEPALIVE_SRC_ADDR = {10, 0, 0, 5};
+ private static final byte[] IPV4_KEEPALIVE_DST_ADDR = {10, 0, 0, 6};
+ private static final byte[] IPV4_ANOTHER_ADDR = {10, 0 , 0, 7};
+ private static final byte[] IPV6_KEEPALIVE_SRC_ADDR =
+ {(byte) 0x24, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (byte) 0xfa, (byte) 0xf1};
+ private static final byte[] IPV6_KEEPALIVE_DST_ADDR =
+ {(byte) 0x24, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (byte) 0xfa, (byte) 0xf2};
+ private static final byte[] IPV6_ANOTHER_ADDR =
+ {(byte) 0x24, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, (byte) 0xfa, (byte) 0xf5};
+
+ @Test
+ public void testApfFilterKeepaliveAck() throws Exception {
+ final MockIpClientCallback cb = new MockIpClientCallback();
+ final ApfConfiguration config = getDefaultConfig();
+ config.multicastFilter = DROP_MULTICAST;
+ config.ieee802_3Filter = DROP_802_3_FRAMES;
+ final TestApfFilter apfFilter = new TestApfFilter(mContext, config, cb, mLog);
+ byte[] program;
+ final int srcPort = 12345;
+ final int dstPort = 54321;
+ final int seqNum = 2123456789;
+ final int ackNum = 1234567890;
+ final int anotherSrcPort = 23456;
+ final int anotherDstPort = 65432;
+ final int anotherSeqNum = 2123456780;
+ final int anotherAckNum = 1123456789;
+ final int slot1 = 1;
+ final int slot2 = 2;
+ final int window = 14480;
+ final int windowScale = 4;
+
+ // src: 10.0.0.5, port: 12345
+ // dst: 10.0.0.6, port: 54321
+ InetAddress srcAddr = InetAddress.getByAddress(IPV4_KEEPALIVE_SRC_ADDR);
+ InetAddress dstAddr = InetAddress.getByAddress(IPV4_KEEPALIVE_DST_ADDR);
+
+ final TcpKeepalivePacketDataParcelable parcel = new TcpKeepalivePacketDataParcelable();
+ parcel.srcAddress = srcAddr.getAddress();
+ parcel.srcPort = srcPort;
+ parcel.dstAddress = dstAddr.getAddress();
+ parcel.dstPort = dstPort;
+ parcel.seq = seqNum;
+ parcel.ack = ackNum;
+
+ apfFilter.addKeepalivePacketFilter(slot1, parcel);
+ program = cb.getApfProgram();
+
+ // Verify IPv4 keepalive ack packet is dropped
+ // src: 10.0.0.6, port: 54321
+ // dst: 10.0.0.5, port: 12345
+ assertDrop(program,
+ ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
+ dstPort, srcPort, ackNum, seqNum + 1, 0 /* dataLength */));
+ // Verify IPv4 non-keepalive ack packet from the same source address is passed
+ assertPass(program,
+ ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
+ dstPort, srcPort, ackNum + 100, seqNum, 0 /* dataLength */));
+ assertPass(program,
+ ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
+ dstPort, srcPort, ackNum, seqNum + 1, 10 /* dataLength */));
+ // Verify IPv4 packet from another address is passed
+ assertPass(program,
+ ipv4Packet(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, anotherSrcPort,
+ anotherDstPort, anotherSeqNum, anotherAckNum, 0 /* dataLength */));
+
+ // Remove IPv4 keepalive filter
+ apfFilter.removeKeepalivePacketFilter(slot1);
+
+ try {
+ // src: 2404:0:0:0:0:0:faf1, port: 12345
+ // dst: 2404:0:0:0:0:0:faf2, port: 54321
+ srcAddr = InetAddress.getByAddress(IPV6_KEEPALIVE_SRC_ADDR);
+ dstAddr = InetAddress.getByAddress(IPV6_KEEPALIVE_DST_ADDR);
+
+ final TcpKeepalivePacketDataParcelable ipv6Parcel =
+ new TcpKeepalivePacketDataParcelable();
+ ipv6Parcel.srcAddress = srcAddr.getAddress();
+ ipv6Parcel.srcPort = srcPort;
+ ipv6Parcel.dstAddress = dstAddr.getAddress();
+ ipv6Parcel.dstPort = dstPort;
+ ipv6Parcel.seq = seqNum;
+ ipv6Parcel.ack = ackNum;
+
+ apfFilter.addKeepalivePacketFilter(slot1, ipv6Parcel);
+ program = cb.getApfProgram();
+
+ // Verify IPv6 keepalive ack packet is dropped
+ // src: 2404:0:0:0:0:0:faf2, port: 54321
+ // dst: 2404:0:0:0:0:0:faf1, port: 12345
+ assertDrop(program,
+ ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR,
+ dstPort, srcPort, ackNum, seqNum + 1));
+ // Verify IPv6 non-keepalive ack packet from the same source address is passed
+ assertPass(program,
+ ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR,
+ dstPort, srcPort, ackNum + 100, seqNum));
+ // Verify IPv6 packet from another address is passed
+ assertPass(program,
+ ipv6Packet(IPV6_ANOTHER_ADDR, IPV6_KEEPALIVE_SRC_ADDR, anotherSrcPort,
+ anotherDstPort, anotherSeqNum, anotherAckNum));
+
+ // Remove IPv6 keepalive filter
+ apfFilter.removeKeepalivePacketFilter(slot1);
+
+ // Verify multiple filters
+ apfFilter.addKeepalivePacketFilter(slot1, parcel);
+ apfFilter.addKeepalivePacketFilter(slot2, ipv6Parcel);
+ program = cb.getApfProgram();
+
+ // Verify IPv4 keepalive ack packet is dropped
+ // src: 10.0.0.6, port: 54321
+ // dst: 10.0.0.5, port: 12345
+ assertDrop(program,
+ ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
+ dstPort, srcPort, ackNum, seqNum + 1, 0 /* dataLength */));
+ // Verify IPv4 non-keepalive ack packet from the same source address is passed
+ assertPass(program,
+ ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
+ dstPort, srcPort, ackNum + 100, seqNum, 0 /* dataLength */));
+ // Verify IPv4 packet from another address is passed
+ assertPass(program,
+ ipv4Packet(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, anotherSrcPort,
+ anotherDstPort, anotherSeqNum, anotherAckNum, 0 /* dataLength */));
+
+ // Verify IPv6 keepalive ack packet is dropped
+ // src: 2404:0:0:0:0:0:faf2, port: 54321
+ // dst: 2404:0:0:0:0:0:faf1, port: 12345
+ assertDrop(program,
+ ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR,
+ dstPort, srcPort, ackNum, seqNum + 1));
+ // Verify IPv6 non-keepalive ack packet from the same source address is passed
+ assertPass(program,
+ ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR,
+ dstPort, srcPort, ackNum + 100, seqNum));
+ // Verify IPv6 packet from another address is passed
+ assertPass(program,
+ ipv6Packet(IPV6_ANOTHER_ADDR, IPV6_KEEPALIVE_SRC_ADDR, anotherSrcPort,
+ anotherDstPort, anotherSeqNum, anotherAckNum));
+
+ // Remove keepalive filters
+ apfFilter.removeKeepalivePacketFilter(slot1);
+ apfFilter.removeKeepalivePacketFilter(slot2);
+ } catch (UnsupportedOperationException e) {
+ // TODO: support V6 packets
+ }
+
+ program = cb.getApfProgram();
+
+ // Verify IPv4, IPv6 packets are passed
+ assertPass(program,
+ ipv4Packet(IPV4_KEEPALIVE_DST_ADDR, IPV4_KEEPALIVE_SRC_ADDR,
+ dstPort, srcPort, ackNum, seqNum + 1, 0 /* dataLength */));
+ assertPass(program,
+ ipv6Packet(IPV6_KEEPALIVE_DST_ADDR, IPV6_KEEPALIVE_SRC_ADDR,
+ dstPort, srcPort, ackNum, seqNum + 1));
+ assertPass(program,
+ ipv4Packet(IPV4_ANOTHER_ADDR, IPV4_KEEPALIVE_SRC_ADDR, srcPort,
+ dstPort, anotherSeqNum, anotherAckNum, 0 /* dataLength */));
+ assertPass(program,
+ ipv6Packet(IPV6_ANOTHER_ADDR, IPV6_KEEPALIVE_SRC_ADDR, srcPort,
+ dstPort, anotherSeqNum, anotherAckNum));
+
+ apfFilter.shutdown();
+ }
+
+ private static byte[] ipv4Packet(byte[] sip, byte[] dip, int sport,
+ int dport, int seq, int ack, int dataLength) {
+ final int totalLength = dataLength + IPV4_HEADER_LEN + IPV4_TCP_HEADER_LEN;
+
+ ByteBuffer packet = ByteBuffer.wrap(new byte[totalLength + ETH_HEADER_LEN]);
+
+ // ether type
+ packet.putShort(ETH_ETHERTYPE_OFFSET, (short) ETH_P_IP);
+
+ // IPv4 header
+ packet.put(IPV4_VERSION_IHL_OFFSET, (byte) 0x45);
+ packet.putShort(IPV4_TOTAL_LENGTH_OFFSET, (short) totalLength);
+ packet.put(IPV4_PROTOCOL_OFFSET, (byte) IPPROTO_TCP);
+ put(packet, IPV4_SRC_ADDR_OFFSET, sip);
+ put(packet, IPV4_DEST_ADDR_OFFSET, dip);
+ packet.putShort(IPV4_TCP_SRC_PORT_OFFSET, (short) sport);
+ packet.putShort(IPV4_TCP_DEST_PORT_OFFSET, (short) dport);
+ packet.putInt(IPV4_TCP_SEQ_NUM_OFFSET, seq);
+ packet.putInt(IPV4_TCP_ACK_NUM_OFFSET, ack);
+
+ // TCP header length 5(20 bytes), reserved 3 bits, NS=0
+ packet.put(IPV4_TCP_HEADER_LENGTH_OFFSET, (byte) 0x50);
+ // TCP flags: ACK set
+ packet.put(IPV4_TCP_HEADER_FLAG_OFFSET, (byte) 0x10);
+ return packet.array();
+ }
+
+ private static byte[] ipv6Packet(byte[] sip, byte[] tip, int sport,
+ int dport, int seq, int ack) {
+ ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
+ packet.putShort(ETH_ETHERTYPE_OFFSET, (short) ETH_P_IPV6);
+ put(packet, IPV6_SRC_ADDR_OFFSET, sip);
+ put(packet, IPV6_DEST_ADDR_OFFSET, tip);
+ packet.putShort(IPV6_TCP_SRC_PORT_OFFSET, (short) sport);
+ packet.putShort(IPV6_TCP_DEST_PORT_OFFSET, (short) dport);
+ packet.putInt(IPV6_TCP_SEQ_NUM_OFFSET, seq);
+ packet.putInt(IPV6_TCP_ACK_NUM_OFFSET, ack);
+ return packet.array();
+ }
+
// Verify that the last program pushed to the IpClient.Callback properly filters the
// given packet for the given lifetime.
private void verifyRaLifetime(byte[] program, ByteBuffer packet, int lifetime) {
diff --git a/packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java b/packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java
index 7e57d1eb00b0..aaaff0279fed 100644
--- a/packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java
+++ b/packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java
@@ -104,8 +104,8 @@ public class IpClientTest {
when(mContext.getSystemService(eq(Context.ALARM_SERVICE))).thenReturn(mAlarm);
when(mContext.getSystemService(eq(ConnectivityManager.class))).thenReturn(mCm);
- when(mContext.getSystemService(INetd.class)).thenReturn(mNetd);
when(mContext.getResources()).thenReturn(mResources);
+ when(mDependencies.getNetd(any())).thenReturn(mNetd);
when(mResources.getInteger(R.integer.config_networkAvoidBadWifi))
.thenReturn(DEFAULT_AVOIDBADWIFI_CONFIG_VALUE);
diff --git a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
index d11bb64213c3..9a16bb77182e 100644
--- a/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
+++ b/packages/NetworkStack/tests/src/com/android/server/connectivity/NetworkMonitorTest.java
@@ -16,8 +16,7 @@
package com.android.server.connectivity;
-import static android.net.ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN;
-import static android.net.ConnectivityManager.EXTRA_CAPTIVE_PORTAL;
+import static android.net.CaptivePortal.APP_RETURN_DISMISSED;
import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_INVALID;
import static android.net.INetworkMonitor.NETWORK_TEST_RESULT_VALID;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
@@ -41,8 +40,6 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
-import android.content.Intent;
-import android.net.CaptivePortal;
import android.net.ConnectivityManager;
import android.net.INetworkMonitorCallbacks;
import android.net.InetAddresses;
@@ -54,10 +51,10 @@ import android.net.captiveportal.CaptivePortalProbeResult;
import android.net.metrics.IpConnectivityLog;
import android.net.util.SharedLog;
import android.net.wifi.WifiManager;
+import android.os.Bundle;
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.SystemClock;
-import android.os.UserHandle;
import android.provider.Settings;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -482,24 +479,28 @@ public class NetworkMonitorTest {
nm.notifyNetworkConnected();
verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1))
- .showProvisioningNotification(any());
+ .showProvisioningNotification(any(), any());
// Check that startCaptivePortalApp sends the expected intent.
nm.launchCaptivePortalApp();
- final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
- verify(mContext, timeout(HANDLER_TIMEOUT_MS).times(1))
- .startActivityAsUser(intentCaptor.capture(), eq(UserHandle.CURRENT));
- final Intent intent = intentCaptor.getValue();
- assertEquals(ACTION_CAPTIVE_PORTAL_SIGN_IN, intent.getAction());
- final Network network = intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK);
- assertEquals(TEST_NETID, network.netId);
+ final ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
+ final ArgumentCaptor<Network> networkCaptor = ArgumentCaptor.forClass(Network.class);
+ verify(mCm, timeout(HANDLER_TIMEOUT_MS).times(1))
+ .startCaptivePortalApp(networkCaptor.capture(), bundleCaptor.capture());
+ final Bundle bundle = bundleCaptor.getValue();
+ final Network bundleNetwork = bundle.getParcelable(ConnectivityManager.EXTRA_NETWORK);
+ assertEquals(TEST_NETID, bundleNetwork.netId);
+ // network is passed both in bundle and as parameter, as the bundle is opaque to the
+ // framework and only intended for the captive portal app, but the framework needs
+ // the network to identify the right NetworkMonitor.
+ assertEquals(TEST_NETID, networkCaptor.getValue().netId);
// Have the app report that the captive portal is dismissed, and check that we revalidate.
setStatus(mHttpsConnection, 204);
setStatus(mHttpConnection, 204);
- final CaptivePortal captivePortal = intent.getParcelableExtra(EXTRA_CAPTIVE_PORTAL);
- captivePortal.reportCaptivePortalDismissed();
+
+ nm.notifyCaptivePortalAppFinished(APP_RETURN_DISMISSED);
verify(mCallbacks, timeout(HANDLER_TIMEOUT_MS).times(1))
.notifyNetworkTested(NETWORK_TEST_RESULT_VALID, null);
}
diff --git a/packages/NetworkStackPermissionStub/Android.bp b/packages/NetworkStackPermissionStub/Android.bp
new file mode 100644
index 000000000000..dd70cf56b51b
--- /dev/null
+++ b/packages/NetworkStackPermissionStub/Android.bp
@@ -0,0 +1,27 @@
+//
+// Copyright (C) 2019 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.
+//
+
+// Stub APK to define permissions for NetworkStack
+android_app {
+ name: "NetworkStackPermissionStub",
+ // TODO: mark app as hasCode=false in manifest once soong stops complaining about apps without
+ // a classes.dex.
+ srcs: ["src/**/*.java"],
+ platform_apis: true,
+ certificate: "networkstack",
+ privileged: true,
+ manifest: "AndroidManifest.xml",
+}
diff --git a/packages/NetworkStackPermissionStub/AndroidManifest.xml b/packages/NetworkStackPermissionStub/AndroidManifest.xml
new file mode 100644
index 000000000000..a8742d7ab34f
--- /dev/null
+++ b/packages/NetworkStackPermissionStub/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2019 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.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.networkstack.permissionstub"
+ android:sharedUserId="android.uid.networkstack">
+ <!--
+ This package only exists to define the below permissions, and enforce that they are only
+ granted to apps sharing the same signature.
+ Permissions defined here are intended to be used only by the NetworkStack: both
+ NetworkStack and this stub APK are to be signed with a dedicated certificate to ensure
+ that, with the below permissions being signature permissions.
+
+ This APK *must* be installed, even if the NetworkStack app is not installed, because otherwise,
+ any application will be able to define this permission and the system will give that application
+ full access to the network stack.
+ -->
+ <permission android:name="android.permission.MAINLINE_NETWORK_STACK"
+ android:protectionLevel="signature"/>
+
+ <application android:name="com.android.server.NetworkStackPermissionStub"/>
+</manifest> \ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/RcsParticipantEvent.java b/packages/NetworkStackPermissionStub/src/com/android/server/NetworkStackPermissionStub.java
index 371b8b723d0a..01e59d28d995 100644
--- a/telephony/java/android/telephony/ims/RcsParticipantEvent.java
+++ b/packages/NetworkStackPermissionStub/src/com/android/server/NetworkStackPermissionStub.java
@@ -13,13 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.telephony.ims;
-import android.os.Parcelable;
+package com.android.server;
+
+import android.app.Application;
/**
- * An event that is associated with an {@link RcsParticipant}
- * @hide - TODO(sahinc) make this public
+ * Empty application for NetworkStackStub that only exists because soong builds complain if APKs
+ * have no source file.
*/
-public abstract class RcsParticipantEvent implements Parcelable {
+public class NetworkStackPermissionStub extends Application {
}
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 4791517d9273..0cae894e89a6 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -22,4 +22,20 @@ android_library {
}
+// NOTE: Keep this module in sync with ./common.mk
+java_defaults {
+ name: "SettingsLibDefaults",
+ static_libs: [
+ "androidx.annotation_annotation",
+ "androidx.lifecycle_lifecycle-common",
+ "androidx.legacy_legacy-support-v4",
+ "androidx.lifecycle_lifecycle-runtime",
+ "androidx.recyclerview_recyclerview",
+ "androidx.preference_preference",
+ "androidx.appcompat_appcompat",
+ "androidx.legacy_legacy-preference-v14",
+ "SettingsLib",
+ ],
+}
+
// For the test package.
diff --git a/packages/SettingsLib/common.mk b/packages/SettingsLib/common.mk
index 834b83b49ada..8c309ff97370 100644
--- a/packages/SettingsLib/common.mk
+++ b/packages/SettingsLib/common.mk
@@ -12,6 +12,11 @@
#
# include frameworks/base/packages/SettingsLib/common.mk
#
+# During the conversion to Soong bluprint files, the equivalent
+# functionality is provided by adding
+# defaults: ["SettingsLibDefaults"],
+# to the corresponding module.
+# NOTE: keep this file and ./Android.bp in sync.
LOCAL_STATIC_JAVA_LIBRARIES += \
androidx.annotation_annotation \
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 76a216d1be48..23e13c231c20 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -191,9 +191,9 @@
<string name="bluetooth_profile_a2dp_high_quality_unknown_codec">HD audio</string>
<!-- Bluetooth settings. The user-visible string that is used whenever referring to the Hearing Aid profile. -->
- <string name="bluetooth_profile_hearing_aid">Hearing Aid</string>
+ <string name="bluetooth_profile_hearing_aid">Hearing Aids</string>
<!-- Bluetooth settings. Connection options screen. The summary for the Hearing Aid checkbox preference when Hearing Aid is connected. -->
- <string name="bluetooth_hearing_aid_profile_summary_connected">Connected to Hearing Aid</string>
+ <string name="bluetooth_hearing_aid_profile_summary_connected">Connected to Hearing Aids</string>
<!-- Bluetooth settings. Connection options screen. The summary for the A2DP checkbox preference when A2DP is connected. -->
<string name="bluetooth_a2dp_profile_summary_connected">Connected to media audio</string>
@@ -233,7 +233,7 @@
will set the HID profile as preferred. -->
<string name="bluetooth_hid_profile_summary_use_for">Use for input</string>
<!-- Bluetooth settings. Connection options screen. The summary for the Hearing Aid checkbox preference that describes how checking it will set the Hearing Aid profile as preferred. -->
- <string name="bluetooth_hearing_aid_profile_summary_use_for">Use for Hearing Aid</string>
+ <string name="bluetooth_hearing_aid_profile_summary_use_for">Use for Hearing Aids</string>
<!-- Button text for accepting an incoming pairing request. [CHAR LIMIT=20] -->
<string name="bluetooth_pairing_accept">Pair</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 9f3401ea7463..6cd2f45213e3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -16,6 +16,7 @@
package com.android.settingslib.bluetooth;
+import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothHearingAid;
@@ -28,7 +29,7 @@ import android.os.ParcelUuid;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.Log;
-import android.bluetooth.BluetoothAdapter;
+
import androidx.annotation.VisibleForTesting;
import com.android.settingslib.R;
@@ -205,7 +206,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice>
// This is to ensure all the profiles are disconnected as some CK/Hs do not
// disconnect PBAP connection when HF connection is brought down
PbapServerProfile PbapProfile = mProfileManager.getPbapProfile();
- if (PbapProfile.getConnectionStatus(mDevice) == BluetoothProfile.STATE_CONNECTED)
+ if (PbapProfile != null && isConnectedProfile(PbapProfile))
{
PbapProfile.disconnect(mDevice);
}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 9306219415cd..b277666411f9 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -78,6 +78,7 @@
<uses-permission android:name="android.permission.MOVE_PACKAGE" />
<uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" />
<uses-permission android:name="android.permission.CLEAR_APP_CACHE" />
+ <uses-permission android:name="android.permission.ACCESS_INSTANT_APPS" />
<uses-permission android:name="android.permission.DELETE_CACHE_FILES" />
<uses-permission android:name="android.permission.DELETE_PACKAGES" />
<uses-permission android:name="android.permission.ACCESS_SURFACE_FLINGER" />
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index 5b3164e3c1fd..7d0291f643cf 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -78,6 +78,7 @@ android_app {
static_libs: [
"SystemUI-core",
],
+ resource_dirs: [],
platform_apis: true,
product_specific: true,
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index 8450b749910c..7a64f5ac4ef8 100755..100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -35,6 +35,7 @@ import android.media.session.PlaybackState;
import android.os.Debug;
import android.os.Handler;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
@@ -242,7 +243,8 @@ public class PipManager implements BasePipManager {
ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener);
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_MEDIA_RESOURCE_GRANTED);
- mContext.registerReceiver(mBroadcastReceiver, intentFilter);
+ mContext.registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, intentFilter,
+ null, null);
if (sSettingsPackageAndClassNamePairList == null) {
String[] settings = mContext.getResources().getStringArray(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index be46d2c7e4ae..be7403a1ab87 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -99,7 +99,6 @@ public class CarStatusBar extends StatusBar implements
Log.d(TAG, "Connecting to HVAC service");
Dependency.get(HvacController.class).connectToCarService();
}
- mCarFacetButtonController = Dependency.get(CarFacetButtonController.class);
mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
mDeviceIsProvisioned = mDeviceProvisionedController.isDeviceProvisioned();
if (!mDeviceIsProvisioned) {
@@ -117,7 +116,7 @@ public class CarStatusBar extends StatusBar implements
/**
* Remove all content from navbars and rebuild them. Used to allow for different nav bars
- * before and after the device is provisioned
+ * before and after the device is provisioned. Also for change of density and font size.
*/
private void restartNavBars() {
mCarFacetButtonController.removeAll();
@@ -216,6 +215,7 @@ public class CarStatusBar extends StatusBar implements
protected void makeStatusBarView() {
super.makeStatusBarView();
+ mCarFacetButtonController = Dependency.get(CarFacetButtonController.class);
mNotificationPanelBackground = getDefaultWallpaper();
mScrimController.setScrimBehindDrawable(mNotificationPanelBackground);
@@ -513,6 +513,7 @@ public class CarStatusBar extends StatusBar implements
@Override
public void onDensityOrFontScaleChanged() {
super.onDensityOrFontScaleChanged();
+ restartNavBars();
// Need to update the background on density changed in case the change was due to night
// mode.
mNotificationPanelBackground = getDefaultWallpaper();
diff --git a/proto/Android.bp b/proto/Android.bp
index f3811bdd7d81..9b7a1c15cf97 100644
--- a/proto/Android.bp
+++ b/proto/Android.bp
@@ -17,3 +17,24 @@ java_library_static {
},
},
}
+
+java_library_static {
+ name: "metrics-constants-protos",
+ host_supported: true,
+ proto: {
+ type: "nano",
+ },
+ srcs: ["src/metrics_constants.proto"],
+ no_framework_libs: true,
+ sdk_version: "system_current",
+ // Pin java_version until jarjar is certified to support later versions. http://b/72703434
+ java_version: "1.8",
+ target: {
+ android: {
+ jarjar_rules: "jarjar-rules.txt",
+ },
+ host: {
+ static_libs: ["libprotobuf-java-nano"],
+ },
+ },
+}
diff --git a/sax/tests/saxtests/Android.bp b/sax/tests/saxtests/Android.bp
new file mode 100644
index 000000000000..5889f769a645
--- /dev/null
+++ b/sax/tests/saxtests/Android.bp
@@ -0,0 +1,11 @@
+android_test {
+ name: "FrameworksSaxTests",
+ // Include all test java files.
+ srcs: ["src/**/*.java"],
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ ],
+ static_libs: ["junit"],
+ platform_apis: true,
+}
diff --git a/sax/tests/saxtests/Android.mk b/sax/tests/saxtests/Android.mk
deleted file mode 100644
index c4517a9a954a..000000000000
--- a/sax/tests/saxtests/Android.mk
+++ /dev/null
@@ -1,16 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# We only want this apk build for tests.
-LOCAL_MODULE_TAGS := tests
-
-# Include all test java files.
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
-LOCAL_STATIC_JAVA_LIBRARIES := junit
-LOCAL_PACKAGE_NAME := FrameworksSaxTests
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-include $(BUILD_PACKAGE)
-
diff --git a/services/accessibility/OWNERS b/services/accessibility/OWNERS
new file mode 100644
index 000000000000..265674a74b7e
--- /dev/null
+++ b/services/accessibility/OWNERS
@@ -0,0 +1,3 @@
+svetoslavganov@google.com
+pweaver@google.com
+rhedjao@google.com
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 9b8f51e49b26..48b69b029643 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -7,6 +7,7 @@ java_library_static {
"frameworks/native/cmds/dumpstate/binder",
"system/core/storaged/binder",
"system/vold/binder",
+ "system/gsid/aidl",
],
},
srcs: [
@@ -15,6 +16,7 @@ java_library_static {
":installd_aidl",
":storaged_aidl",
":vold_aidl",
+ ":gsiservice_aidl",
":mediaupdateservice_aidl",
"java/com/android/server/EventLogTags.logtags",
"java/com/android/server/am/EventLogTags.logtags",
@@ -45,7 +47,7 @@ java_library_static {
"android.hardware.vibrator-V1.0-java",
"android.hardware.configstore-V1.0-java",
"android.hardware.contexthub-V1.0-java",
- "android.hidl.manager-V1.0-java",
+ "android.hidl.manager-V1.2-java",
"netd_aidl_interface-java",
"netd_event_listener_interface-java",
],
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 9892bfa50189..b5fcde4bf203 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -38,7 +38,6 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
import static android.net.NetworkPolicyManager.RULE_NONE;
import static android.net.NetworkPolicyManager.uidRulesToString;
-import static android.net.NetworkStack.NETWORKSTACK_PACKAGE_NAME;
import static android.net.shared.NetworkMonitorUtils.isValidationRequired;
import static android.net.shared.NetworkParcelableUtil.toStableParcelable;
import static android.os.Process.INVALID_UID;
@@ -58,9 +57,10 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.database.ContentObserver;
+import android.net.CaptivePortal;
import android.net.ConnectionInfo;
import android.net.ConnectivityManager;
-import android.net.ConnectivityManager.PacketKeepalive;
+import android.net.ICaptivePortal;
import android.net.IConnectivityManager;
import android.net.IIpConnectivityMetrics;
import android.net.INetd;
@@ -87,12 +87,14 @@ import android.net.NetworkQuotaInfo;
import android.net.NetworkRequest;
import android.net.NetworkSpecifier;
import android.net.NetworkStack;
+import android.net.NetworkStackClient;
import android.net.NetworkState;
import android.net.NetworkUtils;
import android.net.NetworkWatchlistManager;
import android.net.PrivateDnsConfigParcel;
import android.net.ProxyInfo;
import android.net.RouteInfo;
+import android.net.SocketKeepalive;
import android.net.UidRange;
import android.net.Uri;
import android.net.VpnService;
@@ -917,7 +919,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
mPermissionMonitor = new PermissionMonitor(mContext, mNMS);
- //set up the listener for user state for creating user VPNs
+ // Set up the listener for user state for creating user VPNs.
+ // Should run on mHandler to avoid any races.
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_USER_STARTED);
intentFilter.addAction(Intent.ACTION_USER_STOPPED);
@@ -925,7 +928,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
intentFilter.addAction(Intent.ACTION_USER_REMOVED);
intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
mContext.registerReceiverAsUser(
- mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
+ mIntentReceiver,
+ UserHandle.ALL,
+ intentFilter,
+ null /* broadcastPermission */,
+ mHandler);
mContext.registerReceiverAsUser(mUserPresentReceiver, UserHandle.SYSTEM,
new IntentFilter(Intent.ACTION_USER_PRESENT), null, null);
@@ -936,7 +943,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
intentFilter.addDataScheme("package");
mContext.registerReceiverAsUser(
- mIntentReceiver, UserHandle.ALL, intentFilter, null, null);
+ mIntentReceiver,
+ UserHandle.ALL,
+ intentFilter,
+ null /* broadcastPermission */,
+ mHandler);
try {
mNMS.registerObserver(mTethering);
@@ -1832,14 +1843,20 @@ public class ConnectivityService extends IConnectivityManager.Stub
"ConnectivityService");
}
- private void enforceAnyPermissionOf(String... permissions) {
+ private boolean checkAnyPermissionOf(String... permissions) {
for (String permission : permissions) {
if (mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) {
- return;
+ return true;
}
}
- throw new SecurityException(
- "Requires one of the following permissions: " + String.join(", ", permissions) + ".");
+ return false;
+ }
+
+ private void enforceAnyPermissionOf(String... permissions) {
+ if (!checkAnyPermissionOf(permissions)) {
+ throw new SecurityException("Requires one of the following permissions: "
+ + String.join(", ", permissions) + ".");
+ }
}
private void enforceInternetPermission() {
@@ -1859,19 +1876,22 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
private void enforceSettingsPermission() {
- mContext.enforceCallingOrSelfPermission(
+ enforceAnyPermissionOf(
android.Manifest.permission.NETWORK_SETTINGS,
- "ConnectivityService");
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
}
private boolean checkSettingsPermission() {
- return PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.NETWORK_SETTINGS);
+ return checkAnyPermissionOf(
+ android.Manifest.permission.NETWORK_SETTINGS,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
}
private boolean checkSettingsPermission(int pid, int uid) {
return PERMISSION_GRANTED == mContext.checkPermission(
- android.Manifest.permission.NETWORK_SETTINGS, pid, uid);
+ android.Manifest.permission.NETWORK_SETTINGS, pid, uid)
+ || PERMISSION_GRANTED == mContext.checkPermission(
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, pid, uid);
}
private void enforceTetherAccessPermission() {
@@ -1881,9 +1901,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
private void enforceConnectivityInternalPermission() {
- mContext.enforceCallingOrSelfPermission(
+ enforceAnyPermissionOf(
android.Manifest.permission.CONNECTIVITY_INTERNAL,
- "ConnectivityService");
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
}
private void enforceControlAlwaysOnVpnPermission() {
@@ -1894,20 +1914,16 @@ public class ConnectivityService extends IConnectivityManager.Stub
private void enforceNetworkStackSettingsOrSetup() {
enforceAnyPermissionOf(
- android.Manifest.permission.NETWORK_SETTINGS,
- android.Manifest.permission.NETWORK_SETUP_WIZARD,
- android.Manifest.permission.NETWORK_STACK);
- }
-
- private void enforceNetworkStackPermission() {
- mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.NETWORK_SETTINGS,
+ android.Manifest.permission.NETWORK_SETUP_WIZARD,
android.Manifest.permission.NETWORK_STACK,
- "ConnectivityService");
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
}
private boolean checkNetworkStackPermission() {
- return PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.NETWORK_STACK);
+ return checkAnyPermissionOf(
+ android.Manifest.permission.NETWORK_STACK,
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
}
private void enforceConnectivityRestrictedNetworksPermission() {
@@ -2487,8 +2503,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
nai.networkMisc.acceptUnvalidated = msg.arg1 == 1;
break;
}
- case NetworkAgent.EVENT_PACKET_KEEPALIVE: {
- mKeepaliveTracker.handleEventPacketKeepalive(nai, msg);
+ case NetworkAgent.EVENT_SOCKET_KEEPALIVE: {
+ mKeepaliveTracker.handleEventSocketKeepalive(nai, msg);
break;
}
}
@@ -2661,9 +2677,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
@Override
- public void showProvisioningNotification(String action) {
+ public void showProvisioningNotification(String action, String packageName) {
final Intent intent = new Intent(action);
- intent.setPackage(NETWORKSTACK_PACKAGE_NAME);
+ intent.setPackage(packageName);
final PendingIntent pendingIntent;
// Only the system server can register notifications with package "android"
@@ -2685,11 +2701,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
EVENT_PROVISIONING_NOTIFICATION, PROVISIONING_NOTIFICATION_HIDE,
mNai.network.netId));
}
-
- @Override
- public void logCaptivePortalLoginEvent(int eventId, String packageName) {
- new MetricsLogger().action(eventId, packageName);
- }
}
private boolean networkRequiresValidation(NetworkAgentInfo nai) {
@@ -2828,6 +2839,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (DBG) {
log(nai.name() + " got DISCONNECTED, was satisfying " + nai.numNetworkRequests());
}
+ // Clear all notifications of this network.
+ mNotifier.clearNotification(nai.network.netId);
// A network agent has disconnected.
// TODO - if we move the logic to the network agent (have them disconnect
// because they lost all their requests or because their score isn't good)
@@ -2853,8 +2866,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
// sending all CALLBACK_LOST messages (for requests, not listens) at the end
// of rematchAllNetworksAndRequests
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOST);
- mKeepaliveTracker.handleStopAllKeepalives(nai,
- ConnectivityManager.PacketKeepalive.ERROR_INVALID_NETWORK);
+ mKeepaliveTracker.handleStopAllKeepalives(nai, SocketKeepalive.ERROR_INVALID_NETWORK);
for (String iface : nai.linkProperties.getAllInterfaceNames()) {
// Disable wakeup packet monitoring for each interface.
wakeupModifyInterface(iface, nai.networkCapabilities, false);
@@ -3231,6 +3243,66 @@ public class ConnectivityService extends IConnectivityManager.Stub
});
}
+ /**
+ * NetworkStack endpoint to start the captive portal app. The NetworkStack needs to use this
+ * endpoint as it does not have INTERACT_ACROSS_USERS_FULL itself.
+ * @param network Network on which the captive portal was detected.
+ * @param appExtras Bundle to use as intent extras for the captive portal application.
+ * Must be treated as opaque to avoid preventing the captive portal app to
+ * update its arguments.
+ */
+ @Override
+ public void startCaptivePortalAppInternal(Network network, Bundle appExtras) {
+ mContext.checkCallingOrSelfPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
+
+ final Intent appIntent = new Intent(ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN);
+ appIntent.putExtras(appExtras);
+ appIntent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL,
+ new CaptivePortal(new CaptivePortalImpl(network).asBinder()));
+ appIntent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT | Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ Binder.withCleanCallingIdentity(() ->
+ mContext.startActivityAsUser(appIntent, UserHandle.CURRENT));
+ }
+
+ private class CaptivePortalImpl extends ICaptivePortal.Stub {
+ private final Network mNetwork;
+
+ private CaptivePortalImpl(Network network) {
+ mNetwork = network;
+ }
+
+ @Override
+ public void appResponse(final int response) throws RemoteException {
+ if (response == CaptivePortal.APP_RETURN_WANTED_AS_IS) {
+ enforceSettingsPermission();
+ }
+
+ // getNetworkAgentInfoForNetwork is thread-safe
+ final NetworkAgentInfo nai = getNetworkAgentInfoForNetwork(mNetwork);
+ if (nai == null) return;
+
+ // nai.networkMonitor() is thread-safe
+ final INetworkMonitor nm = nai.networkMonitor();
+ if (nm == null) return;
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ nm.notifyCaptivePortalAppFinished(response);
+ } finally {
+ // Not using Binder.withCleanCallingIdentity() to keep the checked RemoteException
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
+ public void logEvent(int eventId, String packageName) {
+ enforceSettingsPermission();
+
+ new MetricsLogger().action(eventId, packageName);
+ }
+ }
+
public boolean avoidBadWifi() {
return mMultinetworkPolicyTracker.getAvoidBadWifi();
}
@@ -3446,12 +3518,12 @@ public class ConnectivityService extends IConnectivityManager.Stub
break;
}
// Sent by KeepaliveTracker to process an app request on the state machine thread.
- case NetworkAgent.CMD_START_PACKET_KEEPALIVE: {
+ case NetworkAgent.CMD_START_SOCKET_KEEPALIVE: {
mKeepaliveTracker.handleStartKeepalive(msg);
break;
}
// Sent by KeepaliveTracker to process an app request on the state machine thread.
- case NetworkAgent.CMD_STOP_PACKET_KEEPALIVE: {
+ case NetworkAgent.CMD_STOP_SOCKET_KEEPALIVE: {
NetworkAgentInfo nai = getNetworkAgentInfoForNetwork((Network) msg.obj);
int slot = msg.arg1;
int reason = msg.arg2;
@@ -3643,6 +3715,20 @@ public class ConnectivityService extends IConnectivityManager.Stub
mTethering.stopTethering(type);
}
+ /**
+ * Get the latest value of the tethering entitlement check.
+ *
+ * Note: Allow privileged apps who have TETHER_PRIVILEGED permission to access. If it turns
+ * out some such apps are observed to abuse this API, change to per-UID limits on this API
+ * if it's really needed.
+ */
+ @Override
+ public void getLatestTetheringEntitlementValue(int type, ResultReceiver receiver,
+ boolean showEntitlementUi, String callerPkg) {
+ ConnectivityManager.enforceTetherChangePermission(mContext, callerPkg);
+ mTethering.getLatestTetheringEntitlementValue(type, receiver, showEntitlementUi);
+ }
+
// Called when we lose the default network and have no replacement yet.
// This will automatically be cleared after X seconds or a new default network
// becomes CONNECTED, whichever happens first. The timer is started by the
@@ -4051,17 +4137,27 @@ public class ConnectivityService extends IConnectivityManager.Stub
* handler thread through their agent, this is asynchronous. When the capabilities objects
* are computed they will be up-to-date as they are computed synchronously from here and
* this is running on the ConnectivityService thread.
- * TODO : Fix this and call updateCapabilities inline to remove out-of-order events.
*/
private void updateAllVpnsCapabilities() {
+ Network defaultNetwork = getNetwork(getDefaultNetwork());
synchronized (mVpns) {
for (int i = 0; i < mVpns.size(); i++) {
final Vpn vpn = mVpns.valueAt(i);
- vpn.updateCapabilities();
+ NetworkCapabilities nc = vpn.updateCapabilities(defaultNetwork);
+ updateVpnCapabilities(vpn, nc);
}
}
}
+ private void updateVpnCapabilities(Vpn vpn, @Nullable NetworkCapabilities nc) {
+ ensureRunningOnConnectivityServiceThread();
+ NetworkAgentInfo vpnNai = getNetworkAgentInfoForNetId(vpn.getNetId());
+ if (vpnNai == null || nc == null) {
+ return;
+ }
+ updateCapabilities(vpnNai.getCurrentScore(), vpnNai, nc);
+ }
+
@Override
public boolean updateLockdownVpn() {
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
@@ -4402,22 +4498,28 @@ public class ConnectivityService extends IConnectivityManager.Stub
private void onUserAdded(int userId) {
mPermissionMonitor.onUserAdded(userId);
+ Network defaultNetwork = getNetwork(getDefaultNetwork());
synchronized (mVpns) {
final int vpnsSize = mVpns.size();
for (int i = 0; i < vpnsSize; i++) {
Vpn vpn = mVpns.valueAt(i);
vpn.onUserAdded(userId);
+ NetworkCapabilities nc = vpn.updateCapabilities(defaultNetwork);
+ updateVpnCapabilities(vpn, nc);
}
}
}
private void onUserRemoved(int userId) {
mPermissionMonitor.onUserRemoved(userId);
+ Network defaultNetwork = getNetwork(getDefaultNetwork());
synchronized (mVpns) {
final int vpnsSize = mVpns.size();
for (int i = 0; i < vpnsSize; i++) {
Vpn vpn = mVpns.valueAt(i);
vpn.onUserRemoved(userId);
+ NetworkCapabilities nc = vpn.updateCapabilities(defaultNetwork);
+ updateVpnCapabilities(vpn, nc);
}
}
}
@@ -4486,6 +4588,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
+ ensureRunningOnConnectivityServiceThread();
final String action = intent.getAction();
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
@@ -4990,6 +5093,19 @@ public class ConnectivityService extends IConnectivityManager.Stub
return getNetworkForRequest(mDefaultRequest.requestId);
}
+ @Nullable
+ private Network getNetwork(@Nullable NetworkAgentInfo nai) {
+ return nai != null ? nai.network : null;
+ }
+
+ private void ensureRunningOnConnectivityServiceThread() {
+ if (mHandler.getLooper().getThread() != Thread.currentThread()) {
+ throw new IllegalStateException(
+ "Not running on ConnectivityService thread: "
+ + Thread.currentThread().getName());
+ }
+ }
+
private boolean isDefaultNetwork(NetworkAgentInfo nai) {
return nai == getDefaultNetwork();
}
@@ -5019,7 +5135,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
if (DBG) log("registerNetworkAgent " + nai);
final long token = Binder.clearCallingIdentity();
try {
- mContext.getSystemService(NetworkStack.class).makeNetworkMonitor(
+ getNetworkStack().makeNetworkMonitor(
toStableParcelable(nai.network), name, new NetworkMonitorCallbacks(nai));
} finally {
Binder.restoreCallingIdentity(token);
@@ -5031,6 +5147,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
return nai.network.netId;
}
+ @VisibleForTesting
+ protected NetworkStackClient getNetworkStack() {
+ return NetworkStackClient.getInstance();
+ }
+
private void handleRegisterNetworkAgent(NetworkAgentInfo nai, INetworkMonitor networkMonitor) {
nai.onNetworkMonitorCreated(networkMonitor);
if (VDBG) log("Got NetworkAgent Messenger");
@@ -5583,6 +5704,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
updateTcpBufferSizes(newNetwork.linkProperties.getTcpBufferSizes());
mDnsManager.setDefaultDnsSystemProperties(newNetwork.linkProperties.getDnsServers());
notifyIfacesChangedForNetworkStats();
+ // Fix up the NetworkCapabilities of any VPNs that don't specify underlying networks.
+ updateAllVpnsCapabilities();
}
private void processListenRequests(NetworkAgentInfo nai, boolean capabilitiesChanged) {
@@ -6022,6 +6145,10 @@ public class ConnectivityService extends IConnectivityManager.Stub
// doing.
updateSignalStrengthThresholds(networkAgent, "CONNECT", null);
+ if (networkAgent.isVPN()) {
+ updateAllVpnsCapabilities();
+ }
+
// Consider network even though it is not yet validated.
final long now = SystemClock.elapsedRealtime();
rematchNetworkAndRequests(networkAgent, ReapUnvalidatedNetworks.REAP, now);
@@ -6283,7 +6410,11 @@ public class ConnectivityService extends IConnectivityManager.Stub
success = mVpns.get(user).setUnderlyingNetworks(networks);
}
if (success) {
- mHandler.post(() -> notifyIfacesChangedForNetworkStats());
+ mHandler.post(() -> {
+ // Update VPN's capabilities based on updated underlying network set.
+ updateAllVpnsCapabilities();
+ notifyIfacesChangedForNetworkStats();
+ });
}
return success;
}
@@ -6301,7 +6432,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
mKeepaliveTracker.startNattKeepalive(
getNetworkAgentInfoForNetwork(network),
intervalSeconds, messenger, binder,
- srcAddr, srcPort, dstAddr, ConnectivityManager.PacketKeepalive.NATT_PORT);
+ srcAddr, srcPort, dstAddr, NattSocketKeepalive.NATT_PORT);
}
@Override
@@ -6316,9 +6447,17 @@ public class ConnectivityService extends IConnectivityManager.Stub
}
@Override
+ public void startTcpKeepalive(Network network, FileDescriptor fd, int intervalSeconds,
+ Messenger messenger, IBinder binder) {
+ enforceKeepalivePermission();
+ mKeepaliveTracker.startTcpKeepalive(
+ getNetworkAgentInfoForNetwork(network), fd, intervalSeconds, messenger, binder);
+ }
+
+ @Override
public void stopKeepalive(Network network, int slot) {
mHandler.sendMessage(mHandler.obtainMessage(
- NetworkAgent.CMD_STOP_PACKET_KEEPALIVE, slot, PacketKeepalive.SUCCESS, network));
+ NetworkAgent.CMD_STOP_SOCKET_KEEPALIVE, slot, SocketKeepalive.SUCCESS, network));
}
@Override
diff --git a/services/core/java/com/android/server/DynamicAndroidService.java b/services/core/java/com/android/server/DynamicAndroidService.java
new file mode 100644
index 000000000000..12a3f02325d2
--- /dev/null
+++ b/services/core/java/com/android/server/DynamicAndroidService.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2019 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;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.gsi.GsiProgress;
+import android.gsi.IGsiService;
+import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
+import android.os.IDynamicAndroidService;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Slog;
+
+/**
+ * DynamicAndroidService implements IDynamicAndroidService. It provides permission check before
+ * passing requests to gsid
+ */
+public class DynamicAndroidService extends IDynamicAndroidService.Stub implements DeathRecipient {
+ private static final String TAG = "DynamicAndroidService";
+ private static final String NO_SERVICE_ERROR = "no gsiservice";
+
+ private Context mContext;
+ private volatile IGsiService mGsiService;
+
+ DynamicAndroidService(Context context) {
+ mContext = context;
+ }
+
+ private static IGsiService connect(DeathRecipient recipient) throws RemoteException {
+ IBinder binder = ServiceManager.getService("gsiservice");
+ if (binder == null) {
+ throw new RemoteException(NO_SERVICE_ERROR);
+ }
+ /**
+ * The init will restart gsiservice if it crashed and the proxy object will need to be
+ * re-initialized in this case.
+ */
+ binder.linkToDeath(recipient, 0);
+ return IGsiService.Stub.asInterface(binder);
+ }
+
+ /** implements DeathRecipient */
+ @Override
+ public void binderDied() {
+ Slog.w(TAG, "gsiservice died; reconnecting");
+ synchronized (this) {
+ mGsiService = null;
+ }
+ }
+
+ private IGsiService getGsiService() throws RemoteException {
+ checkPermission();
+ synchronized (this) {
+ if (mGsiService == null) {
+ mGsiService = connect(this);
+ }
+ return mGsiService;
+ }
+ }
+
+ private void checkPermission() {
+ if (mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.MANAGE_DYNAMIC_ANDROID)
+ != PackageManager.PERMISSION_GRANTED) {
+ throw new SecurityException("Requires MANAGE_DYNAMIC_ANDROID permission");
+ }
+ }
+
+ @Override
+ public boolean startInstallation(long systemSize, long userdataSize) throws RemoteException {
+ return getGsiService().startGsiInstall(systemSize, userdataSize, true) == 0;
+ }
+
+ @Override
+ public GsiProgress getInstallationProgress() throws RemoteException {
+ return getGsiService().getInstallProgress();
+ }
+
+ @Override
+ public boolean abort() throws RemoteException {
+ return getGsiService().cancelGsiInstall();
+ }
+
+ @Override
+ public boolean isInUse() throws RemoteException {
+ return getGsiService().isGsiRunning();
+ }
+
+ @Override
+ public boolean isInstalled() throws RemoteException {
+ return getGsiService().isGsiInstalled();
+ }
+
+ @Override
+ public boolean remove() throws RemoteException {
+ return getGsiService().removeGsiInstall();
+ }
+
+ @Override
+ public boolean toggle() throws RemoteException {
+ IGsiService gsiService = getGsiService();
+ if (gsiService.isGsiRunning()) {
+ return gsiService.disableGsiInstall();
+ } else {
+ return gsiService.setGsiBootable() == 0;
+ }
+ }
+
+ @Override
+ public boolean write(byte[] buf) throws RemoteException {
+ return getGsiService().commitGsiChunkFromMemory(buf);
+ }
+
+ @Override
+ public boolean commit() throws RemoteException {
+ return getGsiService().setGsiBootable() == 0;
+ }
+}
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index da4df22d7b02..a4fda8e9f57b 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -20,18 +20,18 @@ import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
import static android.Manifest.permission.NETWORK_SETTINGS;
import static android.Manifest.permission.NETWORK_STACK;
import static android.Manifest.permission.SHUTDOWN;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE;
+import static android.net.INetd.FIREWALL_BLACKLIST;
+import static android.net.INetd.FIREWALL_CHAIN_DOZABLE;
+import static android.net.INetd.FIREWALL_CHAIN_NONE;
+import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE;
+import static android.net.INetd.FIREWALL_CHAIN_STANDBY;
+import static android.net.INetd.FIREWALL_RULE_ALLOW;
+import static android.net.INetd.FIREWALL_RULE_DENY;
+import static android.net.INetd.FIREWALL_WHITELIST;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NONE;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_POWERSAVE;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY;
-import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW;
import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
-import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY;
-import static android.net.NetworkPolicyManager.FIREWALL_TYPE_BLACKLIST;
-import static android.net.NetworkPolicyManager.FIREWALL_TYPE_WHITELIST;
import static android.net.NetworkStats.SET_DEFAULT;
import static android.net.NetworkStats.STATS_PER_UID;
import static android.net.NetworkStats.TAG_ALL;
@@ -1941,7 +1941,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub
int numUids = 0;
if (DBG) Slog.d(TAG, "Closing sockets after enabling chain " + chainName);
- if (getFirewallType(chain) == FIREWALL_TYPE_WHITELIST) {
+ if (getFirewallType(chain) == FIREWALL_WHITELIST) {
// Close all sockets on all non-system UIDs...
ranges = new UidRange[] {
// TODO: is there a better way of finding all existing users? If so, we could
@@ -1953,7 +1953,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub
final SparseIntArray rules = getUidFirewallRulesLR(chain);
exemptUids = new int[rules.size()];
for (int i = 0; i < exemptUids.length; i++) {
- if (rules.valueAt(i) == NetworkPolicyManager.FIREWALL_RULE_ALLOW) {
+ if (rules.valueAt(i) == FIREWALL_RULE_ALLOW) {
exemptUids[numUids] = rules.keyAt(i);
numUids++;
}
@@ -1975,7 +1975,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub
final SparseIntArray rules = getUidFirewallRulesLR(chain);
ranges = new UidRange[rules.size()];
for (int i = 0; i < ranges.length; i++) {
- if (rules.valueAt(i) == NetworkPolicyManager.FIREWALL_RULE_DENY) {
+ if (rules.valueAt(i) == FIREWALL_RULE_DENY) {
int uid = rules.keyAt(i);
ranges[numUids] = new UidRange(uid, uid);
numUids++;
@@ -2047,13 +2047,13 @@ public class NetworkManagementService extends INetworkManagementService.Stub
private int getFirewallType(int chain) {
switch (chain) {
case FIREWALL_CHAIN_STANDBY:
- return FIREWALL_TYPE_BLACKLIST;
+ return FIREWALL_BLACKLIST;
case FIREWALL_CHAIN_DOZABLE:
- return FIREWALL_TYPE_WHITELIST;
+ return FIREWALL_WHITELIST;
case FIREWALL_CHAIN_POWERSAVE:
- return FIREWALL_TYPE_WHITELIST;
+ return FIREWALL_WHITELIST;
default:
- return isFirewallEnabled() ? FIREWALL_TYPE_WHITELIST : FIREWALL_TYPE_BLACKLIST;
+ return isFirewallEnabled() ? FIREWALL_WHITELIST : FIREWALL_BLACKLIST;
}
}
@@ -2155,14 +2155,14 @@ public class NetworkManagementService extends INetworkManagementService.Stub
private @NonNull String getFirewallRuleName(int chain, int rule) {
String ruleName;
- if (getFirewallType(chain) == FIREWALL_TYPE_WHITELIST) {
- if (rule == NetworkPolicyManager.FIREWALL_RULE_ALLOW) {
+ if (getFirewallType(chain) == FIREWALL_WHITELIST) {
+ if (rule == FIREWALL_RULE_ALLOW) {
ruleName = "allow";
} else {
ruleName = "deny";
}
} else { // Blacklist mode
- if (rule == NetworkPolicyManager.FIREWALL_RULE_DENY) {
+ if (rule == FIREWALL_RULE_DENY) {
ruleName = "deny";
} else {
ruleName = "allow";
@@ -2188,7 +2188,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub
private int getFirewallRuleType(int chain, int rule) {
if (rule == NetworkPolicyManager.FIREWALL_RULE_DEFAULT) {
- return getFirewallType(chain) == FIREWALL_TYPE_WHITELIST
+ return getFirewallType(chain) == FIREWALL_WHITELIST
? INetd.FIREWALL_RULE_DENY : INetd.FIREWALL_RULE_ALLOW;
}
return rule;
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 43af36f86f3d..3b5c9f53d9a1 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -26,6 +26,7 @@ import android.content.pm.PackageManager;
import android.net.LinkProperties;
import android.net.NetworkCapabilities;
import android.os.Binder;
+import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -246,7 +247,10 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
private PreciseDataConnectionState mPreciseDataConnectionState =
new PreciseDataConnectionState();
- static final int ENFORCE_COARSE_LOCATION_PERMISSION_MASK =
+ // Nothing here yet, but putting it here in case we want to add more in the future.
+ static final int ENFORCE_COARSE_LOCATION_PERMISSION_MASK = 0;
+
+ static final int ENFORCE_FINE_LOCATION_PERMISSION_MASK =
PhoneStateListener.LISTEN_CELL_LOCATION
| PhoneStateListener.LISTEN_CELL_INFO;
@@ -637,8 +641,14 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
if ((events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) {
try {
if (VDBG) log("listen: call onSSC state=" + mServiceState[phoneId]);
- r.callback.onServiceStateChanged(
- new ServiceState(mServiceState[phoneId]));
+ ServiceState rawSs = new ServiceState(mServiceState[phoneId]);
+ if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
+ r.callback.onServiceStateChanged(rawSs);
+ } else if (checkCoarseLocationAccess(r, Build.VERSION_CODES.Q)) {
+ r.callback.onServiceStateChanged(rawSs.sanitizeLocationInfo(false));
+ } else {
+ r.callback.onServiceStateChanged(rawSs.sanitizeLocationInfo(true));
+ }
} catch (RemoteException ex) {
remove(r.binder);
}
@@ -673,7 +683,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
try {
if (DBG_LOC) log("listen: mCellLocation = "
+ mCellLocation[phoneId]);
- if (checkLocationAccess(r)) {
+ if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
r.callback.onCellLocationChanged(
new Bundle(mCellLocation[phoneId]));
}
@@ -722,7 +732,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
try {
if (DBG_LOC) log("listen: mCellInfo[" + phoneId + "] = "
+ mCellInfo.get(phoneId));
- if (checkLocationAccess(r)) {
+ if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
}
} catch (RemoteException ex) {
@@ -1009,13 +1019,22 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_SERVICE_STATE) &&
idMatch(r.subId, subId, phoneId)) {
+
try {
+ ServiceState stateToSend;
+ if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
+ stateToSend = new ServiceState(state);
+ } else if (checkCoarseLocationAccess(r, Build.VERSION_CODES.Q)) {
+ stateToSend = state.sanitizeLocationInfo(false);
+ } else {
+ stateToSend = state.sanitizeLocationInfo(true);
+ }
if (DBG) {
log("notifyServiceStateForSubscriber: callback.onSSC r=" + r
+ " subId=" + subId + " phoneId=" + phoneId
+ " state=" + state);
}
- r.callback.onServiceStateChanged(new ServiceState(state));
+ r.callback.onServiceStateChanged(stateToSend);
} catch (RemoteException ex) {
mRemoveList.add(r.binder);
}
@@ -1198,7 +1217,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
for (Record r : mRecords) {
if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO) &&
idMatch(r.subId, subId, phoneId) &&
- checkLocationAccess(r)) {
+ checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
try {
if (DBG_LOC) {
log("notifyCellInfo: mCellInfo=" + cellInfo + " r=" + r);
@@ -1500,7 +1519,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
for (Record r : mRecords) {
if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_LOCATION) &&
idMatch(r.subId, subId, phoneId) &&
- checkLocationAccess(r)) {
+ checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
try {
if (DBG_LOC) {
log("notifyCellLocation: cellLocation=" + cellLocation
@@ -2109,12 +2128,35 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
private boolean checkListenerPermission(
int events, int subId, String callingPackage, String message) {
+ LocationAccessPolicy.LocationPermissionQuery.Builder locationQueryBuilder =
+ new LocationAccessPolicy.LocationPermissionQuery.Builder()
+ .setCallingPackage(callingPackage)
+ .setMethod(message + " events: " + events)
+ .setCallingPid(Binder.getCallingPid())
+ .setCallingUid(Binder.getCallingUid());
+
+ boolean shouldCheckLocationPermissions = false;
if ((events & ENFORCE_COARSE_LOCATION_PERMISSION_MASK) != 0) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
- if (mAppOps.noteOp(AppOpsManager.OP_COARSE_LOCATION, Binder.getCallingUid(),
- callingPackage) != AppOpsManager.MODE_ALLOWED) {
- return false;
+ locationQueryBuilder.setMinSdkVersionForCoarse(0);
+ shouldCheckLocationPermissions = true;
+ }
+
+ if ((events & ENFORCE_FINE_LOCATION_PERMISSION_MASK) != 0) {
+ // Everything that requires fine location started in Q. So far...
+ locationQueryBuilder.setMinSdkVersionForFine(Build.VERSION_CODES.Q);
+ shouldCheckLocationPermissions = true;
+ }
+
+ if (shouldCheckLocationPermissions) {
+ LocationAccessPolicy.LocationPermissionResult result =
+ LocationAccessPolicy.checkLocationPermission(
+ mContext, locationQueryBuilder.build());
+ switch (result) {
+ case DENIED_HARD:
+ throw new SecurityException("Unable to listen for events " + events + " due to "
+ + "insufficient location permissions.");
+ case DENIED_SOFT:
+ return false;
}
}
@@ -2229,15 +2271,38 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
}
}
- private boolean checkLocationAccess(Record r) {
- long token = Binder.clearCallingIdentity();
- try {
- return LocationAccessPolicy.canAccessCellLocation(mContext,
- r.callingPackage, r.callerUid, r.callerPid,
- /*throwOnDeniedPermission*/ false);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ private boolean checkFineLocationAccess(Record r, int minSdk) {
+ LocationAccessPolicy.LocationPermissionQuery query =
+ new LocationAccessPolicy.LocationPermissionQuery.Builder()
+ .setCallingPackage(r.callingPackage)
+ .setCallingPid(r.callerPid)
+ .setCallingUid(r.callerUid)
+ .setMethod("TelephonyRegistry push")
+ .setMinSdkVersionForFine(minSdk)
+ .build();
+
+ return Binder.withCleanCallingIdentity(() -> {
+ LocationAccessPolicy.LocationPermissionResult locationResult =
+ LocationAccessPolicy.checkLocationPermission(mContext, query);
+ return locationResult == LocationAccessPolicy.LocationPermissionResult.ALLOWED;
+ });
+ }
+
+ private boolean checkCoarseLocationAccess(Record r, int minSdk) {
+ LocationAccessPolicy.LocationPermissionQuery query =
+ new LocationAccessPolicy.LocationPermissionQuery.Builder()
+ .setCallingPackage(r.callingPackage)
+ .setCallingPid(r.callerPid)
+ .setCallingUid(r.callerUid)
+ .setMethod("TelephonyRegistry push")
+ .setMinSdkVersionForCoarse(minSdk)
+ .build();
+
+ return Binder.withCleanCallingIdentity(() -> {
+ LocationAccessPolicy.LocationPermissionResult locationResult =
+ LocationAccessPolicy.checkLocationPermission(mContext, query);
+ return locationResult == LocationAccessPolicy.LocationPermissionResult.ALLOWED;
+ });
}
private void checkPossibleMissNotify(Record r, int phoneId) {
@@ -2287,7 +2352,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
log("checkPossibleMissNotify: onCellInfoChanged[" + phoneId + "] = "
+ mCellInfo.get(phoneId));
}
- if (checkLocationAccess(r)) {
+ if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
r.callback.onCellInfoChanged(mCellInfo.get(phoneId));
}
} catch (RemoteException ex) {
@@ -2337,7 +2402,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub {
try {
if (DBG_LOC) log("checkPossibleMissNotify: onCellLocationChanged mCellLocation = "
+ mCellLocation[phoneId]);
- if (checkLocationAccess(r)) {
+ if (checkFineLocationAccess(r, Build.VERSION_CODES.Q)) {
r.callback.onCellLocationChanged(new Bundle(mCellLocation[phoneId]));
}
} catch (RemoteException ex) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 8e80c744b75b..06d1ca632363 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -365,6 +365,7 @@ import android.provider.Downloads;
import android.provider.Settings;
import android.service.voice.IVoiceInteractionSession;
import android.service.voice.VoiceInteractionManagerInternal;
+import android.sysprop.DisplayProperties;
import android.sysprop.VoldProperties;
import android.telecom.TelecomManager;
import android.text.TextUtils;
@@ -7435,6 +7436,7 @@ public class ActivityManagerService extends IActivityManager.Stub
// next app record if we are emulating process with anonymous threads.
ProcessRecord app;
long startTime = SystemClock.uptimeMillis();
+ long bindApplicationTimeMillis;
if (pid != MY_PID && pid >= 0) {
synchronized (mPidsSelfLocked) {
app = mPidsSelfLocked.get(pid);
@@ -7665,6 +7667,7 @@ public class ActivityManagerService extends IActivityManager.Stub
}
checkTime(startTime, "attachApplicationLocked: immediately before bindApplication");
+ bindApplicationTimeMillis = SystemClock.elapsedRealtime();
mStackSupervisor.getActivityMetricsLogger().notifyBindApplication(app);
if (app.isolatedEntryPoint != null) {
// This is an isolated process which should just call an entry point instead of
@@ -7783,6 +7786,18 @@ public class ActivityManagerService extends IActivityManager.Stub
checkTime(startTime, "attachApplicationLocked: after updateOomAdjLocked");
}
+ StatsLog.write(
+ StatsLog.PROCESS_START_TIME,
+ app.info.uid,
+ app.pid,
+ app.info.packageName,
+ StatsLog.PROCESS_START_TIME__TYPE__COLD,
+ app.startTime,
+ (int) (bindApplicationTimeMillis - app.startTime),
+ (int) (SystemClock.elapsedRealtime() - app.startTime),
+ app.hostingType,
+ (app.hostingNameStr != null ? app.hostingNameStr : ""));
+
return true;
}
@@ -14937,8 +14952,8 @@ public class ActivityManagerService extends IActivityManager.Stub
mContext.getPackageManager().hasSystemFeature(FEATURE_LEANBACK_ONLY);
mHiddenApiBlacklist.registerObserver();
- // Transfer any global setting for forcing RTL layout, into a System Property
- SystemProperties.set(DEVELOPMENT_FORCE_RTL, forceRtl ? "1":"0");
+ // Transfer any global setting for forcing RTL layout, into a Display Property
+ DisplayProperties.debug_force_rtl(forceRtl);
final Configuration configuration = new Configuration();
Settings.System.getConfiguration(resolver, configuration);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 455a3e302480..e698b841a2fd 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -3260,6 +3260,21 @@ public class AudioService extends IAudioService.Stub
if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) {
return;
}
+
+ if (mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.MODIFY_PHONE_STATE)
+ != PackageManager.PERMISSION_GRANTED) {
+ synchronized (mSetModeDeathHandlers) {
+ for (SetModeDeathHandler h : mSetModeDeathHandlers) {
+ if (h.getMode() == AudioSystem.MODE_IN_CALL) {
+ Log.w(TAG, "getMode is call, Permission Denial: setSpeakerphoneOn from pid="
+ + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid());
+ return;
+ }
+ }
+ }
+ }
+
// for logging only
final String eventSource = new StringBuilder("setSpeakerphoneOn(").append(on)
.append(") from u/pid:").append(Binder.getCallingUid()).append("/")
diff --git a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
index 1559ba8ba883..6cff57d4bbb1 100644
--- a/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
+++ b/services/core/java/com/android/server/connectivity/KeepaliveTracker.java
@@ -16,26 +16,32 @@
package com.android.server.connectivity;
-// TODO: Clean up imports and remove references of PacketKeepalive constants.
-
-import static android.net.ConnectivityManager.PacketKeepalive.ERROR_INVALID_INTERVAL;
-import static android.net.ConnectivityManager.PacketKeepalive.ERROR_INVALID_IP_ADDRESS;
-import static android.net.ConnectivityManager.PacketKeepalive.ERROR_INVALID_NETWORK;
-import static android.net.ConnectivityManager.PacketKeepalive.MIN_INTERVAL;
-import static android.net.ConnectivityManager.PacketKeepalive.NATT_PORT;
-import static android.net.ConnectivityManager.PacketKeepalive.NO_KEEPALIVE;
-import static android.net.ConnectivityManager.PacketKeepalive.SUCCESS;
-import static android.net.NetworkAgent.CMD_START_PACKET_KEEPALIVE;
-import static android.net.NetworkAgent.CMD_STOP_PACKET_KEEPALIVE;
-import static android.net.NetworkAgent.EVENT_PACKET_KEEPALIVE;
+import static android.net.NattSocketKeepalive.NATT_PORT;
+import static android.net.NetworkAgent.CMD_ADD_KEEPALIVE_PACKET_FILTER;
+import static android.net.NetworkAgent.CMD_REMOVE_KEEPALIVE_PACKET_FILTER;
+import static android.net.NetworkAgent.CMD_START_SOCKET_KEEPALIVE;
+import static android.net.NetworkAgent.CMD_STOP_SOCKET_KEEPALIVE;
+import static android.net.NetworkAgent.EVENT_SOCKET_KEEPALIVE;
+import static android.net.SocketKeepalive.BINDER_DIED;
+import static android.net.SocketKeepalive.ERROR_INVALID_INTERVAL;
+import static android.net.SocketKeepalive.ERROR_INVALID_IP_ADDRESS;
+import static android.net.SocketKeepalive.ERROR_INVALID_NETWORK;
import static android.net.SocketKeepalive.ERROR_INVALID_SOCKET;
+import static android.net.SocketKeepalive.MAX_INTERVAL_SEC;
+import static android.net.SocketKeepalive.MIN_INTERVAL_SEC;
+import static android.net.SocketKeepalive.NO_KEEPALIVE;
+import static android.net.SocketKeepalive.SUCCESS;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.net.ConnectivityManager.PacketKeepalive;
import android.net.KeepalivePacketData;
+import android.net.NattKeepalivePacketData;
import android.net.NetworkAgent;
import android.net.NetworkUtils;
+import android.net.SocketKeepalive.InvalidPacketException;
+import android.net.SocketKeepalive.InvalidSocketException;
+import android.net.TcpKeepalivePacketData;
+import android.net.TcpKeepalivePacketData.TcpSocketInfo;
import android.net.util.IpUtils;
import android.os.Binder;
import android.os.Handler;
@@ -60,11 +66,11 @@ import java.util.ArrayList;
import java.util.HashMap;
/**
- * Manages packet keepalive requests.
+ * Manages socket keepalive requests.
*
* Provides methods to stop and start keepalive requests, and keeps track of keepalives across all
* networks. This class is tightly coupled to ConnectivityService. It is not thread-safe and its
- * methods must be called only from the ConnectivityService handler thread.
+ * handle* methods must be called only from the ConnectivityService handler thread.
*/
public class KeepaliveTracker {
@@ -77,38 +83,54 @@ public class KeepaliveTracker {
private final HashMap <NetworkAgentInfo, HashMap<Integer, KeepaliveInfo>> mKeepalives =
new HashMap<> ();
private final Handler mConnectivityServiceHandler;
+ @NonNull
+ private final TcpKeepaliveController mTcpController;
public KeepaliveTracker(Handler handler) {
mConnectivityServiceHandler = handler;
+ mTcpController = new TcpKeepaliveController(handler);
}
/**
- * Tracks information about a packet keepalive.
+ * Tracks information about a socket keepalive.
*
* All information about this keepalive is known at construction time except the slot number,
* which is only returned when the hardware has successfully started the keepalive.
*/
class KeepaliveInfo implements IBinder.DeathRecipient {
- // Bookkeping data.
+ // Bookkeeping data.
private final Messenger mMessenger;
private final IBinder mBinder;
private final int mUid;
private final int mPid;
private final NetworkAgentInfo mNai;
+ private final int mType;
+ private final FileDescriptor mFd;
- /** Keepalive slot. A small integer that identifies this keepalive among the ones handled
- * by this network. */
- private int mSlot = PacketKeepalive.NO_KEEPALIVE;
+ public static final int TYPE_NATT = 1;
+ public static final int TYPE_TCP = 2;
+
+ // Keepalive slot. A small integer that identifies this keepalive among the ones handled
+ // by this network.
+ private int mSlot = NO_KEEPALIVE;
// Packet data.
private final KeepalivePacketData mPacket;
private final int mInterval;
- // Whether the keepalive is started or not.
- public boolean isStarted;
-
- public KeepaliveInfo(Messenger messenger, IBinder binder, NetworkAgentInfo nai,
- KeepalivePacketData packet, int interval) {
+ // Whether the keepalive is started or not. The initial state is NOT_STARTED.
+ private static final int NOT_STARTED = 1;
+ private static final int STARTING = 2;
+ private static final int STARTED = 3;
+ private int mStartedState = NOT_STARTED;
+
+ KeepaliveInfo(@NonNull Messenger messenger,
+ @NonNull IBinder binder,
+ @NonNull NetworkAgentInfo nai,
+ @NonNull KeepalivePacketData packet,
+ int interval,
+ int type,
+ @NonNull FileDescriptor fd) {
mMessenger = messenger;
mBinder = binder;
mPid = Binder.getCallingPid();
@@ -117,6 +139,8 @@ public class KeepaliveTracker {
mNai = nai;
mPacket = packet;
mInterval = interval;
+ mType = type;
+ mFd = fd;
try {
mBinder.linkToDeath(this, 0);
@@ -129,32 +153,40 @@ public class KeepaliveTracker {
return mNai;
}
+ private String startedStateString(final int state) {
+ switch (state) {
+ case NOT_STARTED : return "NOT_STARTED";
+ case STARTING : return "STARTING";
+ case STARTED : return "STARTED";
+ }
+ throw new IllegalArgumentException("Unknown state");
+ }
+
public String toString() {
- return new StringBuffer("KeepaliveInfo [")
- .append(" network=").append(mNai.network)
- .append(" isStarted=").append(isStarted)
- .append(" ")
- .append(IpUtils.addressAndPortToString(mPacket.srcAddress, mPacket.srcPort))
- .append("->")
- .append(IpUtils.addressAndPortToString(mPacket.dstAddress, mPacket.dstPort))
- .append(" interval=" + mInterval)
- .append(" packetData=" + HexDump.toHexString(mPacket.getPacket()))
- .append(" uid=").append(mUid).append(" pid=").append(mPid)
- .append(" ]")
- .toString();
- }
-
- /** Sends a message back to the application via its PacketKeepalive.Callback. */
+ return "KeepaliveInfo ["
+ + " network=" + mNai.network
+ + " startedState=" + startedStateString(mStartedState)
+ + " "
+ + IpUtils.addressAndPortToString(mPacket.srcAddress, mPacket.srcPort)
+ + "->"
+ + IpUtils.addressAndPortToString(mPacket.dstAddress, mPacket.dstPort)
+ + " interval=" + mInterval
+ + " uid=" + mUid + " pid=" + mPid
+ + " packetData=" + HexDump.toHexString(mPacket.getPacket())
+ + " ]";
+ }
+
+ /** Sends a message back to the application via its SocketKeepalive.Callback. */
void notifyMessenger(int slot, int err) {
+ if (DBG) {
+ Log.d(TAG, "notify keepalive " + mSlot + " on " + mNai.network + " for " + err);
+ }
KeepaliveTracker.this.notifyMessenger(mMessenger, slot, err);
}
/** Called when the application process is killed. */
public void binderDied() {
- // Not called from ConnectivityService handler thread, so send it a message.
- mConnectivityServiceHandler.obtainMessage(
- NetworkAgent.CMD_STOP_PACKET_KEEPALIVE,
- mSlot, PacketKeepalive.BINDER_DIED, mNai.network).sendToTarget();
+ stop(BINDER_DIED);
}
void unlinkDeathRecipient() {
@@ -181,7 +213,10 @@ public class KeepaliveTracker {
}
private int checkInterval() {
- return mInterval >= MIN_INTERVAL ? SUCCESS : ERROR_INVALID_INTERVAL;
+ if (mInterval < MIN_INTERVAL_SEC || mInterval > MAX_INTERVAL_SEC) {
+ return ERROR_INVALID_INTERVAL;
+ }
+ return SUCCESS;
}
private int isValid() {
@@ -198,7 +233,26 @@ public class KeepaliveTracker {
int error = isValid();
if (error == SUCCESS) {
Log.d(TAG, "Starting keepalive " + mSlot + " on " + mNai.name());
- mNai.asyncChannel.sendMessage(CMD_START_PACKET_KEEPALIVE, slot, mInterval, mPacket);
+ switch (mType) {
+ case TYPE_NATT:
+ mNai.asyncChannel
+ .sendMessage(CMD_START_SOCKET_KEEPALIVE, slot, mInterval, mPacket);
+ break;
+ case TYPE_TCP:
+ mTcpController.startSocketMonitor(mFd, this, mSlot);
+ mNai.asyncChannel
+ .sendMessage(CMD_ADD_KEEPALIVE_PACKET_FILTER, slot, 0 /* Unused */,
+ mPacket);
+ // TODO: check result from apf and notify of failure as needed.
+ mNai.asyncChannel
+ .sendMessage(CMD_START_SOCKET_KEEPALIVE, slot, mInterval, mPacket);
+ break;
+ default:
+ Log.wtf(TAG, "Starting keepalive with unknown type: " + mType);
+ handleStopKeepalive(mNai, mSlot, error);
+ return;
+ }
+ mStartedState = STARTING;
} else {
handleStopKeepalive(mNai, mSlot, error);
return;
@@ -212,20 +266,32 @@ public class KeepaliveTracker {
Log.e(TAG, "Cannot stop unowned keepalive " + mSlot + " on " + mNai.network);
}
}
- if (isStarted) {
+ if (NOT_STARTED != mStartedState) {
Log.d(TAG, "Stopping keepalive " + mSlot + " on " + mNai.name());
- mNai.asyncChannel.sendMessage(CMD_STOP_PACKET_KEEPALIVE, mSlot);
+ if (mType == TYPE_NATT) {
+ mNai.asyncChannel.sendMessage(CMD_STOP_SOCKET_KEEPALIVE, mSlot);
+ } else if (mType == TYPE_TCP) {
+ mNai.asyncChannel.sendMessage(CMD_STOP_SOCKET_KEEPALIVE, mSlot);
+ mNai.asyncChannel.sendMessage(CMD_REMOVE_KEEPALIVE_PACKET_FILTER, mSlot);
+ mTcpController.stopSocketMonitor(mSlot);
+ } else {
+ Log.wtf(TAG, "Stopping keepalive with unknown type: " + mType);
+ }
}
// TODO: at the moment we unconditionally return failure here. In cases where the
// NetworkAgent is alive, should we ask it to reply, so it can return failure?
notifyMessenger(mSlot, reason);
unlinkDeathRecipient();
}
+
+ void onFileDescriptorInitiatedStop(final int socketKeepaliveReason) {
+ handleStopKeepalive(mNai, mSlot, socketKeepaliveReason);
+ }
}
void notifyMessenger(Messenger messenger, int slot, int err) {
Message message = Message.obtain();
- message.what = EVENT_PACKET_KEEPALIVE;
+ message.what = EVENT_SOCKET_KEEPALIVE;
message.arg1 = slot;
message.arg2 = err;
message.obj = null;
@@ -310,7 +376,7 @@ public class KeepaliveTracker {
}
/** Handle keepalive events from lower layer. */
- public void handleEventPacketKeepalive(@NonNull NetworkAgentInfo nai,
+ public void handleEventSocketKeepalive(@NonNull NetworkAgentInfo nai,
@NonNull Message message) {
int slot = message.arg1;
int reason = message.arg2;
@@ -324,20 +390,38 @@ public class KeepaliveTracker {
return;
}
- if (reason == SUCCESS && !ki.isStarted) {
+ // This can be called in a number of situations :
+ // - startedState is STARTING.
+ // - reason is SUCCESS => go to STARTED.
+ // - reason isn't SUCCESS => it's an error starting. Go to NOT_STARTED and stop keepalive.
+ // - startedState is STARTED.
+ // - reason is SUCCESS => it's a success stopping. Go to NOT_STARTED and stop keepalive.
+ // - reason isn't SUCCESS => it's an error in exec. Go to NOT_STARTED and stop keepalive.
+ // The control is not supposed to ever come here if the state is NOT_STARTED. This is
+ // because in NOT_STARTED state, the code will switch to STARTING before sending messages
+ // to start, and the only way to NOT_STARTED is this function, through the edges outlined
+ // above : in all cases, keepalive gets stopped and can't restart without going into
+ // STARTING as messages are ordered. This also depends on the hardware processing the
+ // messages in order.
+ // TODO : clarify this code and get rid of mStartedState. Using a StateMachine is an
+ // option.
+ if (reason == SUCCESS && KeepaliveInfo.STARTING == ki.mStartedState) {
// Keepalive successfully started.
if (DBG) Log.d(TAG, "Started keepalive " + slot + " on " + nai.name());
- ki.isStarted = true;
+ ki.mStartedState = KeepaliveInfo.STARTED;
ki.notifyMessenger(slot, reason);
} else {
// Keepalive successfully stopped, or error.
- ki.isStarted = false;
+ ki.mStartedState = KeepaliveInfo.NOT_STARTED;
if (reason == SUCCESS) {
+ // The message indicated success stopping : don't call handleStopKeepalive.
if (DBG) Log.d(TAG, "Successfully stopped keepalive " + slot + " on " + nai.name());
} else {
+ // The message indicated some error trying to start or during the course of
+ // keepalive : do call handleStopKeepalive.
+ handleStopKeepalive(nai, slot, reason);
if (DBG) Log.d(TAG, "Keepalive " + slot + " on " + nai.name() + " error " + reason);
}
- handleStopKeepalive(nai, slot, reason);
}
}
@@ -369,16 +453,55 @@ public class KeepaliveTracker {
KeepalivePacketData packet;
try {
- packet = KeepalivePacketData.nattKeepalivePacket(
+ packet = NattKeepalivePacketData.nattKeepalivePacket(
srcAddress, srcPort, dstAddress, NATT_PORT);
- } catch (KeepalivePacketData.InvalidPacketException e) {
+ } catch (InvalidPacketException e) {
notifyMessenger(messenger, NO_KEEPALIVE, e.error);
return;
}
- KeepaliveInfo ki = new KeepaliveInfo(messenger, binder, nai, packet, intervalSeconds);
- Log.d(TAG, "Created keepalive: " + ki.toString());
+ KeepaliveInfo ki = new KeepaliveInfo(messenger, binder, nai, packet, intervalSeconds,
+ KeepaliveInfo.TYPE_NATT, null);
mConnectivityServiceHandler.obtainMessage(
- NetworkAgent.CMD_START_PACKET_KEEPALIVE, ki).sendToTarget();
+ NetworkAgent.CMD_START_SOCKET_KEEPALIVE, ki).sendToTarget();
+ }
+
+ /**
+ * Called by ConnectivityService to start TCP keepalive on a file descriptor.
+ *
+ * In order to offload keepalive for application correctly, sequence number, ack number and
+ * other fields are needed to form the keepalive packet. Thus, this function synchronously
+ * puts the socket into repair mode to get the necessary information. After the socket has been
+ * put into repair mode, the application cannot access the socket until reverted to normal.
+ *
+ * See {@link android.net.SocketKeepalive}.
+ **/
+ public void startTcpKeepalive(@Nullable NetworkAgentInfo nai,
+ @NonNull FileDescriptor fd,
+ int intervalSeconds,
+ @NonNull Messenger messenger,
+ @NonNull IBinder binder) {
+ if (nai == null) {
+ notifyMessenger(messenger, NO_KEEPALIVE, ERROR_INVALID_NETWORK);
+ return;
+ }
+
+ TcpKeepalivePacketData packet = null;
+ try {
+ TcpSocketInfo tsi = TcpKeepaliveController.switchToRepairMode(fd);
+ packet = TcpKeepalivePacketData.tcpKeepalivePacket(tsi);
+ } catch (InvalidPacketException | InvalidSocketException e) {
+ try {
+ TcpKeepaliveController.switchOutOfRepairMode(fd);
+ } catch (ErrnoException e1) {
+ Log.e(TAG, "Couldn't move fd out of repair mode after failure to start keepalive");
+ }
+ notifyMessenger(messenger, NO_KEEPALIVE, e.error);
+ return;
+ }
+ KeepaliveInfo ki = new KeepaliveInfo(messenger, binder, nai, packet, intervalSeconds,
+ KeepaliveInfo.TYPE_TCP, fd);
+ Log.d(TAG, "Created keepalive: " + ki.toString());
+ mConnectivityServiceHandler.obtainMessage(CMD_START_SOCKET_KEEPALIVE, ki).sendToTarget();
}
/**
@@ -432,7 +555,7 @@ public class KeepaliveTracker {
}
public void dump(IndentingPrintWriter pw) {
- pw.println("Packet keepalives:");
+ pw.println("Socket keepalives:");
pw.increaseIndent();
for (NetworkAgentInfo nai : mKeepalives.keySet()) {
pw.println(nai.name());
diff --git a/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java b/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java
new file mode 100644
index 000000000000..8a9ac23cf06a
--- /dev/null
+++ b/services/core/java/com/android/server/connectivity/TcpKeepaliveController.java
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) 2019 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 android.net.SocketKeepalive.DATA_RECEIVED;
+import static android.net.SocketKeepalive.ERROR_INVALID_SOCKET;
+import static android.net.SocketKeepalive.ERROR_SOCKET_NOT_IDLE;
+import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR;
+import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT;
+import static android.system.OsConstants.FIONREAD;
+import static android.system.OsConstants.IPPROTO_TCP;
+import static android.system.OsConstants.TIOCOUTQ;
+
+import android.annotation.NonNull;
+import android.net.NetworkUtils;
+import android.net.SocketKeepalive.InvalidSocketException;
+import android.net.TcpKeepalivePacketData.TcpSocketInfo;
+import android.net.TcpRepairWindow;
+import android.os.Handler;
+import android.os.MessageQueue;
+import android.os.Messenger;
+import android.system.ErrnoException;
+import android.system.Int32Ref;
+import android.system.Os;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.connectivity.KeepaliveTracker.KeepaliveInfo;
+
+import java.io.FileDescriptor;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+import java.net.SocketException;
+
+/**
+ * Manage tcp socket which offloads tcp keepalive.
+ *
+ * The input socket will be changed to repair mode and the application
+ * will not have permission to read/write data. If the application wants
+ * to write data, it must stop tcp keepalive offload to leave repair mode
+ * first. If a remote packet arrives, repair mode will be turned off and
+ * offload will be stopped. The application will receive a callback to know
+ * it can start reading data.
+ *
+ * {start,stop}SocketMonitor are thread-safe, but care must be taken in the
+ * order in which they are called. Please note that while calling
+ * {@link #startSocketMonitor(FileDescriptor, Messenger, int)} multiple times
+ * with either the same slot or the same FileDescriptor without stopping it in
+ * between will result in an exception, calling {@link #stopSocketMonitor(int)}
+ * multiple times with the same int is explicitly a no-op.
+ * Please also note that switching the socket to repair mode is not synchronized
+ * with either of these operations and has to be done in an orderly fashion
+ * with stopSocketMonitor. Take care in calling these in the right order.
+ * @hide
+ */
+public class TcpKeepaliveController {
+ private static final String TAG = "TcpKeepaliveController";
+ private static final boolean DBG = false;
+
+ private final MessageQueue mFdHandlerQueue;
+
+ private static final int FD_EVENTS = EVENT_INPUT | EVENT_ERROR;
+
+ // Reference include/uapi/linux/tcp.h
+ private static final int TCP_REPAIR = 19;
+ private static final int TCP_REPAIR_QUEUE = 20;
+ private static final int TCP_QUEUE_SEQ = 21;
+ private static final int TCP_NO_QUEUE = 0;
+ private static final int TCP_RECV_QUEUE = 1;
+ private static final int TCP_SEND_QUEUE = 2;
+ private static final int TCP_REPAIR_OFF = 0;
+ private static final int TCP_REPAIR_ON = 1;
+ // Reference include/uapi/linux/sockios.h
+ private static final int SIOCINQ = FIONREAD;
+ private static final int SIOCOUTQ = TIOCOUTQ;
+
+ /**
+ * Keeps track of packet listeners.
+ * Key: slot number of keepalive offload.
+ * Value: {@link FileDescriptor} being listened to.
+ */
+ @GuardedBy("mListeners")
+ private final SparseArray<FileDescriptor> mListeners = new SparseArray<>();
+
+ public TcpKeepaliveController(final Handler connectivityServiceHandler) {
+ mFdHandlerQueue = connectivityServiceHandler.getLooper().getQueue();
+ }
+
+ /**
+ * Switch the tcp socket to repair mode and query tcp socket information.
+ *
+ * @param fd the fd of socket on which to use keepalive offload
+ * @return a {@link TcpKeepalivePacketData#TcpSocketInfo} object for current
+ * tcp/ip information.
+ */
+ // TODO : make this private. It's far too confusing that this gets called from outside
+ // at a time that nobody can understand.
+ public static TcpSocketInfo switchToRepairMode(FileDescriptor fd)
+ throws InvalidSocketException {
+ if (DBG) Log.i(TAG, "switchToRepairMode to start tcp keepalive : " + fd);
+ final SocketAddress srcSockAddr;
+ final SocketAddress dstSockAddr;
+ final InetAddress srcAddress;
+ final InetAddress dstAddress;
+ final int srcPort;
+ final int dstPort;
+ int seq;
+ final int ack;
+ final TcpRepairWindow trw;
+
+ // Query source address and port.
+ try {
+ srcSockAddr = Os.getsockname(fd);
+ } catch (ErrnoException e) {
+ Log.e(TAG, "Get sockname fail: ", e);
+ throw new InvalidSocketException(ERROR_INVALID_SOCKET, e);
+ }
+ if (srcSockAddr instanceof InetSocketAddress) {
+ srcAddress = getAddress((InetSocketAddress) srcSockAddr);
+ srcPort = getPort((InetSocketAddress) srcSockAddr);
+ } else {
+ Log.e(TAG, "Invalid or mismatched SocketAddress");
+ throw new InvalidSocketException(ERROR_INVALID_SOCKET);
+ }
+ // Query destination address and port.
+ try {
+ dstSockAddr = Os.getpeername(fd);
+ } catch (ErrnoException e) {
+ Log.e(TAG, "Get peername fail: ", e);
+ throw new InvalidSocketException(ERROR_INVALID_SOCKET, e);
+ }
+ if (dstSockAddr instanceof InetSocketAddress) {
+ dstAddress = getAddress((InetSocketAddress) dstSockAddr);
+ dstPort = getPort((InetSocketAddress) dstSockAddr);
+ } else {
+ Log.e(TAG, "Invalid or mismatched peer SocketAddress");
+ throw new InvalidSocketException(ERROR_INVALID_SOCKET);
+ }
+
+ // Query sequence and ack number
+ dropAllIncomingPackets(fd, true);
+ try {
+ // Enter tcp repair mode.
+ Os.setsockoptInt(fd, IPPROTO_TCP, TCP_REPAIR, TCP_REPAIR_ON);
+ // Check if socket is idle.
+ if (!isSocketIdle(fd)) {
+ throw new InvalidSocketException(ERROR_SOCKET_NOT_IDLE);
+ }
+ // Query write sequence number from SEND_QUEUE.
+ Os.setsockoptInt(fd, IPPROTO_TCP, TCP_REPAIR_QUEUE, TCP_SEND_QUEUE);
+ seq = Os.getsockoptInt(fd, IPPROTO_TCP, TCP_QUEUE_SEQ);
+ // Query read sequence number from RECV_QUEUE.
+ Os.setsockoptInt(fd, IPPROTO_TCP, TCP_REPAIR_QUEUE, TCP_RECV_QUEUE);
+ ack = Os.getsockoptInt(fd, IPPROTO_TCP, TCP_QUEUE_SEQ);
+ // Switch to NO_QUEUE to prevent illegal socket read/write in repair mode.
+ Os.setsockoptInt(fd, IPPROTO_TCP, TCP_REPAIR_QUEUE, TCP_NO_QUEUE);
+ // Finally, check if socket is still idle. TODO : this check needs to move to
+ // after starting polling to prevent a race.
+ if (!isSocketIdle(fd)) {
+ throw new InvalidSocketException(ERROR_INVALID_SOCKET);
+ }
+
+ // Query tcp window size.
+ trw = NetworkUtils.getTcpRepairWindow(fd);
+ } catch (ErrnoException e) {
+ Log.e(TAG, "Exception reading TCP state from socket", e);
+ try {
+ Os.setsockoptInt(fd, IPPROTO_TCP, TCP_REPAIR, TCP_REPAIR_OFF);
+ } catch (ErrnoException ex) {
+ Log.e(TAG, "Exception while turning off repair mode due to exception", ex);
+ }
+ throw new InvalidSocketException(ERROR_INVALID_SOCKET, e);
+ } finally {
+ dropAllIncomingPackets(fd, false);
+ }
+
+ // Keepalive sequence number is last sequence number - 1. If it couldn't be retrieved,
+ // then it must be set to -1, so decrement in all cases.
+ seq = seq - 1;
+
+ return new TcpSocketInfo(srcAddress, srcPort, dstAddress, dstPort, seq, ack, trw.rcvWnd,
+ trw.rcvWndScale);
+ }
+
+ /**
+ * Switch the tcp socket out of repair mode.
+ *
+ * @param fd the fd of socket to switch back to normal.
+ */
+ // TODO : make this private.
+ public static void switchOutOfRepairMode(@NonNull final FileDescriptor fd)
+ throws ErrnoException {
+ Os.setsockoptInt(fd, IPPROTO_TCP, TCP_REPAIR, TCP_REPAIR_OFF);
+ }
+
+ /**
+ * Start monitoring incoming packets.
+ *
+ * @param fd socket fd to monitor.
+ * @param messenger a callback to notify socket status.
+ * @param slot keepalive slot.
+ */
+ public void startSocketMonitor(@NonNull final FileDescriptor fd,
+ @NonNull final KeepaliveInfo ki, final int slot) {
+ synchronized (mListeners) {
+ if (null != mListeners.get(slot)) {
+ throw new IllegalArgumentException("This slot is already taken");
+ }
+ for (int i = 0; i < mListeners.size(); ++i) {
+ if (fd.equals(mListeners.valueAt(i))) {
+ throw new IllegalArgumentException("This fd is already registered");
+ }
+ }
+ mFdHandlerQueue.addOnFileDescriptorEventListener(fd, FD_EVENTS, (readyFd, events) -> {
+ // This can't be called twice because the queue guarantees that once the listener
+ // is unregistered it can't be called again, even for a message that arrived
+ // before it was unregistered.
+ final int reason;
+ if (0 != (events & EVENT_ERROR)) {
+ reason = ERROR_INVALID_SOCKET;
+ } else {
+ reason = DATA_RECEIVED;
+ }
+ ki.onFileDescriptorInitiatedStop(reason);
+ // The listener returns the new set of events to listen to. Because 0 means no
+ // event, the listener gets unregistered.
+ return 0;
+ });
+ mListeners.put(slot, fd);
+ }
+ }
+
+ /** Stop socket monitor */
+ // This slot may have been stopped automatically already because the socket received data,
+ // was closed on the other end or otherwise suffered some error. In this case, this function
+ // is a no-op.
+ public void stopSocketMonitor(final int slot) {
+ final FileDescriptor fd;
+ synchronized (mListeners) {
+ fd = mListeners.get(slot);
+ if (null == fd) return;
+ mListeners.remove(slot);
+ }
+ mFdHandlerQueue.removeOnFileDescriptorEventListener(fd);
+ try {
+ if (DBG) Log.d(TAG, "Moving socket out of repair mode for stop : " + fd);
+ switchOutOfRepairMode(fd);
+ } catch (ErrnoException e) {
+ Log.e(TAG, "Cannot switch socket out of repair mode", e);
+ // Well, there is not much to do here to recover
+ }
+ }
+
+ private static InetAddress getAddress(InetSocketAddress inetAddr) {
+ return inetAddr.getAddress();
+ }
+
+ private static int getPort(InetSocketAddress inetAddr) {
+ return inetAddr.getPort();
+ }
+
+ private static boolean isSocketIdle(FileDescriptor fd) throws ErrnoException {
+ return isReceiveQueueEmpty(fd) && isSendQueueEmpty(fd);
+ }
+
+ private static boolean isReceiveQueueEmpty(FileDescriptor fd)
+ throws ErrnoException {
+ Int32Ref result = new Int32Ref(-1);
+ Os.ioctlInt(fd, SIOCINQ, result);
+ if (result.value != 0) {
+ Log.e(TAG, "Read queue has data");
+ return false;
+ }
+ return true;
+ }
+
+ private static boolean isSendQueueEmpty(FileDescriptor fd)
+ throws ErrnoException {
+ Int32Ref result = new Int32Ref(-1);
+ Os.ioctlInt(fd, SIOCOUTQ, result);
+ if (result.value != 0) {
+ Log.e(TAG, "Write queue has data");
+ return false;
+ }
+ return true;
+ }
+
+ private static void dropAllIncomingPackets(FileDescriptor fd, boolean enable)
+ throws InvalidSocketException {
+ try {
+ if (enable) {
+ NetworkUtils.attachDropAllBPFFilter(fd);
+ } else {
+ NetworkUtils.detachBPFFilter(fd);
+ }
+ } catch (SocketException e) {
+ Log.e(TAG, "Socket Exception: ", e);
+ throw new InvalidSocketException(ERROR_INVALID_SOCKET, e);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index eb5be77e4a33..c91e1a12078e 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -96,6 +96,7 @@ import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
+import com.android.internal.telephony.TelephonyIntents;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.MessageUtils;
@@ -121,7 +122,6 @@ import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
-
/**
* @hide
*
@@ -181,6 +181,7 @@ public class Tethering extends BaseNetworkObserver {
// into a single coherent structure.
private final HashSet<IpServer> mForwardedDownstreams;
private final VersionedBroadcastListener mCarrierConfigChange;
+ private final VersionedBroadcastListener mDefaultSubscriptionChange;
private final TetheringDependencies mDeps;
private final EntitlementManager mEntitlementMgr;
@@ -223,7 +224,8 @@ public class Tethering extends BaseNetworkObserver {
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_CARRIER_CONFIG_CHANGED);
- mEntitlementMgr = mDeps.getEntitlementManager(mContext, mLog, systemProperties);
+ mEntitlementMgr = mDeps.getEntitlementManager(mContext, mTetherMasterSM,
+ mLog, systemProperties);
mCarrierConfigChange = new VersionedBroadcastListener(
"CarrierConfigChangeListener", mContext, smHandler, filter,
(Intent ignored) -> {
@@ -232,6 +234,15 @@ public class Tethering extends BaseNetworkObserver {
mEntitlementMgr.reevaluateSimCardProvisioning();
});
+ filter = new IntentFilter();
+ filter.addAction(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
+ mDefaultSubscriptionChange = new VersionedBroadcastListener(
+ "DefaultSubscriptionChangeListener", mContext, smHandler, filter,
+ (Intent ignored) -> {
+ mLog.log("OBSERVED default data subscription change");
+ updateConfiguration();
+ mEntitlementMgr.reevaluateSimCardProvisioning();
+ });
mStateReceiver = new StateReceiver();
// Load tethering configuration.
@@ -242,6 +253,7 @@ public class Tethering extends BaseNetworkObserver {
private void startStateMachineUpdaters() {
mCarrierConfigChange.startListening();
+ mDefaultSubscriptionChange.startListening();
final Handler handler = mTetherMasterSM.getHandler();
IntentFilter filter = new IntentFilter();
@@ -270,7 +282,8 @@ public class Tethering extends BaseNetworkObserver {
// NOTE: This is always invoked on the mLooper thread.
private void updateConfiguration() {
- mConfig = new TetheringConfiguration(mContext, mLog);
+ final int subId = mDeps.getDefaultDataSubscriptionId();
+ mConfig = new TetheringConfiguration(mContext, mLog, subId);
mUpstreamNetworkMonitor.updateMobileRequiresDun(mConfig.isDunRequired);
mEntitlementMgr.updateConfiguration(mConfig);
}
@@ -470,6 +483,7 @@ public class Tethering extends BaseNetworkObserver {
} else {
sendTetherResult(receiver, resultCode);
}
+ mEntitlementMgr.updateEntitlementCacheValue(type, resultCode);
}
};
@@ -1662,6 +1676,14 @@ public class Tethering extends BaseNetworkObserver {
mUpstreamNetworkMonitor.startTrackDefaultNetwork(mDeps.getDefaultNetworkRequest());
}
+ /** Get the latest value of the tethering entitlement check. */
+ public void getLatestTetheringEntitlementValue(int type, ResultReceiver receiver,
+ boolean showEntitlementUi) {
+ if (receiver != null) {
+ mEntitlementMgr.getLatestTetheringEntitlementValue(type, receiver, showEntitlementUi);
+ }
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
// Binder.java closes the resource for us.
@@ -1837,7 +1859,7 @@ public class Tethering extends BaseNetworkObserver {
final TetherState tetherState = new TetherState(
new IpServer(iface, mLooper, interfaceType, mLog, mNMService, mStatsService,
makeControlCallback(), mConfig.enableLegacyDhcpServer,
- mDeps.getIpServerDependencies(mContext)));
+ mDeps.getIpServerDependencies()));
mTetherStates.put(iface, tetherState);
tetherState.ipServer.start();
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 250884431440..a7d16d8cd6ec 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -165,6 +165,7 @@ public class Vpn {
private final NetworkInfo mNetworkInfo;
private String mPackage;
private int mOwnerUID;
+ private boolean mIsPackageTargetingAtLeastQ;
private String mInterface;
private Connection mConnection;
private LegacyVpnRunner mLegacyVpnRunner;
@@ -226,6 +227,7 @@ public class Vpn {
mPackage = VpnConfig.LEGACY_VPN;
mOwnerUID = getAppUid(mPackage, mUserHandle);
+ mIsPackageTargetingAtLeastQ = doesPackageTargetAtLeastQ(mPackage);
try {
netService.registerObserver(mObserver);
@@ -238,7 +240,7 @@ public class Vpn {
mNetworkCapabilities = new NetworkCapabilities();
mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_VPN);
mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
- updateCapabilities();
+ updateCapabilities(null /* defaultNetwork */);
loadAlwaysOnPackage();
}
@@ -265,29 +267,57 @@ public class Vpn {
updateAlwaysOnNotification(detailedState);
}
- public void updateCapabilities() {
- final Network[] underlyingNetworks = (mConfig != null) ? mConfig.underlyingNetworks : null;
- updateCapabilities(mContext.getSystemService(ConnectivityManager.class), underlyingNetworks,
- mNetworkCapabilities);
+ /**
+ * Updates {@link #mNetworkCapabilities} based on current underlying networks and returns a
+ * defensive copy.
+ *
+ * <p>Does not propagate updated capabilities to apps.
+ *
+ * @param defaultNetwork underlying network for VPNs following platform's default
+ */
+ public synchronized NetworkCapabilities updateCapabilities(
+ @Nullable Network defaultNetwork) {
+ if (mConfig == null) {
+ // VPN is not running.
+ return null;
+ }
- if (mNetworkAgent != null) {
- mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+ Network[] underlyingNetworks = mConfig.underlyingNetworks;
+ if (underlyingNetworks == null && defaultNetwork != null) {
+ // null underlying networks means to track the default.
+ underlyingNetworks = new Network[] { defaultNetwork };
}
+ // Only apps targeting Q and above can explicitly declare themselves as metered.
+ final boolean isAlwaysMetered = mIsPackageTargetingAtLeastQ && mConfig.isMetered;
+
+ applyUnderlyingCapabilities(
+ mContext.getSystemService(ConnectivityManager.class),
+ underlyingNetworks,
+ mNetworkCapabilities,
+ isAlwaysMetered);
+
+ return new NetworkCapabilities(mNetworkCapabilities);
}
@VisibleForTesting
- public static void updateCapabilities(ConnectivityManager cm, Network[] underlyingNetworks,
- NetworkCapabilities caps) {
+ public static void applyUnderlyingCapabilities(
+ ConnectivityManager cm,
+ Network[] underlyingNetworks,
+ NetworkCapabilities caps,
+ boolean isAlwaysMetered) {
int[] transportTypes = new int[] { NetworkCapabilities.TRANSPORT_VPN };
int downKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
int upKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
- boolean metered = false;
+ // VPN's meteredness is OR'd with isAlwaysMetered and meteredness of its underlying
+ // networks.
+ boolean metered = isAlwaysMetered;
boolean roaming = false;
boolean congested = false;
boolean hadUnderlyingNetworks = false;
if (null != underlyingNetworks) {
for (Network underlying : underlyingNetworks) {
+ // TODO(b/124469351): Get capabilities directly from ConnectivityService instead.
final NetworkCapabilities underlyingCaps = cm.getNetworkCapabilities(underlying);
if (underlyingCaps == null) continue;
hadUnderlyingNetworks = true;
@@ -724,6 +754,7 @@ public class Vpn {
Log.i(TAG, "Switched from " + mPackage + " to " + newPackage);
mPackage = newPackage;
mOwnerUID = getAppUid(newPackage, mUserHandle);
+ mIsPackageTargetingAtLeastQ = doesPackageTargetAtLeastQ(newPackage);
try {
mNetd.allowProtect(mOwnerUID);
} catch (Exception e) {
@@ -789,6 +820,21 @@ public class Vpn {
return result;
}
+ private boolean doesPackageTargetAtLeastQ(String packageName) {
+ if (VpnConfig.LEGACY_VPN.equals(packageName)) {
+ return true;
+ }
+ PackageManager pm = mContext.getPackageManager();
+ try {
+ ApplicationInfo appInfo =
+ pm.getApplicationInfoAsUser(packageName, 0 /*flags*/, mUserHandle);
+ return appInfo.targetSdkVersion >= VERSION_CODES.Q;
+ } catch (NameNotFoundException unused) {
+ Log.w(TAG, "Can't find \"" + packageName + "\"");
+ return false;
+ }
+ }
+
public NetworkInfo getNetworkInfo() {
return mNetworkInfo;
}
@@ -982,9 +1028,8 @@ public class Vpn {
}
/**
- * Establish a VPN network and return the file descriptor of the VPN
- * interface. This methods returns {@code null} if the application is
- * revoked or not prepared.
+ * Establish a VPN network and return the file descriptor of the VPN interface. This methods
+ * returns {@code null} if the application is revoked or not prepared.
*
* @param config The parameters to configure the network.
* @return The file descriptor of the VPN interface.
@@ -1231,6 +1276,11 @@ public class Vpn {
return ranges;
}
+ /**
+ * Updates UID ranges for this VPN and also updates its internal capabilities.
+ *
+ * <p>Should be called on primary ConnectivityService thread.
+ */
public void onUserAdded(int userHandle) {
// If the user is restricted tie them to the parent user's VPN
UserInfo user = UserManager.get(mContext).getUserInfo(userHandle);
@@ -1241,8 +1291,9 @@ public class Vpn {
try {
addUserToRanges(existingRanges, userHandle, mConfig.allowedApplications,
mConfig.disallowedApplications);
+ // ConnectivityService will call {@link #updateCapabilities} and apply
+ // those for VPN network.
mNetworkCapabilities.setUids(existingRanges);
- updateCapabilities();
} catch (Exception e) {
Log.wtf(TAG, "Failed to add restricted user to owner", e);
}
@@ -1252,6 +1303,11 @@ public class Vpn {
}
}
+ /**
+ * Updates UID ranges for this VPN and also updates its capabilities.
+ *
+ * <p>Should be called on primary ConnectivityService thread.
+ */
public void onUserRemoved(int userHandle) {
// clean up if restricted
UserInfo user = UserManager.get(mContext).getUserInfo(userHandle);
@@ -1263,8 +1319,9 @@ public class Vpn {
final List<UidRange> removedRanges =
uidRangesForUser(userHandle, existingRanges);
existingRanges.removeAll(removedRanges);
+ // ConnectivityService will call {@link #updateCapabilities} and
+ // apply those for VPN network.
mNetworkCapabilities.setUids(existingRanges);
- updateCapabilities();
} catch (Exception e) {
Log.wtf(TAG, "Failed to remove restricted user to owner", e);
}
@@ -1477,6 +1534,12 @@ public class Vpn {
return success;
}
+ /**
+ * Updates underlying network set.
+ *
+ * <p>Note: Does not updates capabilities. Call {@link #updateCapabilities} from
+ * ConnectivityService thread to get updated capabilities.
+ */
public synchronized boolean setUnderlyingNetworks(Network[] networks) {
if (!isCallerEstablishedOwnerLocked()) {
return false;
@@ -1493,7 +1556,6 @@ public class Vpn {
}
}
}
- updateCapabilities();
return true;
}
@@ -1776,6 +1838,7 @@ public class Vpn {
config.user = profile.key;
config.interfaze = iface;
config.session = profile.name;
+ config.isMetered = false;
config.addLegacyRoutes(profile.routes);
if (!profile.dnsServers.isEmpty()) {
diff --git a/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java b/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java
index a4e3e1d85bcb..75aac106e0e0 100644
--- a/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java
+++ b/services/core/java/com/android/server/connectivity/tethering/EntitlementManager.java
@@ -21,6 +21,9 @@ import static android.net.ConnectivityManager.EXTRA_PROVISION_CALLBACK;
import static android.net.ConnectivityManager.EXTRA_REM_TETHER_TYPE;
import static android.net.ConnectivityManager.EXTRA_RUN_PROVISION;
import static android.net.ConnectivityManager.EXTRA_SET_ALARM;
+import static android.net.ConnectivityManager.TETHER_ERROR_ENTITLEMENT_UNKONWN;
+import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
+import static android.net.ConnectivityManager.TETHER_ERROR_PROVISION_FAILED;
import static com.android.internal.R.string.config_wifi_tether_enable;
@@ -31,15 +34,21 @@ import android.content.Intent;
import android.content.res.Resources;
import android.net.util.SharedLog;
import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Parcel;
import android.os.PersistableBundle;
import android.os.ResultReceiver;
import android.os.UserHandle;
import android.provider.Settings;
import android.telephony.CarrierConfigManager;
import android.util.ArraySet;
+import android.util.Log;
+import android.util.SparseIntArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.StateMachine;
import com.android.server.connectivity.MockableSystemProperties;
/**
@@ -50,6 +59,7 @@ import com.android.server.connectivity.MockableSystemProperties;
*/
public class EntitlementManager {
private static final String TAG = EntitlementManager.class.getSimpleName();
+ private static final boolean DBG = false;
// {@link ComponentName} of the Service used to run tether provisioning.
private static final ComponentName TETHER_SERVICE = ComponentName.unflattenFromString(
@@ -65,15 +75,19 @@ public class EntitlementManager {
private final Context mContext;
private final MockableSystemProperties mSystemProperties;
private final SharedLog mLog;
+ private final Handler mMasterHandler;
+ private final SparseIntArray mEntitlementCacheValue;
@Nullable
private TetheringConfiguration mConfig;
- public EntitlementManager(Context ctx, SharedLog log,
+ public EntitlementManager(Context ctx, StateMachine tetherMasterSM, SharedLog log,
MockableSystemProperties systemProperties) {
mContext = ctx;
- mLog = log;
+ mLog = log.forSubComponent(TAG);
mCurrentTethers = new ArraySet<Integer>();
mSystemProperties = systemProperties;
+ mEntitlementCacheValue = new SparseIntArray();
+ mMasterHandler = tetherMasterSM.getHandler();
}
/**
@@ -128,6 +142,10 @@ public class EntitlementManager {
* Reference ConnectivityManager.TETHERING_{@code *} for each tether type.
*/
public void reevaluateSimCardProvisioning() {
+ synchronized (mEntitlementCacheValue) {
+ mEntitlementCacheValue.clear();
+ }
+
if (!mConfig.hasMobileHotspotProvisionApp()) return;
if (carrierConfigAffirmsEntitlementCheckNotRequired()) return;
@@ -175,6 +193,11 @@ public class EntitlementManager {
}
public void runUiTetherProvisioningAndEnable(int type, ResultReceiver receiver) {
+ runUiTetherProvisioning(type, receiver);
+ }
+
+ @VisibleForTesting
+ protected void runUiTetherProvisioning(int type, ResultReceiver receiver) {
Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING);
intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
@@ -221,4 +244,70 @@ public class EntitlementManager {
Binder.restoreCallingIdentity(ident);
}
}
+
+ private ResultReceiver buildProxyReceiver(int type, final ResultReceiver receiver) {
+ ResultReceiver rr = new ResultReceiver(mMasterHandler) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ int updatedCacheValue = updateEntitlementCacheValue(type, resultCode);
+ receiver.send(updatedCacheValue, null);
+ }
+ };
+
+ return writeToParcel(rr);
+ }
+
+ private ResultReceiver writeToParcel(final ResultReceiver receiver) {
+ // This is necessary to avoid unmarshalling issues when sending the receiver
+ // across processes.
+ Parcel parcel = Parcel.obtain();
+ receiver.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ ResultReceiver receiverForSending = ResultReceiver.CREATOR.createFromParcel(parcel);
+ parcel.recycle();
+ return receiverForSending;
+ }
+
+ /**
+ * Update the last entitlement value to internal cache
+ *
+ * @param type tethering type from ConnectivityManager.TETHERING_{@code *}
+ * @param resultCode last entitlement value
+ * @return the last updated entitlement value
+ */
+ public int updateEntitlementCacheValue(int type, int resultCode) {
+ if (DBG) {
+ Log.d(TAG, "updateEntitlementCacheValue: " + type + ", result: " + resultCode);
+ }
+ synchronized (mEntitlementCacheValue) {
+ if (resultCode == TETHER_ERROR_NO_ERROR) {
+ mEntitlementCacheValue.put(type, resultCode);
+ return resultCode;
+ } else {
+ mEntitlementCacheValue.put(type, TETHER_ERROR_PROVISION_FAILED);
+ return TETHER_ERROR_PROVISION_FAILED;
+ }
+ }
+ }
+
+ /** Get the last value of the tethering entitlement check. */
+ public void getLatestTetheringEntitlementValue(int downstream, ResultReceiver receiver,
+ boolean showEntitlementUi) {
+ if (!isTetherProvisioningRequired()) {
+ receiver.send(TETHER_ERROR_NO_ERROR, null);
+ return;
+ }
+
+ final int cacheValue;
+ synchronized (mEntitlementCacheValue) {
+ cacheValue = mEntitlementCacheValue.get(
+ downstream, TETHER_ERROR_ENTITLEMENT_UNKONWN);
+ }
+ if (cacheValue == TETHER_ERROR_NO_ERROR || !showEntitlementUi) {
+ receiver.send(cacheValue, null);
+ } else {
+ ResultReceiver proxy = buildProxyReceiver(downstream, receiver);
+ runUiTetherProvisioning(downstream, proxy);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
index 1e6bb04858a1..8a46ff18979f 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
@@ -26,8 +26,8 @@ import static android.provider.Settings.Global.TETHER_ENABLE_LEGACY_DHCP_SERVER;
import static com.android.internal.R.array.config_mobile_hotspot_provision_app;
import static com.android.internal.R.array.config_tether_bluetooth_regexs;
import static com.android.internal.R.array.config_tether_dhcp_range;
-import static com.android.internal.R.array.config_tether_usb_regexs;
import static com.android.internal.R.array.config_tether_upstream_types;
+import static com.android.internal.R.array.config_tether_usb_regexs;
import static com.android.internal.R.array.config_tether_wifi_regexs;
import static com.android.internal.R.bool.config_tether_upstream_automatic;
import static com.android.internal.R.string.config_mobile_hotspot_provision_app_no_ui;
@@ -38,6 +38,7 @@ import android.content.res.Resources;
import android.net.ConnectivityManager;
import android.net.util.SharedLog;
import android.provider.Settings;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
@@ -100,29 +101,34 @@ public class TetheringConfiguration {
public final String[] provisioningApp;
public final String provisioningAppNoUi;
- public TetheringConfiguration(Context ctx, SharedLog log) {
+ public final int subId;
+
+ public TetheringConfiguration(Context ctx, SharedLog log, int id) {
final SharedLog configLog = log.forSubComponent("config");
- tetherableUsbRegexs = getResourceStringArray(ctx, config_tether_usb_regexs);
+ subId = id;
+ Resources res = getResources(ctx, subId);
+
+ tetherableUsbRegexs = getResourceStringArray(res, config_tether_usb_regexs);
// TODO: Evaluate deleting this altogether now that Wi-Fi always passes
// us an interface name. Careful consideration needs to be given to
// implications for Settings and for provisioning checks.
- tetherableWifiRegexs = getResourceStringArray(ctx, config_tether_wifi_regexs);
- tetherableBluetoothRegexs = getResourceStringArray(ctx, config_tether_bluetooth_regexs);
+ tetherableWifiRegexs = getResourceStringArray(res, config_tether_wifi_regexs);
+ tetherableBluetoothRegexs = getResourceStringArray(res, config_tether_bluetooth_regexs);
dunCheck = checkDunRequired(ctx);
configLog.log("DUN check returned: " + dunCheckString(dunCheck));
- chooseUpstreamAutomatically = getResourceBoolean(ctx, config_tether_upstream_automatic);
- preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(ctx, dunCheck);
+ chooseUpstreamAutomatically = getResourceBoolean(res, config_tether_upstream_automatic);
+ preferredUpstreamIfaceTypes = getUpstreamIfaceTypes(res, dunCheck);
isDunRequired = preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN);
- legacyDhcpRanges = getLegacyDhcpRanges(ctx);
+ legacyDhcpRanges = getLegacyDhcpRanges(res);
defaultIPv4DNS = copy(DEFAULT_IPV4_DNS);
enableLegacyDhcpServer = getEnableLegacyDhcpServer(ctx);
- provisioningApp = getResourceStringArray(ctx, config_mobile_hotspot_provision_app);
- provisioningAppNoUi = getProvisioningAppNoUi(ctx);
+ provisioningApp = getResourceStringArray(res, config_mobile_hotspot_provision_app);
+ provisioningAppNoUi = getProvisioningAppNoUi(res);
configLog.log(toString());
}
@@ -144,6 +150,9 @@ public class TetheringConfiguration {
}
public void dump(PrintWriter pw) {
+ pw.print("subId: ");
+ pw.println(subId);
+
dumpStringArray(pw, "tetherableUsbRegexs", tetherableUsbRegexs);
dumpStringArray(pw, "tetherableWifiRegexs", tetherableWifiRegexs);
dumpStringArray(pw, "tetherableBluetoothRegexs", tetherableBluetoothRegexs);
@@ -169,6 +178,7 @@ public class TetheringConfiguration {
public String toString() {
final StringJoiner sj = new StringJoiner(" ");
+ sj.add(String.format("subId:%d", subId));
sj.add(String.format("tetherableUsbRegexs:%s", makeString(tetherableUsbRegexs)));
sj.add(String.format("tetherableWifiRegexs:%s", makeString(tetherableWifiRegexs)));
sj.add(String.format("tetherableBluetoothRegexs:%s",
@@ -235,8 +245,8 @@ public class TetheringConfiguration {
}
}
- private static Collection<Integer> getUpstreamIfaceTypes(Context ctx, int dunCheck) {
- final int ifaceTypes[] = ctx.getResources().getIntArray(config_tether_upstream_types);
+ private static Collection<Integer> getUpstreamIfaceTypes(Resources res, int dunCheck) {
+ final int[] ifaceTypes = res.getIntArray(config_tether_upstream_types);
final ArrayList<Integer> upstreamIfaceTypes = new ArrayList<>(ifaceTypes.length);
for (int i : ifaceTypes) {
switch (i) {
@@ -286,33 +296,33 @@ public class TetheringConfiguration {
return false;
}
- private static String[] getLegacyDhcpRanges(Context ctx) {
- final String[] fromResource = getResourceStringArray(ctx, config_tether_dhcp_range);
+ private static String[] getLegacyDhcpRanges(Resources res) {
+ final String[] fromResource = getResourceStringArray(res, config_tether_dhcp_range);
if ((fromResource.length > 0) && (fromResource.length % 2 == 0)) {
return fromResource;
}
return copy(LEGACY_DHCP_DEFAULT_RANGE);
}
- private static String getProvisioningAppNoUi(Context ctx) {
+ private static String getProvisioningAppNoUi(Resources res) {
try {
- return ctx.getResources().getString(config_mobile_hotspot_provision_app_no_ui);
+ return res.getString(config_mobile_hotspot_provision_app_no_ui);
} catch (Resources.NotFoundException e) {
return "";
}
}
- private static boolean getResourceBoolean(Context ctx, int resId) {
+ private static boolean getResourceBoolean(Resources res, int resId) {
try {
- return ctx.getResources().getBoolean(resId);
+ return res.getBoolean(resId);
} catch (Resources.NotFoundException e404) {
return false;
}
}
- private static String[] getResourceStringArray(Context ctx, int resId) {
+ private static String[] getResourceStringArray(Resources res, int resId) {
try {
- final String[] strArray = ctx.getResources().getStringArray(resId);
+ final String[] strArray = res.getStringArray(resId);
return (strArray != null) ? strArray : EMPTY_STRING_ARRAY;
} catch (Resources.NotFoundException e404) {
return EMPTY_STRING_ARRAY;
@@ -325,6 +335,19 @@ public class TetheringConfiguration {
return intVal != 0;
}
+ private Resources getResources(Context ctx, int subId) {
+ if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ return getResourcesForSubIdWrapper(ctx, subId);
+ } else {
+ return ctx.getResources();
+ }
+ }
+
+ @VisibleForTesting
+ protected Resources getResourcesForSubIdWrapper(Context ctx, int subId) {
+ return SubscriptionManager.getResourcesForSubId(ctx, subId);
+ }
+
private static String[] copy(String[] strarray) {
return Arrays.copyOf(strarray, strarray.length);
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
index a42efe960ff9..173d7860e4ac 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java
@@ -21,6 +21,7 @@ import android.net.NetworkRequest;
import android.net.ip.IpServer;
import android.net.util.SharedLog;
import android.os.Handler;
+import android.telephony.SubscriptionManager;
import com.android.internal.util.StateMachine;
import com.android.server.connectivity.MockableSystemProperties;
@@ -60,8 +61,8 @@ public class TetheringDependencies {
/**
* Get dependencies to be used by IpServer.
*/
- public IpServer.Dependencies getIpServerDependencies(Context context) {
- return new IpServer.Dependencies(context);
+ public IpServer.Dependencies getIpServerDependencies() {
+ return new IpServer.Dependencies();
}
/**
@@ -81,8 +82,15 @@ public class TetheringDependencies {
/**
* Get a reference to the EntitlementManager to be used by tethering.
*/
- public EntitlementManager getEntitlementManager(Context ctx, SharedLog log,
- MockableSystemProperties systemProperties) {
- return new EntitlementManager(ctx, log, systemProperties);
+ public EntitlementManager getEntitlementManager(Context ctx, StateMachine target,
+ SharedLog log, MockableSystemProperties systemProperties) {
+ return new EntitlementManager(ctx, target, log, systemProperties);
+ }
+
+ /**
+ * Get default data subscription id to build TetheringConfiguration.
+ */
+ public int getDefaultDataSubscriptionId() {
+ return SubscriptionManager.getDefaultDataSubscriptionId();
}
}
diff --git a/services/core/java/com/android/server/net/LockdownVpnTracker.java b/services/core/java/com/android/server/net/LockdownVpnTracker.java
index 9e5b92a6b944..3f15b381c18b 100644
--- a/services/core/java/com/android/server/net/LockdownVpnTracker.java
+++ b/services/core/java/com/android/server/net/LockdownVpnTracker.java
@@ -17,9 +17,6 @@
package com.android.server.net;
import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NONE;
-import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW;
-import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
import static android.provider.Settings.ACTION_VPN_SETTINGS;
import android.app.Notification;
@@ -30,17 +27,14 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.ConnectivityManager;
-import android.net.LinkProperties;
import android.net.LinkAddress;
+import android.net.LinkProperties;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
import android.net.NetworkInfo.State;
-import android.net.NetworkPolicyManager;
import android.os.INetworkManagementService;
-import android.os.RemoteException;
import android.security.Credentials;
import android.security.KeyStore;
-import android.system.Os;
import android.text.TextUtils;
import android.util.Slog;
diff --git a/services/core/java/com/android/server/net/NetworkPolicyLogger.java b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
index 31fdc01b8d4d..7cc357c3661c 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyLogger.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyLogger.java
@@ -15,15 +15,15 @@
*/
package com.android.server.net;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE;
+import static android.net.INetd.FIREWALL_CHAIN_DOZABLE;
+import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE;
+import static android.net.INetd.FIREWALL_CHAIN_STANDBY;
+import static android.net.INetd.FIREWALL_RULE_ALLOW;
+import static android.net.INetd.FIREWALL_RULE_DENY;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_POWERSAVE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_STANDBY;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_POWERSAVE;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY;
-import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW;
import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
-import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY;
import android.app.ActivityManager;
import android.net.NetworkPolicyManager;
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index e539ffd5a85f..863ef67d4f0f 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -38,6 +38,11 @@ import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_DISABLE
import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_ENABLED;
import static android.net.ConnectivityManager.RESTRICT_BACKGROUND_STATUS_WHITELISTED;
import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.INetd.FIREWALL_CHAIN_DOZABLE;
+import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE;
+import static android.net.INetd.FIREWALL_CHAIN_STANDBY;
+import static android.net.INetd.FIREWALL_RULE_ALLOW;
+import static android.net.INetd.FIREWALL_RULE_DENY;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
@@ -45,12 +50,7 @@ import static android.net.NetworkPolicy.LIMIT_DISABLED;
import static android.net.NetworkPolicy.SNOOZE_NEVER;
import static android.net.NetworkPolicy.WARNING_DISABLED;
import static android.net.NetworkPolicyManager.EXTRA_NETWORK_TEMPLATE;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_POWERSAVE;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY;
-import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW;
import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
-import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY;
import static android.net.NetworkPolicyManager.MASK_ALL_NETWORKS;
import static android.net.NetworkPolicyManager.MASK_METERED_NETWORKS;
import static android.net.NetworkPolicyManager.POLICY_ALLOW_METERED_BACKGROUND;
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index f1b03d1fc9d6..81d6b63d9d85 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -344,6 +344,11 @@ public final class OverlayManagerService extends SystemService {
private final class PackageReceiver extends BroadcastReceiver {
@Override
public void onReceive(@NonNull final Context context, @NonNull final Intent intent) {
+ final String action = intent.getAction();
+ if (action == null) {
+ Slog.e(TAG, "Cannot handle package broadcast with null action");
+ return;
+ }
final Uri data = intent.getData();
if (data == null) {
Slog.e(TAG, "Cannot handle package broadcast with null data");
@@ -361,7 +366,7 @@ public final class OverlayManagerService extends SystemService {
userIds = new int[] { UserHandle.getUserId(extraUid) };
}
- switch (intent.getAction()) {
+ switch (action) {
case ACTION_PACKAGE_ADDED:
if (replacing) {
onPackageUpgraded(packageName, userIds);
diff --git a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
index f736056c5c7f..1dada92ab118 100644
--- a/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
+++ b/services/core/java/com/android/server/os/BugreportManagerServiceImpl.java
@@ -17,8 +17,10 @@
package com.android.server.os;
import android.annotation.RequiresPermission;
+import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.content.Context;
+import android.content.pm.UserInfo;
import android.os.Binder;
import android.os.BugreportParams;
import android.os.IDumpstate;
@@ -28,26 +30,29 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.UserManager;
import android.util.Slog;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
+
import java.io.FileDescriptor;
// TODO(b/111441001):
-// 1. Handle the case where another bugreport is in progress
-// 2. Make everything threadsafe
-// 3. Pass validation & other errors on listener
+// Intercept onFinished() & implement death recipient here and shutdown
+// bugreportd service.
/**
* Implementation of the service that provides a privileged API to capture and consume bugreports.
*
- * <p>Delegates the actualy generation to a native implementation of {@code Dumpstate}.
+ * <p>Delegates the actualy generation to a native implementation of {@code IDumpstate}.
*/
class BugreportManagerServiceImpl extends IDumpstate.Stub {
private static final String TAG = "BugreportManagerService";
private static final String BUGREPORT_SERVICE = "bugreportd";
private static final long DEFAULT_BUGREPORT_SERVICE_TIMEOUT_MILLIS = 30 * 1000;
- private IDumpstate mDs = null;
+ private final Object mLock = new Object();
private final Context mContext;
private final AppOpsManager mAppOps;
@@ -59,43 +64,44 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub {
@Override
@RequiresPermission(android.Manifest.permission.DUMP)
public IDumpstateToken setListener(String name, IDumpstateListener listener,
- boolean getSectionDetails) throws RemoteException {
- // TODO(b/111441001): Figure out if lazy setting of listener should be allowed
- // and if so how to handle it.
+ boolean getSectionDetails) {
throw new UnsupportedOperationException("setListener is not allowed on this service");
}
- // TODO(b/111441001): Intercept onFinished here in system server and shutdown
- // the bugreportd service.
@Override
@RequiresPermission(android.Manifest.permission.DUMP)
public void startBugreport(int callingUidUnused, String callingPackage,
FileDescriptor bugreportFd, FileDescriptor screenshotFd,
- int bugreportMode, IDumpstateListener listener) throws RemoteException {
- int callingUid = Binder.getCallingUid();
- // TODO(b/111441001): validate all arguments & ensure primary user
- validate(bugreportMode);
+ int bugreportMode, IDumpstateListener listener) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, "startBugreport");
+ Preconditions.checkNotNull(callingPackage);
+ Preconditions.checkNotNull(bugreportFd);
+ Preconditions.checkNotNull(listener);
+ validateBugreportMode(bugreportMode);
+ ensureIsPrimaryUser();
+ int callingUid = Binder.getCallingUid();
mAppOps.checkPackage(callingUid, callingPackage);
- mDs = getDumpstateService();
- if (mDs == null) {
- Slog.w(TAG, "Unable to get bugreport service");
- // TODO(b/111441001): pass error on listener
- return;
+
+ synchronized (mLock) {
+ startBugreportLocked(callingUid, callingPackage, bugreportFd, screenshotFd,
+ bugreportMode, listener);
}
- mDs.startBugreport(callingUid, callingPackage,
- bugreportFd, screenshotFd, bugreportMode, listener);
}
@Override
@RequiresPermission(android.Manifest.permission.DUMP)
- public void cancelBugreport() throws RemoteException {
- // This tells init to cancel bugreportd service.
- SystemProperties.set("ctl.stop", BUGREPORT_SERVICE);
- mDs = null;
+ public void cancelBugreport() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, "startBugreport");
+ // This tells init to cancel bugreportd service. Note that this is achieved through setting
+ // a system property which is not thread-safe. So the lock here offers thread-safety only
+ // among callers of the API.
+ synchronized (mLock) {
+ SystemProperties.set("ctl.stop", BUGREPORT_SERVICE);
+ }
}
- private boolean validate(@BugreportParams.BugreportMode int mode) {
+ private void validateBugreportMode(@BugreportParams.BugreportMode int mode) {
if (mode != BugreportParams.BUGREPORT_MODE_FULL
&& mode != BugreportParams.BUGREPORT_MODE_INTERACTIVE
&& mode != BugreportParams.BUGREPORT_MODE_REMOTE
@@ -103,9 +109,66 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub {
&& mode != BugreportParams.BUGREPORT_MODE_TELEPHONY
&& mode != BugreportParams.BUGREPORT_MODE_WIFI) {
Slog.w(TAG, "Unknown bugreport mode: " + mode);
- return false;
+ throw new IllegalArgumentException("Unknown bugreport mode: " + mode);
+ }
+ }
+
+ /**
+ * Validates that the current user is the primary user.
+ *
+ * @throws IllegalArgumentException if the current user is not the primary user
+ */
+ private void ensureIsPrimaryUser() {
+ UserInfo currentUser = null;
+ try {
+ currentUser = ActivityManager.getService().getCurrentUser();
+ } catch (RemoteException e) {
+ // Impossible to get RemoteException for an in-process call.
+ }
+
+ UserInfo primaryUser = UserManager.get(mContext).getPrimaryUser();
+ if (currentUser == null) {
+ logAndThrow("No current user. Only primary user is allowed to take bugreports.");
+ }
+ if (primaryUser == null) {
+ logAndThrow("No primary user. Only primary user is allowed to take bugreports.");
+ }
+ if (primaryUser.id != currentUser.id) {
+ logAndThrow("Current user not primary user. Only primary user"
+ + " is allowed to take bugreports.");
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void startBugreportLocked(int callingUid, String callingPackage,
+ FileDescriptor bugreportFd, FileDescriptor screenshotFd,
+ int bugreportMode, IDumpstateListener listener) {
+ if (isDumpstateBinderServiceRunningLocked()) {
+ Slog.w(TAG, "'dumpstate' is already running. Cannot start a new bugreport"
+ + " while another one is currently in progress.");
+ // TODO(b/111441001): Use a new error code; add this to the documentation of the API.
+ reportError(listener, IDumpstateListener.BUGREPORT_ERROR_RUNTIME_ERROR);
+ return;
+ }
+
+ IDumpstate ds = startAndGetDumpstateBinderServiceLocked();
+ if (ds == null) {
+ Slog.w(TAG, "Unable to get bugreport service");
+ reportError(listener, IDumpstateListener.BUGREPORT_ERROR_RUNTIME_ERROR);
+ return;
+ }
+ try {
+ ds.startBugreport(callingUid, callingPackage,
+ bugreportFd, screenshotFd, bugreportMode, listener);
+ } catch (RemoteException e) {
+ reportError(listener, IDumpstateListener.BUGREPORT_ERROR_RUNTIME_ERROR);
}
- return true;
+ }
+
+ @GuardedBy("mLock")
+ private boolean isDumpstateBinderServiceRunningLocked() {
+ IDumpstate ds = IDumpstate.Stub.asInterface(ServiceManager.getService("dumpstate"));
+ return ds != null;
}
/*
@@ -115,8 +178,12 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub {
* <p>Generating bugreports requires root privileges. To limit the footprint
* of the root access, the actual generation in Dumpstate binary is accessed as a
* oneshot service 'bugreport'.
+ *
+ * <p>Note that starting the service is achieved through setting a system property, which is
+ * not thread-safe. So the lock here offers thread-safety only among callers of the API.
*/
- private IDumpstate getDumpstateService() {
+ @GuardedBy("mLock")
+ private IDumpstate startAndGetDumpstateBinderServiceLocked() {
// Start bugreport service.
SystemProperties.set("ctl.start", BUGREPORT_SERVICE);
@@ -145,4 +212,18 @@ class BugreportManagerServiceImpl extends IDumpstate.Stub {
}
return ds;
}
+
+ private void reportError(IDumpstateListener listener, int errorCode) {
+ try {
+ listener.onError(errorCode);
+ } catch (RemoteException e) {
+ // Something went wrong in binder or app process. There's nothing to do here.
+ Slog.w(TAG, "onError() transaction threw RemoteException: " + e.getMessage());
+ }
+ }
+
+ private void logAndThrow(String message) {
+ Slog.w(TAG, message);
+ throw new IllegalArgumentException(message);
+ }
}
diff --git a/services/core/java/com/android/server/pm/OWNERS b/services/core/java/com/android/server/pm/OWNERS
index 60d792530101..640b155e69d5 100644
--- a/services/core/java/com/android/server/pm/OWNERS
+++ b/services/core/java/com/android/server/pm/OWNERS
@@ -19,6 +19,9 @@ per-file BackgroundDexOptService.java = ngeoffray@google.com
per-file CompilerStats.java = agampe@google.com
per-file CompilerStats.java = calin@google.com
per-file CompilerStats.java = ngeoffray@google.com
+per-file DynamicCodeLoggingService.java = agampe@google.com
+per-file DynamicCodeLoggingService.java = calin@google.com
+per-file DynamicCodeLoggingService.java = ngeoffray@google.com
per-file InstructionSets.java = agampe@google.com
per-file InstructionSets.java = calin@google.com
per-file InstructionSets.java = ngeoffray@google.com
@@ -51,6 +54,8 @@ per-file UserManagerService.java = omakoto@google.com
per-file UserManagerService.java = yamasani@google.com
per-file UserRestrictionsUtils.java = omakoto@google.com
per-file UserRestrictionsUtils.java = yamasani@google.com
+per-file UserRestrictionsUtils.java = rubinxu@google.com
+per-file UserRestrictionsUtils.java = sandness@google.com
# security
per-file KeySetHandle.java = cbrubaker@google.com
diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
index a8be07d76b58..08dea7f577a1 100644
--- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java
+++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java
@@ -480,8 +480,10 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub {
final String apkPath = pkg.baseCodePath;
final ApplicationInfo appInfo = pkg.applicationInfo;
final String outDexFile = appInfo.dataDir + "/code_cache/compiled_view.dex";
- if (appInfo.isPrivilegedApp()) {
+ if (appInfo.isPrivilegedApp() || appInfo.isDefaultToDeviceProtectedStorage()) {
// Privileged apps prefer to load trusted code so they don't use compiled views.
+ // Also disable the view compiler for protected storage apps since there are
+ // selinux permissions required for writing to user_de.
return false;
}
Log.i("PackageManager", "Compiling layouts in " + packageName + " (" + apkPath +
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
deleted file mode 100644
index 5f71b0b3a59a..000000000000
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorService.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2018 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.timezonedetector;
-
-import com.android.internal.util.DumpUtils;
-import com.android.server.SystemService;
-import android.app.timezonedetector.ITimeZoneDetectorService;
-import android.content.Context;
-import android.util.Slog;
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-public final class TimeZoneDetectorService extends ITimeZoneDetectorService.Stub {
- private static final String TAG = "timezonedetector.TimeZoneDetectorService";
-
- public static class Lifecycle extends SystemService {
-
- public Lifecycle(Context context) {
- super(context);
- }
-
- @Override
- public void onStart() {
- TimeZoneDetectorService service = TimeZoneDetectorService.create(getContext());
- // Publish the binder service so it can be accessed from other (appropriately
- // permissioned) processes.
- publishBinderService(Context.TIME_ZONE_DETECTOR_SERVICE, service);
- }
- }
-
- private final Context mContext;
-
- private static TimeZoneDetectorService create(Context context) {
- return new TimeZoneDetectorService(context);
- }
-
- public TimeZoneDetectorService(Context context) {
- mContext = context;
- }
-
- @Override
- public void stubbedCall() {
- // Empty call for initial tests.
- Slog.d(TAG, "stubbedCall() called");
- // TODO: Remove when there are real methods.
- }
-
- @Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
- // TODO: Implement when there is state.
- }
-}
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index c1607e94dd1e..f08e58579975 100644..100755
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -24,7 +24,6 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.PackageManager;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.HdmiHotplugEvent;
@@ -46,7 +45,6 @@ import android.media.tv.ITvInputHardwareCallback;
import android.media.tv.TvInputHardwareInfo;
import android.media.tv.TvInputInfo;
import android.media.tv.TvStreamConfig;
-import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
@@ -56,7 +54,6 @@ import android.util.ArrayMap;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
-import android.view.KeyEvent;
import android.view.Surface;
import com.android.internal.util.DumpUtils;
@@ -943,7 +940,7 @@ class TvInputHardwareManager implements TvInputHal.Callback {
sinkChannelMask = sinkConfig.channelMask();
}
if (sinkFormat == AudioFormat.ENCODING_DEFAULT) {
- sinkChannelMask = sinkConfig.format();
+ sinkFormat = sinkConfig.format();
}
}
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index d5e59c8dfd6a..c30babd464f4 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -128,6 +128,8 @@ public final class TvInputManagerService extends SystemService {
private final WatchLogHandler mWatchLogHandler;
+ private IBinder.DeathRecipient mDeathRecipient;
+
public TvInputManagerService(Context context) {
super(context);
@@ -672,6 +674,7 @@ public final class TvInputManagerService extends SystemService {
if (sessionToken == userState.mainSessionToken) {
setMainLocked(sessionToken, false, callingUid, userId);
}
+ sessionState.session.asBinder().unlinkToDeath(sessionState, 0);
sessionState.session.release();
}
} catch (RemoteException | SessionNotFoundException e) {
@@ -707,6 +710,7 @@ public final class TvInputManagerService extends SystemService {
clientState.sessionTokens.remove(sessionToken);
if (clientState.isEmpty()) {
userState.clientStateMap.remove(sessionState.client.asBinder());
+ sessionState.client.asBinder().unlinkToDeath(clientState, 0);
}
}
@@ -1000,17 +1004,19 @@ public final class TvInputManagerService extends SystemService {
synchronized (mLock) {
final UserState userState = getOrCreateUserStateLocked(resolvedUserId);
userState.callbackSet.add(callback);
- try {
- callback.asBinder().linkToDeath(new IBinder.DeathRecipient() {
- @Override
- public void binderDied() {
- synchronized (mLock) {
- if (userState.callbackSet != null) {
- userState.callbackSet.remove(callback);
- }
+ mDeathRecipient = new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ synchronized (mLock) {
+ if (userState.callbackSet != null) {
+ userState.callbackSet.remove(callback);
}
}
- }, 0);
+ }
+ };
+
+ try {
+ callback.asBinder().linkToDeath(mDeathRecipient, 0);
} catch (RemoteException e) {
Slog.e(TAG, "client process has already died", e);
}
@@ -1029,6 +1035,7 @@ public final class TvInputManagerService extends SystemService {
synchronized (mLock) {
UserState userState = getOrCreateUserStateLocked(resolvedUserId);
userState.callbackSet.remove(callback);
+ callback.asBinder().unlinkToDeath(mDeathRecipient, 0);
}
} finally {
Binder.restoreCallingIdentity(identity);
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 5f2a0e8ba9d8..6735ab4d9ce2 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -120,6 +120,7 @@ cc_defaults {
"android.frameworks.schedulerservice@1.0",
"android.frameworks.sensorservice@1.0",
"android.system.suspend@1.0",
+ "suspend_control_aidl_interface-cpp",
],
static_libs: [
diff --git a/services/core/jni/com_android_server_am_BatteryStatsService.cpp b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
index 0ff60e44b0ce..8f6cafb17042 100644
--- a/services/core/jni/com_android_server_am_BatteryStatsService.cpp
+++ b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
@@ -30,8 +30,8 @@
#include <android/hardware/power/1.0/IPower.h>
#include <android/hardware/power/1.1/IPower.h>
-#include <android/system/suspend/1.0/ISystemSuspend.h>
-#include <android/system/suspend/1.0/ISystemSuspendCallback.h>
+#include <android/system/suspend/BnSuspendCallback.h>
+#include <android/system/suspend/ISuspendControlService.h>
#include <android_runtime/AndroidRuntime.h>
#include <jni.h>
@@ -44,14 +44,14 @@
using android::hardware::Return;
using android::hardware::Void;
+using android::system::suspend::BnSuspendCallback;
using android::hardware::power::V1_0::PowerStatePlatformSleepState;
using android::hardware::power::V1_0::PowerStateVoter;
using android::hardware::power::V1_0::Status;
using android::hardware::power::V1_1::PowerStateSubsystem;
using android::hardware::power::V1_1::PowerStateSubsystemSleepState;
using android::hardware::hidl_vec;
-using android::system::suspend::V1_0::ISystemSuspend;
-using android::system::suspend::V1_0::ISystemSuspendCallback;
+using android::system::suspend::ISuspendControlService;
using IPowerV1_1 = android::hardware::power::V1_1::IPower;
using IPowerV1_0 = android::hardware::power::V1_0::IPower;
@@ -66,7 +66,7 @@ static sem_t wakeup_sem;
extern sp<IPowerV1_0> getPowerHalV1_0();
extern sp<IPowerV1_1> getPowerHalV1_1();
extern bool processPowerHalReturn(const Return<void> &ret, const char* functionName);
-extern sp<ISystemSuspend> getSuspendHal();
+extern sp<ISuspendControlService> getSuspendControl();
// Java methods used in getLowPowerStats
static jmethodID jgetAndUpdatePlatformState = NULL;
@@ -74,17 +74,17 @@ static jmethodID jgetSubsystem = NULL;
static jmethodID jputVoter = NULL;
static jmethodID jputState = NULL;
-class WakeupCallback : public ISystemSuspendCallback {
-public:
- Return<void> notifyWakeup(bool success) override {
- ALOGV("In wakeup_callback: %s", success ? "resumed from suspend" : "suspend aborted");
+class WakeupCallback : public BnSuspendCallback {
+ public:
+ binder::Status notifyWakeup(bool success) override {
+ ALOGI("In wakeup_callback: %s", success ? "resumed from suspend" : "suspend aborted");
int ret = sem_post(&wakeup_sem);
if (ret < 0) {
char buf[80];
strerror_r(errno, buf, sizeof(buf));
ALOGE("Error posting wakeup sem: %s\n", buf);
}
- return Void();
+ return binder::Status::ok();
}
};
@@ -107,9 +107,12 @@ static jint nativeWaitWakeup(JNIEnv *env, jobject clazz, jobject outBuf)
jniThrowException(env, "java/lang/IllegalStateException", buf);
return -1;
}
- ALOGV("Registering callback...");
- sp<ISystemSuspend> suspendHal = getSuspendHal();
- suspendHal->registerCallback(new WakeupCallback());
+ sp<ISuspendControlService> suspendControl = getSuspendControl();
+ bool isRegistered = false;
+ suspendControl->registerCallback(new WakeupCallback(), &isRegistered);
+ if (!isRegistered) {
+ ALOGE("Failed to register wakeup callback");
+ }
}
// Wait for wakeup.
diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp
index 0c9b5f4999a0..9be728bac532 100644
--- a/services/core/jni/com_android_server_power_PowerManagerService.cpp
+++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp
@@ -20,6 +20,7 @@
#include <android/hardware/power/1.1/IPower.h>
#include <android/system/suspend/1.0/ISystemSuspend.h>
+#include <android/system/suspend/ISuspendControlService.h>
#include <nativehelper/JNIHelp.h>
#include "jni.h"
@@ -30,13 +31,14 @@
#include <android-base/chrono_utils.h>
#include <android_runtime/AndroidRuntime.h>
#include <android_runtime/Log.h>
+#include <binder/IServiceManager.h>
+#include <hardware/power.h>
+#include <hardware_legacy/power.h>
+#include <hidl/ServiceManagement.h>
#include <utils/Timers.h>
#include <utils/misc.h>
#include <utils/String8.h>
#include <utils/Log.h>
-#include <hardware/power.h>
-#include <hardware_legacy/power.h>
-#include <hidl/ServiceManagement.h>
#include "com_android_server_power_PowerManagerService.h"
@@ -48,6 +50,7 @@ using android::String8;
using android::system::suspend::V1_0::ISystemSuspend;
using android::system::suspend::V1_0::IWakeLock;
using android::system::suspend::V1_0::WakeLockType;
+using android::system::suspend::ISuspendControlService;
using IPowerV1_1 = android::hardware::power::V1_1::IPower;
using IPowerV1_0 = android::hardware::power::V1_0::IPower;
@@ -176,6 +179,7 @@ void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t
}
static sp<ISystemSuspend> gSuspendHal = nullptr;
+static sp<ISuspendControlService> gSuspendControl = nullptr;
static sp<IWakeLock> gSuspendBlocker = nullptr;
static std::mutex gSuspendMutex;
@@ -191,18 +195,33 @@ sp<ISystemSuspend> getSuspendHal() {
return gSuspendHal;
}
+sp<ISuspendControlService> getSuspendControl() {
+ static std::once_flag suspendControlFlag;
+ std::call_once(suspendControlFlag, [](){
+ while(gSuspendControl == nullptr) {
+ sp<IBinder> control =
+ defaultServiceManager()->getService(String16("suspend_control"));
+ if (control != nullptr) {
+ gSuspendControl = interface_cast<ISuspendControlService>(control);
+ }
+ }
+ });
+ return gSuspendControl;
+}
+
void enableAutoSuspend() {
static bool enabled = false;
-
- std::lock_guard<std::mutex> lock(gSuspendMutex);
if (!enabled) {
- sp<ISystemSuspend> suspendHal = getSuspendHal();
- suspendHal->enableAutosuspend();
- enabled = true;
+ sp<ISuspendControlService> suspendControl = getSuspendControl();
+ suspendControl->enableAutosuspend(&enabled);
}
- if (gSuspendBlocker) {
- gSuspendBlocker->release();
- gSuspendBlocker.clear();
+
+ {
+ std::lock_guard<std::mutex> lock(gSuspendMutex);
+ if (gSuspendBlocker) {
+ gSuspendBlocker->release();
+ gSuspendBlocker.clear();
+ }
}
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 3ecbd47cf12e..406822c774ca 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -37,6 +37,7 @@ import android.content.res.Resources.Theme;
import android.database.sqlite.SQLiteCompatibilityWalFlags;
import android.database.sqlite.SQLiteGlobal;
import android.hardware.display.DisplayManagerInternal;
+import android.net.NetworkStackClient;
import android.os.BaseBundle;
import android.os.Binder;
import android.os.Build;
@@ -238,8 +239,6 @@ public final class SystemServer {
"com.android.internal.car.CarServiceHelperService";
private static final String TIME_DETECTOR_SERVICE_CLASS =
"com.android.server.timedetector.TimeDetectorService$Lifecycle";
- private static final String TIME_ZONE_DETECTOR_SERVICE_CLASS =
- "com.android.server.timezonedetector.TimeZoneDetectorService$Lifecycle";
private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
@@ -758,6 +757,7 @@ public final class SystemServer {
private void startOtherServices() {
final Context context = mSystemContext;
VibratorService vibrator = null;
+ DynamicAndroidService dynamicAndroid = null;
IStorageManager storageManager = null;
NetworkManagementService networkManagement = null;
IpSecService ipSecService = null;
@@ -867,6 +867,11 @@ public final class SystemServer {
ServiceManager.addService("vibrator", vibrator);
traceEnd();
+ traceBeginAndSlog("StartDynamicAndroidService");
+ dynamicAndroid = new DynamicAndroidService(context);
+ ServiceManager.addService("dynamic_android", dynamicAndroid);
+ traceEnd();
+
if (!isWatch) {
traceBeginAndSlog("StartConsumerIrService");
consumerIr = new ConsumerIrService(context);
@@ -1236,9 +1241,7 @@ public final class SystemServer {
traceBeginAndSlog("StartNetworkStack");
try {
- final android.net.NetworkStack networkStack =
- context.getSystemService(android.net.NetworkStack.class);
- networkStack.start(context);
+ NetworkStackClient.getInstance().start(context);
} catch (Throwable e) {
reportWtf("starting Network Stack", e);
}
@@ -1310,14 +1313,6 @@ public final class SystemServer {
reportWtf("starting StartTimeDetectorService service", e);
}
traceEnd();
-
- traceBeginAndSlog("StartTimeZoneDetectorService");
- try {
- mSystemServiceManager.startService(TIME_ZONE_DETECTOR_SERVICE_CLASS);
- } catch (Throwable e) {
- reportWtf("starting StartTimeZoneDetectorService service", e);
- }
- traceEnd();
}
if (!isWatch) {
diff --git a/services/net/Android.bp b/services/net/Android.bp
index 638ec95ec544..8ad4d7679107 100644
--- a/services/net/Android.bp
+++ b/services/net/Android.bp
@@ -1,6 +1,10 @@
java_library_static {
name: "services.net",
srcs: ["java/**/*.java"],
+ static_libs: [
+ "netd_aidl_interface-java",
+ "networkstack-aidl-interfaces-java",
+ ]
}
filegroup {
diff --git a/services/net/java/android/net/NetworkStackClient.java b/services/net/java/android/net/NetworkStackClient.java
new file mode 100644
index 000000000000..1eb7b98d801a
--- /dev/null
+++ b/services/net/java/android/net/NetworkStackClient.java
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.net;
+
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
+import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_HIGH;
+import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.net.dhcp.DhcpServingParamsParcel;
+import android.net.dhcp.IDhcpServerCallbacks;
+import android.net.ip.IIpClientCallbacks;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+
+/**
+ * Service used to communicate with the network stack, which is running in a separate module.
+ * @hide
+ */
+public class NetworkStackClient {
+ private static final String TAG = NetworkStackClient.class.getSimpleName();
+
+ private static final int NETWORKSTACK_TIMEOUT_MS = 10_000;
+
+ private static NetworkStackClient sInstance;
+
+ @NonNull
+ @GuardedBy("mPendingNetStackRequests")
+ private final ArrayList<NetworkStackCallback> mPendingNetStackRequests = new ArrayList<>();
+ @Nullable
+ @GuardedBy("mPendingNetStackRequests")
+ private INetworkStackConnector mConnector;
+
+ private volatile boolean mNetworkStackStartRequested = false;
+
+ private interface NetworkStackCallback {
+ void onNetworkStackConnected(INetworkStackConnector connector);
+ }
+
+ private NetworkStackClient() { }
+
+ /**
+ * Get the NetworkStackClient singleton instance.
+ */
+ public static synchronized NetworkStackClient getInstance() {
+ if (sInstance == null) {
+ sInstance = new NetworkStackClient();
+ }
+ return sInstance;
+ }
+
+ /**
+ * Create a DHCP server according to the specified parameters.
+ *
+ * <p>The server will be returned asynchronously through the provided callbacks.
+ */
+ public void makeDhcpServer(final String ifName, final DhcpServingParamsParcel params,
+ final IDhcpServerCallbacks cb) {
+ requestConnector(connector -> {
+ try {
+ connector.makeDhcpServer(ifName, params, cb);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ });
+ }
+
+ /**
+ * Create an IpClient on the specified interface.
+ *
+ * <p>The IpClient will be returned asynchronously through the provided callbacks.
+ */
+ public void makeIpClient(String ifName, IIpClientCallbacks cb) {
+ requestConnector(connector -> {
+ try {
+ connector.makeIpClient(ifName, cb);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ });
+ }
+
+ /**
+ * Create a NetworkMonitor.
+ *
+ * <p>The INetworkMonitor will be returned asynchronously through the provided callbacks.
+ */
+ public void makeNetworkMonitor(
+ NetworkParcelable network, String name, INetworkMonitorCallbacks cb) {
+ requestConnector(connector -> {
+ try {
+ connector.makeNetworkMonitor(network, name, cb);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ });
+ }
+
+ private class NetworkStackConnection implements ServiceConnection {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ registerNetworkStackService(service);
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ // TODO: crash/reboot the system ?
+ Slog.wtf(TAG, "Lost network stack connector");
+ }
+ };
+
+ private void registerNetworkStackService(@NonNull IBinder service) {
+ final INetworkStackConnector connector = INetworkStackConnector.Stub.asInterface(service);
+
+ ServiceManager.addService(Context.NETWORK_STACK_SERVICE, service, false /* allowIsolated */,
+ DUMP_FLAG_PRIORITY_HIGH | DUMP_FLAG_PRIORITY_NORMAL);
+
+ final ArrayList<NetworkStackCallback> requests;
+ synchronized (mPendingNetStackRequests) {
+ requests = new ArrayList<>(mPendingNetStackRequests);
+ mPendingNetStackRequests.clear();
+ mConnector = connector;
+ }
+
+ for (NetworkStackCallback r : requests) {
+ r.onNetworkStackConnected(connector);
+ }
+ }
+
+ /**
+ * Start the network stack. Should be called only once on device startup.
+ *
+ * <p>This method will start the network stack either in the network stack process, or inside
+ * the system server on devices that do not support the network stack module. The network stack
+ * connector will then be delivered asynchronously to clients that requested it before it was
+ * started.
+ */
+ public void start(Context context) {
+ mNetworkStackStartRequested = true;
+ // Try to bind in-process if the library is available
+ IBinder connector = null;
+ try {
+ final Class service = Class.forName(
+ "com.android.server.NetworkStackService",
+ true /* initialize */,
+ context.getClassLoader());
+ connector = (IBinder) service.getMethod("makeConnector", Context.class)
+ .invoke(null, context);
+ } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
+ Slog.wtf(TAG, "Could not create network stack connector from NetworkStackService");
+ // TODO: crash/reboot system here ?
+ return;
+ } catch (ClassNotFoundException e) {
+ // Normal behavior if stack is provided by the app: fall through
+ }
+
+ // In-process network stack. Add the service to the service manager here.
+ if (connector != null) {
+ registerNetworkStackService(connector);
+ return;
+ }
+ // Start the network stack process. The service will be added to the service manager in
+ // NetworkStackConnection.onServiceConnected().
+ final Intent intent = new Intent(INetworkStackConnector.class.getName());
+ final ComponentName comp = intent.resolveSystemService(context.getPackageManager(), 0);
+ intent.setComponent(comp);
+
+ if (comp == null) {
+ Slog.wtf(TAG, "Could not resolve the network stack with " + intent);
+ // TODO: crash/reboot system server ?
+ return;
+ }
+ final PackageManager pm = context.getPackageManager();
+ int uid = -1;
+ try {
+ uid = pm.getPackageUid(comp.getPackageName(), UserHandle.USER_SYSTEM);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.wtf("Network stack package not found", e);
+ // Fall through
+ }
+ if (uid != Process.NETWORK_STACK_UID) {
+ throw new SecurityException("Invalid network stack UID: " + uid);
+ }
+
+ final int hasPermission =
+ pm.checkPermission(PERMISSION_MAINLINE_NETWORK_STACK, comp.getPackageName());
+ if (hasPermission != PERMISSION_GRANTED) {
+ throw new SecurityException(
+ "Network stack does not have permission " + PERMISSION_MAINLINE_NETWORK_STACK);
+ }
+
+ if (!context.bindServiceAsUser(intent, new NetworkStackConnection(),
+ Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT, UserHandle.SYSTEM)) {
+ Slog.wtf(TAG,
+ "Could not bind to network stack in-process, or in app with " + intent);
+ // TODO: crash/reboot system server if no network stack after a timeout ?
+ }
+ }
+
+ /**
+ * For non-system server clients, get the connector registered by the system server.
+ */
+ private INetworkStackConnector getRemoteConnector() {
+ // Block until the NetworkStack connector is registered in ServiceManager.
+ // <p>This is only useful for non-system processes that do not have a way to be notified of
+ // registration completion. Adding a callback system would be too heavy weight considering
+ // that the connector is registered on boot, so it is unlikely that a client would request
+ // it before it is registered.
+ // TODO: consider blocking boot on registration and simplify much of the logic in this class
+ IBinder connector;
+ try {
+ final long before = System.currentTimeMillis();
+ while ((connector = ServiceManager.getService(Context.NETWORK_STACK_SERVICE)) == null) {
+ Thread.sleep(20);
+ if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) {
+ Slog.e(TAG, "Timeout waiting for NetworkStack connector");
+ return null;
+ }
+ }
+ } catch (InterruptedException e) {
+ Slog.e(TAG, "Error waiting for NetworkStack connector", e);
+ return null;
+ }
+
+ return INetworkStackConnector.Stub.asInterface(connector);
+ }
+
+ private void requestConnector(@NonNull NetworkStackCallback request) {
+ // TODO: PID check.
+ final int caller = Binder.getCallingUid();
+ if (caller != Process.SYSTEM_UID && !UserHandle.isSameApp(caller, Process.BLUETOOTH_UID)) {
+ // Don't even attempt to obtain the connector and give a nice error message
+ throw new SecurityException(
+ "Only the system server should try to bind to the network stack.");
+ }
+
+ if (!mNetworkStackStartRequested) {
+ // The network stack is not being started in this process, e.g. this process is not
+ // the system server. Get a remote connector registered by the system server.
+ final INetworkStackConnector connector = getRemoteConnector();
+ synchronized (mPendingNetStackRequests) {
+ mConnector = connector;
+ }
+ request.onNetworkStackConnected(connector);
+ return;
+ }
+
+ final INetworkStackConnector connector;
+ synchronized (mPendingNetStackRequests) {
+ connector = mConnector;
+ if (connector == null) {
+ mPendingNetStackRequests.add(request);
+ return;
+ }
+ }
+
+ request.onNetworkStackConnected(connector);
+ }
+}
diff --git a/core/java/android/net/dhcp/DhcpServerCallbacks.java b/services/net/java/android/net/dhcp/DhcpServerCallbacks.java
index bb56876c77f5..bb56876c77f5 100644
--- a/core/java/android/net/dhcp/DhcpServerCallbacks.java
+++ b/services/net/java/android/net/dhcp/DhcpServerCallbacks.java
diff --git a/core/java/android/net/ip/IpClientCallbacks.java b/services/net/java/android/net/ip/IpClientCallbacks.java
index db01ae4d4d9c..db01ae4d4d9c 100644
--- a/core/java/android/net/ip/IpClientCallbacks.java
+++ b/services/net/java/android/net/ip/IpClientCallbacks.java
diff --git a/services/net/java/android/net/ip/IpClientUtil.java b/services/net/java/android/net/ip/IpClientUtil.java
index 2a2a67a92a86..bf917bf88b2d 100644
--- a/services/net/java/android/net/ip/IpClientUtil.java
+++ b/services/net/java/android/net/ip/IpClientUtil.java
@@ -23,8 +23,7 @@ import android.content.Context;
import android.net.DhcpResultsParcelable;
import android.net.LinkProperties;
import android.net.LinkPropertiesParcelable;
-import android.net.NetworkStack;
-import android.net.ip.IIpClientCallbacks;
+import android.net.NetworkStackClient;
import android.os.ConditionVariable;
import java.io.FileDescriptor;
@@ -76,30 +75,17 @@ public class IpClientUtil {
*
* <p>This is a convenience method to allow clients to use {@link IpClientCallbacks} instead of
* {@link IIpClientCallbacks}.
- * @see {@link NetworkStack#makeIpClient(String, IIpClientCallbacks)}
+ * @see {@link NetworkStackClient#makeIpClient(String, IIpClientCallbacks)}
*/
public static void makeIpClient(Context context, String ifName, IpClientCallbacks callback) {
- context.getSystemService(NetworkStack.class)
- .makeIpClient(ifName, new IpClientCallbacksProxy(callback));
- }
-
- /**
- * Create a new IpClient.
- *
- * <p>This is a convenience method to allow clients to use {@link IpClientCallbacksProxy}
- * instead of {@link IIpClientCallbacks}.
- * @see {@link NetworkStack#makeIpClient(String, IIpClientCallbacks)}
- */
- public static void makeIpClient(
- Context context, String ifName, IpClientCallbacksProxy callback) {
- context.getSystemService(NetworkStack.class)
- .makeIpClient(ifName, callback);
+ // TODO: migrate clients and remove context argument
+ NetworkStackClient.getInstance().makeIpClient(ifName, new IpClientCallbacksProxy(callback));
}
/**
* Wrapper to relay calls from {@link IIpClientCallbacks} to {@link IpClientCallbacks}.
*/
- public static class IpClientCallbacksProxy extends IIpClientCallbacks.Stub {
+ private static class IpClientCallbacksProxy extends IIpClientCallbacks.Stub {
protected final IpClientCallbacks mCb;
/**
diff --git a/services/net/java/android/net/ip/IpServer.java b/services/net/java/android/net/ip/IpServer.java
index 7910c9a69310..34fc7354d63e 100644
--- a/services/net/java/android/net/ip/IpServer.java
+++ b/services/net/java/android/net/ip/IpServer.java
@@ -22,7 +22,6 @@ import static android.net.util.NetworkConstants.FF;
import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
import static android.net.util.NetworkConstants.asByte;
-import android.content.Context;
import android.net.ConnectivityManager;
import android.net.INetd;
import android.net.INetworkStackStatusCallback;
@@ -31,7 +30,7 @@ import android.net.InterfaceConfiguration;
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
-import android.net.NetworkStack;
+import android.net.NetworkStackClient;
import android.net.RouteInfo;
import android.net.dhcp.DhcpServerCallbacks;
import android.net.dhcp.DhcpServingParamsParcel;
@@ -132,10 +131,6 @@ public class IpServer extends StateMachine {
}
public static class Dependencies {
- private final Context mContext;
- public Dependencies(Context context) {
- mContext = context;
- }
public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) {
return new RouterAdvertisementDaemon(ifParams);
}
@@ -153,7 +148,7 @@ public class IpServer extends StateMachine {
*/
public void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
DhcpServerCallbacks cb) {
- mContext.getSystemService(NetworkStack.class).makeDhcpServer(ifName, params, cb);
+ NetworkStackClient.getInstance().makeDhcpServer(ifName, params, cb);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/DynamicAndroidServiceTest.java b/services/tests/servicestests/src/com/android/server/DynamicAndroidServiceTest.java
new file mode 100644
index 000000000000..149428443fa1
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/DynamicAndroidServiceTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2019 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;
+
+import android.os.IDynamicAndroidService;
+import android.os.ServiceManager;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+
+public class DynamicAndroidServiceTest extends AndroidTestCase {
+ private static final String TAG = "DynamicAndroidServiceTests";
+ private IDynamicAndroidService mService;
+
+ @Override
+ protected void setUp() throws Exception {
+ mService =
+ IDynamicAndroidService.Stub.asInterface(
+ ServiceManager.getService("dynamic_android"));
+ }
+
+ @LargeTest
+ public void test1() {
+ assertTrue("dynamic_android service available", mService != null);
+ try {
+ mService.startInstallation(1 << 20, 8 << 30);
+ fail("DynamicAndroidService did not throw SecurityException as expected");
+ } catch (SecurityException e) {
+ // expected
+ } catch (Exception e) {
+ fail(e.toString());
+ }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java b/services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java
index c9180a99c98d..a5ac20e951ec 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkManagementInternalTest.java
@@ -16,12 +16,12 @@
package com.android.server;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_POWERSAVE;
-import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_STANDBY;
-import static android.net.NetworkPolicyManager.FIREWALL_RULE_ALLOW;
+import static android.net.INetd.FIREWALL_CHAIN_DOZABLE;
+import static android.net.INetd.FIREWALL_CHAIN_POWERSAVE;
+import static android.net.INetd.FIREWALL_CHAIN_STANDBY;
+import static android.net.INetd.FIREWALL_RULE_ALLOW;
+import static android.net.INetd.FIREWALL_RULE_DENY;
import static android.net.NetworkPolicyManager.FIREWALL_RULE_DEFAULT;
-import static android.net.NetworkPolicyManager.FIREWALL_RULE_DENY;
import static android.util.DebugUtils.valueToString;
import static org.junit.Assert.assertEquals;
diff --git a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
deleted file mode 100644
index 19d31cfafc35..000000000000
--- a/services/tests/servicestests/src/com/android/server/timezonedetector/TimeZoneDetectorServiceTest.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright (C) 2018 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.timezonedetector;
-
-import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Unit tests for the {@link TimeZoneDetectorService}.
- */
-@RunWith(AndroidJUnit4.class)
-public class TimeZoneDetectorServiceTest {
-
- private TimeZoneDetectorService mTimeZoneDetectorService;
-
- @Before
- public void setUp() {
- final Context context = InstrumentationRegistry.getContext();
- mTimeZoneDetectorService = new TimeZoneDetectorService(context);
- }
-
- @Test
- public void testStubbedCall() {
- mTimeZoneDetectorService.stubbedCall();
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
index 84611667d113..39fc715c7770 100644
--- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java
@@ -124,6 +124,7 @@ public class AppStandbyControllerTests {
static class MyInjector extends AppStandbyController.Injector {
long mElapsedRealtime;
+ boolean mIsAppIdleEnabled = true;
boolean mIsCharging;
List<String> mPowerSaveWhitelistExceptIdle = new ArrayList<>();
boolean mDisplayOn;
@@ -155,7 +156,7 @@ public class AppStandbyControllerTests {
@Override
boolean isAppIdleEnabled() {
- return true;
+ return mIsAppIdleEnabled;
}
@Override
@@ -266,6 +267,13 @@ public class AppStandbyControllerTests {
}
}
+ private void setAppIdleEnabled(AppStandbyController controller, boolean enabled) {
+ mInjector.mIsAppIdleEnabled = enabled;
+ if (controller != null) {
+ controller.setAppIdleEnabled(enabled);
+ }
+ }
+
private AppStandbyController setupController() throws Exception {
mInjector.mElapsedRealtime = 0;
setupPm(mInjector.getContext().getPackageManager());
@@ -335,7 +343,7 @@ public class AppStandbyControllerTests {
public void onParoleStateChanged(boolean isParoleOn) {
synchronized (this) {
// Only record information if it is being looked for
- if (mLatch.getCount() > 0) {
+ if (mLatch != null && mLatch.getCount() > 0) {
mOnParole = isParoleOn;
mLastParoleChangeTime = getCurrentTime();
mLatch.countDown();
@@ -396,6 +404,74 @@ public class AppStandbyControllerTests {
marginOfError);
}
+ @Test
+ public void testEnabledState() throws Exception {
+ TestParoleListener paroleListener = new TestParoleListener();
+ mController.addListener(paroleListener);
+ long lastUpdateTime;
+
+ // Test that listeners are notified if enabled changes when the device is not in parole.
+ setChargingState(mController, false);
+
+ // Start off not enabled. Device is effectively on permanent parole.
+ setAppIdleEnabled(mController, false);
+
+ // Enable controller
+ paroleListener.rearmLatch();
+ setAppIdleEnabled(mController, true);
+ paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
+ assertFalse(paroleListener.mOnParole);
+ lastUpdateTime = paroleListener.getLastParoleChangeTime();
+
+ paroleListener.rearmLatch();
+ setAppIdleEnabled(mController, true);
+ paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
+ assertFalse(paroleListener.mOnParole);
+ // Make sure AppStandbyController doesn't notify listeners when there's no change.
+ assertEquals(lastUpdateTime, paroleListener.getLastParoleChangeTime());
+
+ // Disable controller
+ paroleListener.rearmLatch();
+ setAppIdleEnabled(mController, false);
+ paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
+ assertTrue(paroleListener.mOnParole);
+ lastUpdateTime = paroleListener.getLastParoleChangeTime();
+
+ paroleListener.rearmLatch();
+ setAppIdleEnabled(mController, false);
+ paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
+ assertTrue(paroleListener.mOnParole);
+ // Make sure AppStandbyController doesn't notify listeners when there's no change.
+ assertEquals(lastUpdateTime, paroleListener.getLastParoleChangeTime());
+
+
+ // Test that listeners aren't notified if enabled status changes when the device is already
+ // in parole.
+
+ // A device is in parole whenever it's charging.
+ setChargingState(mController, true);
+
+ // Start off not enabled.
+ paroleListener.rearmLatch();
+ setAppIdleEnabled(mController, false);
+ paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
+ assertTrue(paroleListener.mOnParole);
+ lastUpdateTime = paroleListener.getLastParoleChangeTime();
+
+ // Test that toggling doesn't notify the listener.
+ paroleListener.rearmLatch();
+ setAppIdleEnabled(mController, true);
+ paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
+ assertTrue(paroleListener.mOnParole);
+ assertEquals(lastUpdateTime, paroleListener.getLastParoleChangeTime());
+
+ paroleListener.rearmLatch();
+ setAppIdleEnabled(mController, false);
+ paroleListener.awaitOnLatch(STABLE_CHARGING_THRESHOLD * 3 / 2);
+ assertTrue(paroleListener.mOnParole);
+ assertEquals(lastUpdateTime, paroleListener.getLastParoleChangeTime());
+ }
+
private void assertTimeout(AppStandbyController controller, long elapsedTime, int bucket) {
mInjector.mElapsedRealtime = elapsedTime;
controller.checkIdleStates(USER_ID);
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index 9c62700a9118..e380d7a6808b 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -30,18 +30,19 @@ import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_EXEMPTED_SYNC
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_BACKGROUND;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_MOVE_TO_FOREGROUND;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_NOTIFICATION_SEEN;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SLICE_PINNED;
+import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SLICE_PINNED_PRIV;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYNC_ADAPTER;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYSTEM_INTERACTION;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SYSTEM_UPDATE;
import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_USER_INTERACTION;
-import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SLICE_PINNED;
-import static android.app.usage.UsageStatsManager.REASON_SUB_USAGE_SLICE_PINNED_PRIV;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_ACTIVE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_EXEMPTED;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_FREQUENT;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_NEVER;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RARE;
import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET;
+
import static com.android.server.SystemService.PHASE_BOOT_COMPLETED;
import static com.android.server.SystemService.PHASE_SYSTEM_SERVICES_READY;
@@ -340,14 +341,21 @@ public class AppStandbyController {
}
void setAppIdleEnabled(boolean enabled) {
- mAppIdleEnabled = enabled;
+ synchronized (mAppIdleLock) {
+ if (mAppIdleEnabled != enabled) {
+ final boolean oldParoleState = isParoledOrCharging();
+ mAppIdleEnabled = enabled;
+ if (isParoledOrCharging() != oldParoleState) {
+ postParoleStateChanged();
+ }
+ }
+ }
}
public void onBootPhase(int phase) {
mInjector.onBootPhase(phase);
if (phase == PHASE_SYSTEM_SERVICES_READY) {
Slog.d(TAG, "Setting app idle enabled state");
- setAppIdleEnabled(mInjector.isAppIdleEnabled());
// Observe changes to the threshold
SettingsObserver settingsObserver = new SettingsObserver(mHandler);
settingsObserver.registerObserver();
@@ -1807,8 +1815,6 @@ public class AppStandbyController {
mContext.getContentResolver(),
Global.APP_IDLE_CONSTANTS));
}
- // Check if app_idle_enabled has changed
- setAppIdleEnabled(mInjector.isAppIdleEnabled());
// Look at global settings for this.
// TODO: Maybe apply different thresholds for different users.
@@ -1880,6 +1886,10 @@ public class AppStandbyController {
(KEY_STABLE_CHARGING_THRESHOLD,
COMPRESS_TIME ? ONE_MINUTE : DEFAULT_STABLE_CHARGING_THRESHOLD);
}
+
+ // Check if app_idle_enabled has changed. Do this after getting the rest of the settings
+ // in case we need to change something based on the new values.
+ setAppIdleEnabled(mInjector.isAppIdleEnabled());
}
long[] parseLongArray(String values, long[] defaults) {
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 7ea61e30dafe..da682c6df621 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -69,6 +69,7 @@ import android.os.storage.StorageVolume;
import android.provider.Settings;
import android.service.usb.UsbDeviceManagerProto;
import android.service.usb.UsbHandlerProto;
+import android.sysprop.AdbProperties;
import android.sysprop.VoldProperties;
import android.util.Pair;
import android.util.Slog;
@@ -285,7 +286,7 @@ public class UsbDeviceManager implements ActivityManagerInternal.ScreenObserver
}
mControlFds.put(UsbManager.FUNCTION_PTP, ptpFd);
- boolean secureAdbEnabled = SystemProperties.getBoolean("ro.adb.secure", false);
+ boolean secureAdbEnabled = AdbProperties.secure().orElse(false);
boolean dataEncrypted = "1".equals(VoldProperties.decrypt().orElse(""));
if (secureAdbEnabled && !dataEncrypted) {
mDebuggingManager = new UsbDebuggingManager(context);
diff --git a/startop/view_compiler/OWNERS b/startop/view_compiler/OWNERS
new file mode 100644
index 000000000000..e5aead9ddac8
--- /dev/null
+++ b/startop/view_compiler/OWNERS
@@ -0,0 +1,2 @@
+eholk@google.com
+mathieuc@google.com
diff --git a/startop/view_compiler/TEST_MAPPING b/startop/view_compiler/TEST_MAPPING
index 700607536890..5f7d3f99ae81 100644
--- a/startop/view_compiler/TEST_MAPPING
+++ b/startop/view_compiler/TEST_MAPPING
@@ -4,6 +4,14 @@
"name": "dex-builder-test"
},
{
+ "name": "CtsViewTestCases",
+ "options": [
+ {
+ "include-filter": "android.view.cts.PrecompiledLayoutTest"
+ }
+ ]
+ },
+ {
"name": "view-compiler-tests",
"host": true
}
diff --git a/startop/view_compiler/dex_builder.cc b/startop/view_compiler/dex_builder.cc
index a78f7d53d135..6047e8c74e38 100644
--- a/startop/view_compiler/dex_builder.cc
+++ b/startop/view_compiler/dex_builder.cc
@@ -21,8 +21,6 @@
#include <fstream>
#include <memory>
-#define DCHECK_NOT_NULL(p) DCHECK((p) != nullptr)
-
namespace startop {
namespace dex {
@@ -32,6 +30,8 @@ using std::string;
using ::dex::kAccPublic;
using Op = Instruction::Op;
+using Opcode = ::art::Instruction::Code;
+
const TypeDescriptor TypeDescriptor::Int() { return TypeDescriptor{"I"}; };
const TypeDescriptor TypeDescriptor::Void() { return TypeDescriptor{"V"}; };
@@ -42,6 +42,23 @@ constexpr uint8_t kDexFileMagic[]{0x64, 0x65, 0x78, 0x0a, 0x30, 0x33, 0x38, 0x00
// Strings lengths can be 32 bits long, but encoded as LEB128 this can take up to five bytes.
constexpr size_t kMaxEncodedStringLength{5};
+// Converts invoke-* to invoke-*/range
+constexpr Opcode InvokeToInvokeRange(Opcode opcode) {
+ switch (opcode) {
+ case ::art::Instruction::INVOKE_VIRTUAL:
+ return ::art::Instruction::INVOKE_VIRTUAL_RANGE;
+ case ::art::Instruction::INVOKE_DIRECT:
+ return ::art::Instruction::INVOKE_DIRECT_RANGE;
+ case ::art::Instruction::INVOKE_STATIC:
+ return ::art::Instruction::INVOKE_STATIC_RANGE;
+ case ::art::Instruction::INVOKE_INTERFACE:
+ return ::art::Instruction::INVOKE_INTERFACE_RANGE;
+ default:
+ LOG(FATAL) << opcode << " is not a recognized invoke opcode.";
+ UNREACHABLE();
+ }
+}
+
} // namespace
std::ostream& operator<<(std::ostream& out, const Instruction::Op& opcode) {
@@ -55,6 +72,9 @@ std::ostream& operator<<(std::ostream& out, const Instruction::Op& opcode) {
case Instruction::Op::kMove:
out << "kMove";
return out;
+ case Instruction::Op::kMoveObject:
+ out << "kMoveObject";
+ return out;
case Instruction::Op::kInvokeVirtual:
out << "kInvokeVirtual";
return out;
@@ -233,6 +253,11 @@ std::string Prototype::Shorty() const {
return shorty;
}
+const TypeDescriptor& Prototype::ArgType(size_t index) const {
+ CHECK_LT(index, param_types_.size());
+ return param_types_[index];
+}
+
ClassBuilder::ClassBuilder(DexBuilder* parent, const std::string& name, ir::Class* class_def)
: parent_(parent), type_descriptor_{TypeDescriptor::FromClassname(name)}, class_(class_def) {}
@@ -257,10 +282,10 @@ ir::EncodedMethod* MethodBuilder::Encode() {
method->access_flags = kAccPublic | ::dex::kAccStatic;
auto* code = dex_->Alloc<ir::Code>();
- DCHECK_NOT_NULL(decl_->prototype);
+ CHECK(decl_->prototype != nullptr);
size_t const num_args =
decl_->prototype->param_types != nullptr ? decl_->prototype->param_types->types.size() : 0;
- code->registers = num_registers_ + num_args;
+ code->registers = num_registers_ + num_args + kMaxScratchRegisters;
code->ins_count = num_args;
EncodeInstructions();
code->instructions = slicer::ArrayView<const ::dex::u2>(buffer_.data(), buffer_.size());
@@ -292,7 +317,7 @@ void MethodBuilder::BuildReturn(Value src, bool is_object) {
}
void MethodBuilder::BuildConst4(Value target, int value) {
- DCHECK_LT(value, 16);
+ CHECK_LT(value, 16);
AddInstruction(Instruction::OpWithArgs(Op::kMove, target, Value::Immediate(value)));
}
@@ -315,6 +340,7 @@ void MethodBuilder::EncodeInstruction(const Instruction& instruction) {
case Instruction::Op::kReturnObject:
return EncodeReturn(instruction, ::art::Instruction::RETURN_OBJECT);
case Instruction::Op::kMove:
+ case Instruction::Op::kMoveObject:
return EncodeMove(instruction);
case Instruction::Op::kInvokeVirtual:
return EncodeInvoke(instruction, art::Instruction::INVOKE_VIRTUAL);
@@ -338,33 +364,43 @@ void MethodBuilder::EncodeInstruction(const Instruction& instruction) {
}
void MethodBuilder::EncodeReturn(const Instruction& instruction, ::art::Instruction::Code opcode) {
- DCHECK(!instruction.dest().has_value());
+ CHECK(!instruction.dest().has_value());
if (instruction.args().size() == 0) {
Encode10x(art::Instruction::RETURN_VOID);
} else {
- DCHECK_EQ(1, instruction.args().size());
+ CHECK_EQ(1, instruction.args().size());
size_t source = RegisterValue(instruction.args()[0]);
Encode11x(opcode, source);
}
}
void MethodBuilder::EncodeMove(const Instruction& instruction) {
- DCHECK_EQ(Instruction::Op::kMove, instruction.opcode());
- DCHECK(instruction.dest().has_value());
- DCHECK(instruction.dest()->is_register() || instruction.dest()->is_parameter());
- DCHECK_EQ(1, instruction.args().size());
+ CHECK(Instruction::Op::kMove == instruction.opcode() ||
+ Instruction::Op::kMoveObject == instruction.opcode());
+ CHECK(instruction.dest().has_value());
+ CHECK(instruction.dest()->is_variable());
+ CHECK_EQ(1, instruction.args().size());
const Value& source = instruction.args()[0];
if (source.is_immediate()) {
// TODO: support more registers
- DCHECK_LT(RegisterValue(*instruction.dest()), 16);
+ CHECK_LT(RegisterValue(*instruction.dest()), 16);
Encode11n(art::Instruction::CONST_4, RegisterValue(*instruction.dest()), source.value());
} else if (source.is_string()) {
constexpr size_t kMaxRegisters = 256;
- DCHECK_LT(RegisterValue(*instruction.dest()), kMaxRegisters);
- DCHECK_LT(source.value(), 65536); // make sure we don't need a jumbo string
+ CHECK_LT(RegisterValue(*instruction.dest()), kMaxRegisters);
+ CHECK_LT(source.value(), 65536); // make sure we don't need a jumbo string
Encode21c(::art::Instruction::CONST_STRING, RegisterValue(*instruction.dest()), source.value());
+ } else if (source.is_variable()) {
+ // For the moment, we only use this when we need to reshuffle registers for
+ // an invoke instruction, meaning we are too big for the 4-bit version.
+ // We'll err on the side of caution and always generate the 16-bit form of
+ // the instruction.
+ Opcode opcode = instruction.opcode() == Instruction::Op::kMove
+ ? ::art::Instruction::MOVE_16
+ : ::art::Instruction::MOVE_OBJECT_16;
+ Encode32x(opcode, RegisterValue(*instruction.dest()), RegisterValue(source));
} else {
UNIMPLEMENTED(FATAL);
}
@@ -373,22 +409,61 @@ void MethodBuilder::EncodeMove(const Instruction& instruction) {
void MethodBuilder::EncodeInvoke(const Instruction& instruction, ::art::Instruction::Code opcode) {
constexpr size_t kMaxArgs = 5;
+ // Currently, we only support up to 5 arguments.
CHECK_LE(instruction.args().size(), kMaxArgs);
uint8_t arguments[kMaxArgs]{};
+ bool has_long_args = false;
for (size_t i = 0; i < instruction.args().size(); ++i) {
CHECK(instruction.args()[i].is_variable());
arguments[i] = RegisterValue(instruction.args()[i]);
+ if (!IsShortRegister(arguments[i])) {
+ has_long_args = true;
+ }
}
- Encode35c(opcode,
- instruction.args().size(),
- instruction.method_id(),
- arguments[0],
- arguments[1],
- arguments[2],
- arguments[3],
- arguments[4]);
+ if (has_long_args) {
+ // Some of the registers don't fit in the four bit short form of the invoke
+ // instruction, so we need to do an invoke/range. To do this, we need to
+ // first move all the arguments into contiguous temporary registers.
+ std::array<Value, kMaxArgs> scratch = GetScratchRegisters<kMaxArgs>();
+
+ const auto& prototype = dex_->GetPrototypeByMethodId(instruction.method_id());
+ CHECK(prototype.has_value());
+
+ for (size_t i = 0; i < instruction.args().size(); ++i) {
+ Instruction::Op move_op;
+ if (opcode == ::art::Instruction::INVOKE_VIRTUAL ||
+ opcode == ::art::Instruction::INVOKE_DIRECT) {
+ // In this case, there is an implicit `this` argument, which is always an object.
+ if (i == 0) {
+ move_op = Instruction::Op::kMoveObject;
+ } else {
+ move_op = prototype->ArgType(i - 1).is_object() ? Instruction::Op::kMoveObject
+ : Instruction::Op::kMove;
+ }
+ } else {
+ move_op = prototype->ArgType(i).is_object() ? Instruction::Op::kMoveObject
+ : Instruction::Op::kMove;
+ }
+
+ EncodeMove(Instruction::OpWithArgs(move_op, scratch[i], instruction.args()[i]));
+ }
+
+ Encode3rc(InvokeToInvokeRange(opcode),
+ instruction.args().size(),
+ instruction.method_id(),
+ RegisterValue(scratch[0]));
+ } else {
+ Encode35c(opcode,
+ instruction.args().size(),
+ instruction.method_id(),
+ arguments[0],
+ arguments[1],
+ arguments[2],
+ arguments[3],
+ arguments[4]);
+ }
// If there is a return value, add a move-result instruction
if (instruction.dest().has_value()) {
@@ -416,26 +491,26 @@ void MethodBuilder::EncodeBranch(art::Instruction::Code op, const Instruction& i
}
void MethodBuilder::EncodeNew(const Instruction& instruction) {
- DCHECK_EQ(Instruction::Op::kNew, instruction.opcode());
- DCHECK(instruction.dest().has_value());
- DCHECK(instruction.dest()->is_variable());
- DCHECK_EQ(1, instruction.args().size());
+ CHECK_EQ(Instruction::Op::kNew, instruction.opcode());
+ CHECK(instruction.dest().has_value());
+ CHECK(instruction.dest()->is_variable());
+ CHECK_EQ(1, instruction.args().size());
const Value& type = instruction.args()[0];
- DCHECK_LT(RegisterValue(*instruction.dest()), 256);
- DCHECK(type.is_type());
+ CHECK_LT(RegisterValue(*instruction.dest()), 256);
+ CHECK(type.is_type());
Encode21c(::art::Instruction::NEW_INSTANCE, RegisterValue(*instruction.dest()), type.value());
}
void MethodBuilder::EncodeCast(const Instruction& instruction) {
- DCHECK_EQ(Instruction::Op::kCheckCast, instruction.opcode());
- DCHECK(instruction.dest().has_value());
- DCHECK(instruction.dest()->is_variable());
- DCHECK_EQ(1, instruction.args().size());
+ CHECK_EQ(Instruction::Op::kCheckCast, instruction.opcode());
+ CHECK(instruction.dest().has_value());
+ CHECK(instruction.dest()->is_variable());
+ CHECK_EQ(1, instruction.args().size());
const Value& type = instruction.args()[0];
- DCHECK_LT(RegisterValue(*instruction.dest()), 256);
- DCHECK(type.is_type());
+ CHECK_LT(RegisterValue(*instruction.dest()), 256);
+ CHECK(type.is_type());
Encode21c(::art::Instruction::CHECK_CAST, RegisterValue(*instruction.dest()), type.value());
}
@@ -443,9 +518,9 @@ size_t MethodBuilder::RegisterValue(const Value& value) const {
if (value.is_register()) {
return value.value();
} else if (value.is_parameter()) {
- return value.value() + num_registers_;
+ return value.value() + num_registers_ + kMaxScratchRegisters;
}
- DCHECK(false && "Must be either a parameter or a register");
+ CHECK(false && "Must be either a parameter or a register");
return 0;
}
@@ -498,7 +573,7 @@ const MethodDeclData& DexBuilder::GetOrDeclareMethod(TypeDescriptor type, const
// update the index -> ir node map (see tools/dexter/slicer/dex_ir_builder.cc)
auto new_index = dex_file_->methods_indexes.AllocateIndex();
auto& ir_node = dex_file_->methods_map[new_index];
- SLICER_CHECK(ir_node == nullptr);
+ CHECK(ir_node == nullptr);
ir_node = decl;
decl->orig_index = decl->index = new_index;
@@ -508,6 +583,15 @@ const MethodDeclData& DexBuilder::GetOrDeclareMethod(TypeDescriptor type, const
return entry;
}
+std::optional<const Prototype> DexBuilder::GetPrototypeByMethodId(size_t method_id) const {
+ for (const auto& entry : method_id_map_) {
+ if (entry.second.id == method_id) {
+ return entry.first.prototype;
+ }
+ }
+ return {};
+}
+
ir::Proto* DexBuilder::GetOrEncodeProto(Prototype prototype) {
ir::Proto*& ir_proto = proto_map_[prototype];
if (ir_proto == nullptr) {
diff --git a/startop/view_compiler/dex_builder.h b/startop/view_compiler/dex_builder.h
index 757d863461f0..541d80077bd3 100644
--- a/startop/view_compiler/dex_builder.h
+++ b/startop/view_compiler/dex_builder.h
@@ -16,6 +16,7 @@
#ifndef DEX_BUILDER_H_
#define DEX_BUILDER_H_
+#include <array>
#include <forward_list>
#include <map>
#include <optional>
@@ -70,6 +71,8 @@ class TypeDescriptor {
// Return the shorty descriptor, such as I or L
std::string short_descriptor() const { return descriptor().substr(0, 1); }
+ bool is_object() const { return short_descriptor() == "L"; }
+
bool operator<(const TypeDescriptor& rhs) const { return descriptor_ < rhs.descriptor_; }
private:
@@ -92,6 +95,8 @@ class Prototype {
// Get the shorty descriptor, such as VII for (Int, Int) -> Void
std::string Shorty() const;
+ const TypeDescriptor& ArgType(size_t index) const;
+
bool operator<(const Prototype& rhs) const {
return std::make_tuple(return_type_, param_types_) <
std::make_tuple(rhs.return_type_, rhs.param_types_);
@@ -124,11 +129,13 @@ class Value {
size_t value() const { return value_; }
+ constexpr Value() : value_{0}, kind_{Kind::kInvalid} {}
+
private:
- enum class Kind { kLocalRegister, kParameter, kImmediate, kString, kLabel, kType };
+ enum class Kind { kInvalid, kLocalRegister, kParameter, kImmediate, kString, kLabel, kType };
- const size_t value_;
- const Kind kind_;
+ size_t value_;
+ Kind kind_;
constexpr Value(size_t value, Kind kind) : value_{value}, kind_{kind} {}
};
@@ -151,6 +158,7 @@ class Instruction {
kInvokeStatic,
kInvokeVirtual,
kMove,
+ kMoveObject,
kNew,
kReturn,
kReturnObject,
@@ -172,7 +180,7 @@ class Instruction {
// A cast instruction. Basically, `(type)val`
static inline Instruction Cast(Value val, Value type) {
- DCHECK(type.is_type());
+ CHECK(type.is_type());
return OpWithArgs(Op::kCheckCast, val, type);
}
@@ -343,21 +351,48 @@ class MethodBuilder {
buffer_.push_back(b);
}
+ inline void Encode32x(art::Instruction::Code opcode, uint16_t a, uint16_t b) {
+ buffer_.push_back(opcode);
+ buffer_.push_back(a);
+ buffer_.push_back(b);
+ }
+
inline void Encode35c(art::Instruction::Code opcode, size_t a, uint16_t b, uint8_t c, uint8_t d,
uint8_t e, uint8_t f, uint8_t g) {
// a|g|op|bbbb|f|e|d|c
CHECK_LE(a, 5);
- CHECK_LT(c, 16);
- CHECK_LT(d, 16);
- CHECK_LT(e, 16);
- CHECK_LT(f, 16);
- CHECK_LT(g, 16);
+ CHECK(IsShortRegister(c));
+ CHECK(IsShortRegister(d));
+ CHECK(IsShortRegister(e));
+ CHECK(IsShortRegister(f));
+ CHECK(IsShortRegister(g));
buffer_.push_back((a << 12) | (g << 8) | opcode);
buffer_.push_back(b);
buffer_.push_back((f << 12) | (e << 8) | (d << 4) | c);
}
+ inline void Encode3rc(art::Instruction::Code opcode, size_t a, uint16_t b, uint16_t c) {
+ CHECK_LE(a, 255);
+ buffer_.push_back((a << 8) | opcode);
+ buffer_.push_back(b);
+ buffer_.push_back(c);
+ }
+
+ static constexpr bool IsShortRegister(size_t register_value) { return register_value < 16; }
+
+ // Returns an array of num_regs scratch registers. These are guaranteed to be
+ // contiguous, so they are suitable for the invoke-*/range instructions.
+ template <int num_regs>
+ std::array<Value, num_regs> GetScratchRegisters() const {
+ static_assert(num_regs <= kMaxScratchRegisters);
+ std::array<Value, num_regs> regs;
+ for (size_t i = 0; i < num_regs; ++i) {
+ regs[i] = std::move(Value::Local(num_registers_ + i));
+ }
+ return regs;
+ }
+
// Converts a register or parameter to its DEX register number.
size_t RegisterValue(const Value& value) const;
@@ -379,6 +414,10 @@ class MethodBuilder {
// A buffer to hold instructions that have been encoded.
std::vector<::dex::u2> buffer_;
+ // We create some scratch registers for when we have to shuffle registers
+ // around to make legal DEX code.
+ static constexpr size_t kMaxScratchRegisters = 5;
+
// How many registers we've allocated
size_t num_registers_{0};
@@ -447,6 +486,8 @@ class DexBuilder {
const MethodDeclData& GetOrDeclareMethod(TypeDescriptor type, const std::string& name,
Prototype prototype);
+ std::optional<const Prototype> GetPrototypeByMethodId(size_t method_id) const;
+
private:
// Looks up the ir::Proto* corresponding to this given prototype, or creates one if it does not
// exist.
diff --git a/startop/view_compiler/dex_builder_test.cc b/startop/view_compiler/dex_builder_test.cc
index 61c86b4091b3..90c256f271cf 100644
--- a/startop/view_compiler/dex_builder_test.cc
+++ b/startop/view_compiler/dex_builder_test.cc
@@ -140,3 +140,41 @@ TEST(DexBuilderTest, VerifyDexCallStringLength) {
EXPECT_TRUE(EncodeAndVerify(&dex_file));
}
+
+// Write out and verify a DEX file that corresponds to:
+//
+// package dextest;
+// public class DexTest {
+// public static int foo(String s) { return s.length(); }
+// }
+TEST(DexBuilderTest, VerifyDexCallManyRegisters) {
+ DexBuilder dex_file;
+
+ auto cbuilder{dex_file.MakeClass("dextest.DexTest")};
+
+ MethodBuilder method{cbuilder.CreateMethod(
+ "foo", Prototype{TypeDescriptor::Int()})};
+
+ Value result = method.MakeRegister();
+
+ // Make a bunch of registers
+ for (size_t i = 0; i < 25; ++i) {
+ method.MakeRegister();
+ }
+
+ // Now load a string literal into a register
+ Value string_val = method.MakeRegister();
+ method.BuildConstString(string_val, "foo");
+
+ MethodDeclData string_length =
+ dex_file.GetOrDeclareMethod(TypeDescriptor::FromClassname("java.lang.String"),
+ "length",
+ Prototype{TypeDescriptor::Int()});
+
+ method.AddInstruction(Instruction::InvokeVirtual(string_length.id, result, string_val));
+ method.BuildReturn(result);
+
+ method.Encode();
+
+ EXPECT_TRUE(EncodeAndVerify(&dex_file));
+}
diff --git a/startop/view_compiler/dex_builder_test/Android.bp b/startop/view_compiler/dex_builder_test/Android.bp
index d4f38ed148c9..ac60e966fe43 100644
--- a/startop/view_compiler/dex_builder_test/Android.bp
+++ b/startop/view_compiler/dex_builder_test/Android.bp
@@ -15,7 +15,7 @@
//
genrule {
- name: "generate_compiled_layout",
+ name: "generate_compiled_layout1",
tools: [":viewcompiler"],
cmd: "$(location :viewcompiler) $(in) --dex --out $(out) --package android.startop.test",
srcs: ["res/layout/layout1.xml"],
@@ -24,6 +24,16 @@ genrule {
],
}
+genrule {
+ name: "generate_compiled_layout2",
+ tools: [":viewcompiler"],
+ cmd: "$(location :viewcompiler) $(in) --dex --out $(out) --package android.startop.test",
+ srcs: ["res/layout/layout2.xml"],
+ out: [
+ "layout2.dex",
+ ],
+}
+
android_test {
name: "dex-builder-test",
srcs: [
@@ -31,7 +41,7 @@ android_test {
"src/android/startop/test/LayoutCompilerTest.java",
],
sdk_version: "current",
- data: [":generate_dex_testcases", ":generate_compiled_layout"],
+ data: [":generate_dex_testcases", ":generate_compiled_layout1", ":generate_compiled_layout2"],
static_libs: [
"android-support-test",
"guava",
diff --git a/startop/view_compiler/dex_builder_test/AndroidTest.xml b/startop/view_compiler/dex_builder_test/AndroidTest.xml
index 68d8fdc444d8..92e2a718bcce 100644
--- a/startop/view_compiler/dex_builder_test/AndroidTest.xml
+++ b/startop/view_compiler/dex_builder_test/AndroidTest.xml
@@ -26,6 +26,7 @@
<option name="push" value="trivial.dex->/data/local/tmp/dex-builder-test/trivial.dex" />
<option name="push" value="simple.dex->/data/local/tmp/dex-builder-test/simple.dex" />
<option name="push" value="layout1.dex->/data/local/tmp/dex-builder-test/layout1.dex" />
+ <option name="push" value="layout2.dex->/data/local/tmp/dex-builder-test/layout2.dex" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
diff --git a/startop/view_compiler/dex_builder_test/res/layout/layout2.xml b/startop/view_compiler/dex_builder_test/res/layout/layout2.xml
new file mode 100644
index 000000000000..b092e1c20311
--- /dev/null
+++ b/startop/view_compiler/dex_builder_test/res/layout/layout2.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TableRow
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Button" />
+
+ <TableRow
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Button" />
+ <TableRow
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Button" />
+
+ <TableRow
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Button" />
+ </TableRow>
+
+ </TableRow>
+ </TableRow>
+ </TableRow>
+</LinearLayout>
diff --git a/startop/view_compiler/dex_builder_test/src/android/startop/test/LayoutCompilerTest.java b/startop/view_compiler/dex_builder_test/src/android/startop/test/LayoutCompilerTest.java
index ce3ce8328559..a3b1b6c11ac3 100644
--- a/startop/view_compiler/dex_builder_test/src/android/startop/test/LayoutCompilerTest.java
+++ b/startop/view_compiler/dex_builder_test/src/android/startop/test/LayoutCompilerTest.java
@@ -36,11 +36,20 @@ public class LayoutCompilerTest {
}
@Test
- public void loadAndInflaterLayout1() throws Exception {
+ public void loadAndInflateLayout1() throws Exception {
ClassLoader dex_file = loadDexFile("layout1.dex");
Class compiled_view = dex_file.loadClass("android.startop.test.CompiledView");
Method layout1 = compiled_view.getMethod("layout1", Context.class, int.class);
Context context = InstrumentationRegistry.getTargetContext();
layout1.invoke(null, context, R.layout.layout1);
}
+
+ @Test
+ public void loadAndInflateLayout2() throws Exception {
+ ClassLoader dex_file = loadDexFile("layout2.dex");
+ Class compiled_view = dex_file.loadClass("android.startop.test.CompiledView");
+ Method layout1 = compiled_view.getMethod("layout2", Context.class, int.class);
+ Context context = InstrumentationRegistry.getTargetContext();
+ layout1.invoke(null, context, R.layout.layout1);
+ }
}
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 2820836282a1..dcaa49996d0b 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -239,6 +239,30 @@ public final class Call {
"android.telecom.event.HANDOVER_FAILED";
public static class Details {
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ prefix = { "DIRECTION_" },
+ value = {DIRECTION_UNKNOWN, DIRECTION_INCOMING, DIRECTION_OUTGOING})
+ public @interface CallDirection {}
+
+ /**
+ * Indicates that the call is neither and incoming nor an outgoing call. This can be the
+ * case for calls reported directly by a {@link ConnectionService} in special cases such as
+ * call handovers.
+ */
+ public static final int DIRECTION_UNKNOWN = -1;
+
+ /**
+ * Indicates that the call is an incoming call.
+ */
+ public static final int DIRECTION_INCOMING = 0;
+
+ /**
+ * Indicates that the call is an outgoing call.
+ */
+ public static final int DIRECTION_OUTGOING = 1;
+
/** Call can currently be put on hold or unheld. */
public static final int CAPABILITY_HOLD = 0x00000001;
@@ -519,6 +543,7 @@ public final class Call {
private final Bundle mIntentExtras;
private final long mCreationTimeMillis;
private final CallIdentification mCallIdentification;
+ private final @CallDirection int mCallDirection;
/**
* Whether the supplied capabilities supports the specified capability.
@@ -838,6 +863,14 @@ public final class Call {
return mCallIdentification;
}
+ /**
+ * Indicates whether the call is an incoming or outgoing call.
+ * @return The call's direction.
+ */
+ public @CallDirection int getCallDirection() {
+ return mCallDirection;
+ }
+
@Override
public boolean equals(Object o) {
if (o instanceof Details) {
@@ -859,7 +892,8 @@ public final class Call {
areBundlesEqual(mExtras, d.mExtras) &&
areBundlesEqual(mIntentExtras, d.mIntentExtras) &&
Objects.equals(mCreationTimeMillis, d.mCreationTimeMillis) &&
- Objects.equals(mCallIdentification, d.mCallIdentification);
+ Objects.equals(mCallIdentification, d.mCallIdentification) &&
+ Objects.equals(mCallDirection, d.mCallDirection);
}
return false;
}
@@ -881,7 +915,8 @@ public final class Call {
mExtras,
mIntentExtras,
mCreationTimeMillis,
- mCallIdentification);
+ mCallIdentification,
+ mCallDirection);
}
/** {@hide} */
@@ -902,7 +937,8 @@ public final class Call {
Bundle extras,
Bundle intentExtras,
long creationTimeMillis,
- CallIdentification callIdentification) {
+ CallIdentification callIdentification,
+ int callDirection) {
mTelecomCallId = telecomCallId;
mHandle = handle;
mHandlePresentation = handlePresentation;
@@ -920,6 +956,7 @@ public final class Call {
mIntentExtras = intentExtras;
mCreationTimeMillis = creationTimeMillis;
mCallIdentification = callIdentification;
+ mCallDirection = callDirection;
}
/** {@hide} */
@@ -941,7 +978,8 @@ public final class Call {
parcelableCall.getExtras(),
parcelableCall.getIntentExtras(),
parcelableCall.getCreationTimeMillis(),
- parcelableCall.getCallIdentification());
+ parcelableCall.getCallIdentification(),
+ parcelableCall.getCallDirection());
}
@Override
diff --git a/telecomm/java/android/telecom/CallIdentification.java b/telecomm/java/android/telecom/CallIdentification.java
index 97af06c1d64c..87834fd5109d 100644
--- a/telecomm/java/android/telecom/CallIdentification.java
+++ b/telecomm/java/android/telecom/CallIdentification.java
@@ -250,8 +250,8 @@ public final class CallIdentification implements Parcelable {
mDetails = details;
mPhoto = photo;
mNuisanceConfidence = nuisanceConfidence;
- mCallScreeningAppName = callScreeningPackageName;
- mCallScreeningPackageName = callScreeningAppName;
+ mCallScreeningAppName = callScreeningAppName;
+ mCallScreeningPackageName = callScreeningPackageName;
}
private String mName;
@@ -430,4 +430,22 @@ public final class CallIdentification implements Parcelable {
return Objects.hash(mName, mDescription, mDetails, mPhoto, mNuisanceConfidence,
mCallScreeningAppName, mCallScreeningPackageName);
}
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("[CallId mName=");
+ sb.append(Log.pii(mName));
+ sb.append(", mDesc=");
+ sb.append(mDescription);
+ sb.append(", mDet=");
+ sb.append(mDetails);
+ sb.append(", conf=");
+ sb.append(mNuisanceConfidence);
+ sb.append(", appName=");
+ sb.append(mCallScreeningAppName);
+ sb.append(", pkgName=");
+ sb.append(mCallScreeningPackageName);
+ return sb.toString();
+ }
}
diff --git a/telecomm/java/android/telecom/CallScreeningService.java b/telecomm/java/android/telecom/CallScreeningService.java
index be96b3cac6f6..818ebd998f50 100644
--- a/telecomm/java/android/telecom/CallScreeningService.java
+++ b/telecomm/java/android/telecom/CallScreeningService.java
@@ -16,11 +16,13 @@
package android.telecom;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SdkConstant;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
+import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
@@ -31,10 +33,14 @@ import com.android.internal.os.SomeArgs;
import com.android.internal.telecom.ICallScreeningAdapter;
import com.android.internal.telecom.ICallScreeningService;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* This service can be implemented by the default dialer (see
- * {@link TelecomManager#getDefaultDialerPackage()}) to allow or disallow incoming calls before
- * they are shown to a user.
+ * {@link TelecomManager#getDefaultDialerPackage()}) or a third party app to allow or disallow
+ * incoming calls before they are shown to a user. This service can also provide
+ * {@link CallIdentification} information for calls.
* <p>
* Below is an example manifest registration for a {@code CallScreeningService}.
* <pre>
@@ -56,8 +62,158 @@ import com.android.internal.telecom.ICallScreeningService;
* information about a {@link Call.Details call} which will be shown to the user in the
* Dialer app.</li>
* </ol>
+ * <p>
+ * <h2>Becoming the {@link CallScreeningService}</h2>
+ * Telecom will bind to a single app chosen by the user which implements the
+ * {@link CallScreeningService} API when there are new incoming and outgoing calls.
+ * <p>
+ * The code snippet below illustrates how your app can request that it fills the call screening
+ * role.
+ * <pre>
+ * {@code
+ * private static final int REQUEST_ID = 1;
+ *
+ * public void requestRole() {
+ * RoleManager roleManager = (RoleManager) getSystemService(ROLE_SERVICE);
+ * Intent intent = roleManager.createRequestRoleIntent("android.app.role.CALL_SCREENING_APP");
+ * startActivityForResult(intent, REQUEST_ID);
+ * }
+ *
+ * &#64;Override
+ * public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ * if (requestCode == REQUEST_ID) {
+ * if (resultCode == android.app.Activity.RESULT_OK) {
+ * // Your app is now the call screening app
+ * } else {
+ * // Your app is not the call screening app
+ * }
+ * }
+ * }
+ * </pre>
*/
public abstract class CallScreeningService extends Service {
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ prefix = { "CALL_DURATION_" },
+ value = {CALL_DURATION_VERY_SHORT, CALL_DURATION_SHORT, CALL_DURATION_MEDIUM,
+ CALL_DURATION_LONG})
+ public @interface CallDuration {}
+
+ /**
+ * Call duration reported with {@link #EXTRA_CALL_DURATION} to indicate to the
+ * {@link CallScreeningService} the duration of a call for which the user reported the nuisance
+ * status (see {@link TelecomManager#reportNuisanceCallStatus(Uri, boolean)}). The
+ * {@link CallScreeningService} can use this as a signal for training nuisance detection
+ * algorithms. The call duration is reported in coarse grained buckets to minimize exposure of
+ * identifying call log information to the {@link CallScreeningService}.
+ * <p>
+ * Indicates the call was < 3 seconds in duration.
+ */
+ public static final int CALL_DURATION_VERY_SHORT = 1;
+
+ /**
+ * Call duration reported with {@link #EXTRA_CALL_DURATION} to indicate to the
+ * {@link CallScreeningService} the duration of a call for which the user reported the nuisance
+ * status (see {@link TelecomManager#reportNuisanceCallStatus(Uri, boolean)}). The
+ * {@link CallScreeningService} can use this as a signal for training nuisance detection
+ * algorithms. The call duration is reported in coarse grained buckets to minimize exposure of
+ * identifying call log information to the {@link CallScreeningService}.
+ * <p>
+ * Indicates the call was greater than 3 seconds, but less than 60 seconds in duration.
+ */
+ public static final int CALL_DURATION_SHORT = 2;
+
+ /**
+ * Call duration reported with {@link #EXTRA_CALL_DURATION} to indicate to the
+ * {@link CallScreeningService} the duration of a call for which the user reported the nuisance
+ * status (see {@link TelecomManager#reportNuisanceCallStatus(Uri, boolean)}). The
+ * {@link CallScreeningService} can use this as a signal for training nuisance detection
+ * algorithms. The call duration is reported in coarse grained buckets to minimize exposure of
+ * identifying call log information to the {@link CallScreeningService}.
+ * <p>
+ * Indicates the call was greater than 60 seconds, but less than 120 seconds in duration.
+ */
+ public static final int CALL_DURATION_MEDIUM = 3;
+
+ /**
+ * Call duration reported with {@link #EXTRA_CALL_DURATION} to indicate to the
+ * {@link CallScreeningService} the duration of a call for which the user reported the nuisance
+ * status (see {@link TelecomManager#reportNuisanceCallStatus(Uri, boolean)}). The
+ * {@link CallScreeningService} can use this as a signal for training nuisance detection
+ * algorithms. The call duration is reported in coarse grained buckets to minimize exposure of
+ * identifying call log information to the {@link CallScreeningService}.
+ * <p>
+ * Indicates the call was greater than 120 seconds.
+ */
+ public static final int CALL_DURATION_LONG = 4;
+
+ /**
+ * Telecom sends this intent to the {@link CallScreeningService} which the user has chosen to
+ * fill the call screening role when the user indicates through the default dialer whether a
+ * call is a nuisance call or not (see
+ * {@link TelecomManager#reportNuisanceCallStatus(Uri, boolean)}).
+ * <p>
+ * The following extra values are provided for the call:
+ * <ol>
+ * <li>{@link #EXTRA_CALL_HANDLE} - the handle of the call.</li>
+ * <li>{@link #EXTRA_IS_NUISANCE} - {@code true} if the user reported the call as a nuisance
+ * call, {@code false} otherwise.</li>
+ * <li>{@link #EXTRA_CALL_TYPE} - reports the type of call (incoming, rejected, missed,
+ * blocked).</li>
+ * <li>{@link #EXTRA_CALL_DURATION} - the duration of the call (see
+ * {@link #EXTRA_CALL_DURATION} for valid values).</li>
+ * </ol>
+ * <p>
+ * {@link CallScreeningService} implementations which want to track whether the user reports
+ * calls are nuisance calls should use declare a broadcast receiver in their manifest for this
+ * intent.
+ * <p>
+ * Note: Only {@link CallScreeningService} implementations which have provided
+ * {@link CallIdentification} information for calls at some point will receive this intent.
+ */
+ public static final String ACTION_NUISANCE_CALL_STATUS_CHANGED =
+ "android.telecom.action.NUISANCE_CALL_STATUS_CHANGED";
+
+ /**
+ * Extra used to provide the handle of the call for
+ * {@link #ACTION_NUISANCE_CALL_STATUS_CHANGED}. The call handle is reported as a
+ * {@link Uri}.
+ */
+ public static final String EXTRA_CALL_HANDLE = "android.telecom.extra.CALL_HANDLE";
+
+ /**
+ * Boolean extra used to indicate whether the user reported a call as a nuisance call.
+ * When {@code true}, the user reported that a call was a nuisance call, {@code false}
+ * otherwise. Sent with {@link #ACTION_NUISANCE_CALL_STATUS_CHANGED}.
+ */
+ public static final String EXTRA_IS_NUISANCE = "android.telecom.extra.IS_NUISANCE";
+
+ /**
+ * Integer extra used with {@link #ACTION_NUISANCE_CALL_STATUS_CHANGED} to report the type of
+ * call. Valid values are:
+ * <UL>
+ * <li>{@link android.provider.CallLog.Calls#MISSED_TYPE}</li>
+ * <li>{@link android.provider.CallLog.Calls#INCOMING_TYPE}</li>
+ * <li>{@link android.provider.CallLog.Calls#BLOCKED_TYPE}</li>
+ * <li>{@link android.provider.CallLog.Calls#REJECTED_TYPE}</li>
+ * </UL>
+ */
+ public static final String EXTRA_CALL_TYPE = "android.telecom.extra.CALL_TYPE";
+
+ /**
+ * Integer extra used to with {@link #ACTION_NUISANCE_CALL_STATUS_CHANGED} to report how long
+ * the call lasted. Valid values are:
+ * <UL>
+ * <LI>{@link #CALL_DURATION_VERY_SHORT}</LI>
+ * <LI>{@link #CALL_DURATION_SHORT}</LI>
+ * <LI>{@link #CALL_DURATION_MEDIUM}</LI>
+ * <LI>{@link #CALL_DURATION_LONG}</LI>
+ * </UL>
+ */
+ public static final String EXTRA_CALL_DURATION = "android.telecom.extra.CALL_DURATION";
+
/**
* The {@link Intent} that must be declared as handled by the service.
*/
@@ -222,30 +378,46 @@ public abstract class CallScreeningService extends Service {
}
/**
- * Called when a new incoming call is added.
- * {@link CallScreeningService#respondToCall(Call.Details, CallScreeningService.CallResponse)}
- * should be called to allow or disallow the call.
+ * Called when a new incoming or outgoing call is added which is not in the user's contact list.
+ * <p>
+ * A {@link CallScreeningService} must indicate whether an incoming call is allowed or not by
+ * calling
+ * {@link CallScreeningService#respondToCall(Call.Details, CallScreeningService.CallResponse)}.
+ * Your app can tell if a call is an incoming call by checking to see if
+ * {@link Call.Details#getCallDirection()} is {@link Call.Details#DIRECTION_INCOMING}.
+ * <p>
+ * For incoming or outgoing calls, the {@link CallScreeningService} can call
+ * {@link #provideCallIdentification(Call.Details, CallIdentification)} in order to provide
+ * {@link CallIdentification} for the call.
* <p>
* Note: The {@link Call.Details} instance provided to a call screening service will only have
* the following properties set. The rest of the {@link Call.Details} properties will be set to
* their default value or {@code null}.
* <ul>
- * <li>{@link Call.Details#getState()}</li>
+ * <li>{@link Call.Details#getCallDirection()}</li>
* <li>{@link Call.Details#getConnectTimeMillis()}</li>
* <li>{@link Call.Details#getCreationTimeMillis()}</li>
* <li>{@link Call.Details#getHandle()}</li>
* <li>{@link Call.Details#getHandlePresentation()}</li>
* </ul>
+ * <p>
+ * Only calls where the {@link Call.Details#getHandle() handle} {@link Uri#getScheme() scheme}
+ * is {@link PhoneAccount#SCHEME_TEL} are passed for call
+ * screening. Further, only calls which are not in the user's contacts are passed for
+ * screening. For outgoing calls, no post-dial digits are passed.
*
- * @param callDetails Information about a new incoming call, see {@link Call.Details}.
+ * @param callDetails Information about a new call, see {@link Call.Details}.
*/
public abstract void onScreenCall(@NonNull Call.Details callDetails);
/**
- * Responds to the given call, either allowing it or disallowing it.
+ * Responds to the given incoming call, either allowing it or disallowing it.
* <p>
* The {@link CallScreeningService} calls this method to inform the system whether the call
* should be silently blocked or not.
+ * <p>
+ * Calls to this method are ignored unless the {@link Call.Details#getCallDirection()} is
+ * {@link Call.Details#DIRECTION_INCOMING}.
*
* @param callDetails The call to allow.
* <p>
diff --git a/telecomm/java/android/telecom/ConferenceParticipant.java b/telecomm/java/android/telecom/ConferenceParticipant.java
index 20b04ebed6a4..6317770676cd 100644
--- a/telecomm/java/android/telecom/ConferenceParticipant.java
+++ b/telecomm/java/android/telecom/ConferenceParticipant.java
@@ -19,6 +19,10 @@ package android.telecom;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.TextUtils;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.telephony.PhoneConstants;
/**
* Parcelable representation of a participant's state in a conference call.
@@ -27,6 +31,11 @@ import android.os.Parcelable;
public class ConferenceParticipant implements Parcelable {
/**
+ * RFC5767 states that a SIP URI with an unknown number should use an address of
+ * {@code anonymous@anonymous.invalid}. E.g. the host name is anonymous.invalid.
+ */
+ private static final String ANONYMOUS_INVALID_HOST = "anonymous.invalid";
+ /**
* The conference participant's handle (e.g., phone number).
*/
private final Uri mHandle;
@@ -50,6 +59,16 @@ public class ConferenceParticipant implements Parcelable {
private final int mState;
/**
+ * The connect time of the participant.
+ */
+ private long mConnectTime;
+
+ /**
+ * The connect elapsed time of the participant.
+ */
+ private long mConnectElapsedTime;
+
+ /**
* Creates an instance of {@code ConferenceParticipant}.
*
* @param handle The conference participant's handle (e.g., phone number).
@@ -92,6 +111,54 @@ public class ConferenceParticipant implements Parcelable {
}
/**
+ * Determines the number presentation for a conference participant. Per RFC5767, if the host
+ * name contains {@code anonymous.invalid} we can assume that there is no valid caller ID
+ * information for the caller, otherwise we'll assume that the URI can be shown.
+ *
+ * @return The number presentation.
+ */
+ @VisibleForTesting
+ public int getParticipantPresentation() {
+ Uri address = getHandle();
+ if (address == null) {
+ return PhoneConstants.PRESENTATION_RESTRICTED;
+ }
+
+ String number = address.getSchemeSpecificPart();
+ // If no number, bail early and set restricted presentation.
+ if (TextUtils.isEmpty(number)) {
+ return PhoneConstants.PRESENTATION_RESTRICTED;
+ }
+ // Per RFC3261, the host name portion can also potentially include extra information:
+ // E.g. sip:anonymous1@anonymous.invalid;legid=1
+ // In this case, hostName will be anonymous.invalid and there is an extra parameter for
+ // legid=1.
+ // Parameters are optional, and the address (e.g. test@test.com) will always be the first
+ // part, with any parameters coming afterwards.
+ String [] hostParts = number.split("[;]");
+ String addressPart = hostParts[0];
+
+ // Get the number portion from the address part.
+ // This will typically be formatted similar to: 6505551212@test.com
+ String [] numberParts = addressPart.split("[@]");
+
+ // If we can't parse the host name out of the URI, then there is probably other data
+ // present, and is likely a valid SIP URI.
+ if (numberParts.length != 2) {
+ return PhoneConstants.PRESENTATION_ALLOWED;
+ }
+ String hostName = numberParts[1];
+
+ // If the hostname portion of the SIP URI is the invalid host string, presentation is
+ // restricted.
+ if (hostName.equals(ANONYMOUS_INVALID_HOST)) {
+ return PhoneConstants.PRESENTATION_RESTRICTED;
+ }
+
+ return PhoneConstants.PRESENTATION_ALLOWED;
+ }
+
+ /**
* Writes the {@code ConferenceParticipant} to a parcel.
*
* @param dest The Parcel in which the object should be written.
@@ -121,6 +188,10 @@ public class ConferenceParticipant implements Parcelable {
sb.append(Log.pii(mEndpoint));
sb.append(" State: ");
sb.append(Connection.stateToString(mState));
+ sb.append(" ConnectTime: ");
+ sb.append(getConnectTime());
+ sb.append(" ConnectElapsedTime: ");
+ sb.append(getConnectElapsedTime());
sb.append("]");
return sb.toString();
}
@@ -155,4 +226,26 @@ public class ConferenceParticipant implements Parcelable {
public int getState() {
return mState;
}
+
+ /**
+ * The connect time of the participant to the conference.
+ */
+ public long getConnectTime() {
+ return mConnectTime;
+ }
+
+ public void setConnectTime(long connectTime) {
+ this.mConnectTime = connectTime;
+ }
+
+ /**
+ * The connect elpased time of the participant to the conference.
+ */
+ public long getConnectElapsedTime() {
+ return mConnectElapsedTime;
+ }
+
+ public void setConnectElapsedTime(long connectElapsedTime) {
+ mConnectElapsedTime = connectElapsedTime;
+ }
}
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 05d5a13092f1..bd0d4ae27800 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -16,10 +16,6 @@
package android.telecom;
-import com.android.internal.os.SomeArgs;
-import com.android.internal.telecom.IVideoCallback;
-import com.android.internal.telecom.IVideoProvider;
-
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -43,6 +39,10 @@ import android.telephony.TelephonyManager;
import android.util.ArraySet;
import android.view.Surface;
+import com.android.internal.os.SomeArgs;
+import com.android.internal.telecom.IVideoCallback;
+import com.android.internal.telecom.IVideoProvider;
+
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java
index 1de67a5883e3..5a97c948f31f 100644
--- a/telecomm/java/android/telecom/DisconnectCause.java
+++ b/telecomm/java/android/telecom/DisconnectCause.java
@@ -16,9 +16,9 @@
package android.telecom;
+import android.media.ToneGenerator;
import android.os.Parcel;
import android.os.Parcelable;
-import android.media.ToneGenerator;
import android.text.TextUtils;
import java.util.Objects;
@@ -91,6 +91,12 @@ public final class DisconnectCause implements Parcelable {
*/
public static final String REASON_IMS_ACCESS_BLOCKED = "REASON_IMS_ACCESS_BLOCKED";
+ /**
+ * Reason code, which indicates that the conference call is simulating single party conference.
+ * @hide
+ */
+ public static final String REASON_EMULATING_SINGLE_CALL = "EMULATING_SINGLE_CALL";
+
private int mDisconnectCode;
private CharSequence mDisconnectLabel;
private CharSequence mDisconnectDescription;
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index 1aeeca73c0b9..cea2fbfa588c 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -40,11 +40,30 @@ import java.util.Collections;
import java.util.List;
/**
- * This service is implemented by any app that wishes to provide the user-interface for managing
- * phone calls. Telecom binds to this service while there exists a live (active or incoming) call,
- * and uses it to notify the in-call app of any live and recently disconnected calls. An app must
- * first be set as the default phone app (See {@link TelecomManager#getDefaultDialerPackage()})
- * before the telecom service will bind to its {@code InCallService} implementation.
+ * This service is implemented by an app that wishes to provide functionality for managing
+ * phone calls.
+ * <p>
+ * There are three types of apps which Telecom can bind to when there exists a live (active or
+ * incoming) call:
+ * <ol>
+ * <li>Default Dialer/Phone app - the default dialer/phone app is one which provides the
+ * in-call user interface while the device is in a call. A device is bundled with a system
+ * provided default dialer/phone app. The user may choose a single app to take over this role
+ * from the system app.</li>
+ * <li>Default Car-mode Dialer/Phone app - the default car-mode dialer/phone app is one which
+ * provides the in-call user interface while the device is in a call and the device is in car
+ * mode. The user may choose a single app to fill this role.</li>
+ * <li>Call Companion app - a call companion app is one which provides no user interface itself,
+ * but exposes call information to another display surface, such as a wearable device. The
+ * user may choose multiple apps to fill this role.</li>
+ * </ol>
+ * <p>
+ * Apps which wish to fulfill one of the above roles use the {@code android.app.role.RoleManager}
+ * to request that they fill the desired role.
+ *
+ * <h2>Becoming the Default Phone App</h2>
+ * An app filling the role of the default phone app provides a user interface while the device is in
+ * a call, and the device is not in car mode.
* <p>
* Below is an example manifest registration for an {@code InCallService}. The meta-data
* {@link TelecomManager#METADATA_IN_CALL_SERVICE_UI} indicates that this particular
@@ -82,12 +101,34 @@ import java.util.List;
* }
* </pre>
* <p>
- * When a user installs your application and runs it for the first time, you should prompt the user
- * to see if they would like your application to be the new default phone app. See the
- * {@link TelecomManager#ACTION_CHANGE_DEFAULT_DIALER} intent documentation for more information on
- * how to do this.
+ * When a user installs your application and runs it for the first time, you should use the
+ * {@code android.app.role.RoleManager} to prompt the user to see if they would like your app to
+ * be the new default phone app.
+ * <p id="requestRole">
+ * The code below shows how your app can request to become the default phone/dialer app:
+ * <pre>
+ * {@code
+ * private static final int REQUEST_ID = 1;
+ *
+ * public void requestRole() {
+ * RoleManager roleManager = (RoleManager) getSystemService(ROLE_SERVICE);
+ * Intent intent = roleManager.createRequestRoleIntent(RoleManager.ROLE_DIALER);
+ * startActivityForResult(intent, REQUEST_ID);
+ * }
+ *
+ * &#64;Override
+ * public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ * if (requestCode == REQUEST_ID) {
+ * if (resultCode == android.app.Activity.RESULT_OK) {
+ * // Your app is now the default dialer app
+ * } else {
+ * // Your app is not the default dialer app
+ * }
+ * }
+ * }
+ * </pre>
* <p id="incomingCallNotification">
- * <h2>Showing the Incoming Call Notification</h2>
+ * <h3>Showing the Incoming Call Notification</h3>
* When your app receives a new incoming call via {@link InCallService#onCallAdded(Call)}, it is
* responsible for displaying an incoming call UI for the incoming call. It should do this using
* {@link android.app.NotificationManager} APIs to post a new incoming call notification.
@@ -121,7 +162,7 @@ import java.util.List;
* heads-up notification if the user is actively using the phone. When the user is not using the
* phone, your full-screen incoming call UI is used instead.
* For example:
- * <pre><code>
+ * <pre><code>{@code
* // Create an intent which triggers your fullscreen incoming call user interface.
* Intent intent = new Intent(Intent.ACTION_MAIN, null);
* intent.setFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION | Intent.FLAG_ACTIVITY_NEW_TASK);
@@ -151,7 +192,49 @@ import java.util.List;
* NotificationManager notificationManager = mContext.getSystemService(
* NotificationManager.class);
* notificationManager.notify(YOUR_CHANNEL_ID, YOUR_TAG, YOUR_ID, builder.build());
- * </code></pre>
+ * }</pre>
+ * <p>
+ * <h2>Becoming the Default Car-mode Phone App</h2>
+ * An app filling the role of the default car-mode dialer/phone app provides a user interface while
+ * the device is in a call, and in car mode. See
+ * {@link android.app.UiModeManager#ACTION_ENTER_CAR_MODE} for more information about car mode.
+ * When the device is in car mode, Telecom binds to the default car-mode dialer/phone app instead
+ * of the usual dialer/phone app.
+ * <p>
+ * Similar to the requirements for becoming the default dialer/phone app, your app must declare a
+ * manifest entry for its {@link InCallService} implementation. Your manifest entry should ensure
+ * the following conditions are met:
+ * <ul>
+ * <li>Do NOT declare the {@link TelecomManager#METADATA_IN_CALL_SERVICE_UI} metadata.</li>
+ * <li>Set the {@link TelecomManager#METADATA_IN_CALL_SERVICE_CAR_MODE_UI} metadata to
+ * {@code true}<li>
+ * <li>Your app must request the permission
+ * {@link android.Manifest.permission.CALL_COMPANION_APP}.</li>
+ * </ul>
+ * <p>
+ * Your app should request to fill the role {@code android.app.role.CAR_MODE_DIALER_APP} in order to
+ * become the default (see <a href="#requestRole">above</a> for how to request your app fills this
+ * role).
+ *
+ * <h2>Becoming a Call Companion App</h2>
+ * An app which fills the companion app role does not directly provide a user interface while the
+ * device is in a call. Instead, it is typically used to relay information about calls to another
+ * display surface, such as a wearable device.
+ * <p>
+ * Similar to the requirements for becoming the default dialer/phone app, your app must declare a
+ * manifest entry for its {@link InCallService} implementation. Your manifest entry should
+ * ensure the following conditions are met:
+ * <ul>
+ * <li>Do NOT declare the {@link TelecomManager#METADATA_IN_CALL_SERVICE_UI} metadata.</li>
+ * <li>Do NOT declare the {@link TelecomManager#METADATA_IN_CALL_SERVICE_CAR_MODE_UI}
+ * metadata.</li>
+ * <li>Your app must request the permission
+ * {@link android.Manifest.permission.CALL_COMPANION_APP}.</li>
+ * </ul>
+ * <p>
+ * Your app should request to fill the role {@code android.app.role.CALL_COMPANION_APP} in order to
+ * become a call companion app (see <a href="#requestRole">above</a> for how to request your app
+ * fills this role).
*/
public abstract class InCallService extends Service {
diff --git a/telecomm/java/android/telecom/ParcelableCall.java b/telecomm/java/android/telecom/ParcelableCall.java
index 911786e455c2..f7dec83c3ace 100644
--- a/telecomm/java/android/telecom/ParcelableCall.java
+++ b/telecomm/java/android/telecom/ParcelableCall.java
@@ -24,6 +24,7 @@ import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteException;
+import android.telecom.Call.Details.CallDirection;
import java.util.ArrayList;
import java.util.Collections;
@@ -64,6 +65,7 @@ public final class ParcelableCall implements Parcelable {
private final Bundle mExtras;
private final long mCreationTimeMillis;
private final CallIdentification mCallIdentification;
+ private final int mCallDirection;
public ParcelableCall(
String id,
@@ -92,7 +94,8 @@ public final class ParcelableCall implements Parcelable {
Bundle intentExtras,
Bundle extras,
long creationTimeMillis,
- CallIdentification callIdentification) {
+ CallIdentification callIdentification,
+ int callDirection) {
mId = id;
mState = state;
mDisconnectCause = disconnectCause;
@@ -120,6 +123,7 @@ public final class ParcelableCall implements Parcelable {
mExtras = extras;
mCreationTimeMillis = creationTimeMillis;
mCallIdentification = callIdentification;
+ mCallDirection = callDirection;
}
/** The unique ID of the call. */
@@ -318,6 +322,13 @@ public final class ParcelableCall implements Parcelable {
return mCallIdentification;
}
+ /**
+ * Indicates whether the call is an incoming or outgoing call.
+ */
+ public @CallDirection int getCallDirection() {
+ return mCallDirection;
+ }
+
/** Responsible for creating ParcelableCall objects for deserialized Parcels. */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public static final Parcelable.Creator<ParcelableCall> CREATOR =
@@ -356,6 +367,7 @@ public final class ParcelableCall implements Parcelable {
ParcelableRttCall rttCall = source.readParcelable(classLoader);
long creationTimeMillis = source.readLong();
CallIdentification callIdentification = source.readParcelable(classLoader);
+ int callDirection = source.readInt();
return new ParcelableCall(
id,
state,
@@ -383,7 +395,8 @@ public final class ParcelableCall implements Parcelable {
intentExtras,
extras,
creationTimeMillis,
- callIdentification);
+ callIdentification,
+ callDirection);
}
@Override
@@ -429,6 +442,7 @@ public final class ParcelableCall implements Parcelable {
destination.writeParcelable(mRttCall, 0);
destination.writeLong(mCreationTimeMillis);
destination.writeParcelable(mCallIdentification, 0);
+ destination.writeInt(mCallDirection);
}
@Override
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index c3e80b480099..c60eb56005eb 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -289,6 +289,19 @@ public class TelecomManager {
"android.telecom.extra.OUTGOING_CALL_EXTRAS";
/**
+ * An optional boolean extra on {@link android.content.Intent#ACTION_CALL_EMERGENCY} to tell
+ * whether the user's dial intent is emergency; this is required to specify when the dialed
+ * number is ambiguous, identified as both emergency number and any other non-emergency number;
+ * e.g. in some situation, 611 could be both an emergency number in a country and a
+ * non-emergency number of a carrier's customer service hotline.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_IS_USER_INTENT_EMERGENCY_CALL =
+ "android.telecom.extra.IS_USER_INTENT_EMERGENCY_CALL";
+
+ /**
* @hide
*/
public static final String EXTRA_UNKNOWN_CALL_HANDLE =
@@ -1397,8 +1410,14 @@ public class TelecomManager {
*
* @return {@code true} if there is a call which will be rejected or terminated, {@code false}
* otherwise.
+ * @deprecated Companion apps for wearable devices should use the {@link InCallService} API
+ * instead. Apps performing call screening should use the {@link CallScreeningService} API
+ * instead.
*/
+
+
@RequiresPermission(Manifest.permission.ANSWER_PHONE_CALLS)
+ @Deprecated
public boolean endCall() {
try {
if (isServiceConnected()) {
@@ -1419,11 +1438,15 @@ public class TelecomManager {
*
* Requires permission: {@link android.Manifest.permission#MODIFY_PHONE_STATE} or
* {@link android.Manifest.permission#ANSWER_PHONE_CALLS}
+ *
+ * @deprecated Companion apps for wearable devices should use the {@link InCallService} API
+ * instead.
*/
//TODO: L-release - need to convert all invocation of ITelecmmService#answerRingingCall to use
// this method (clockwork & gearhead).
@RequiresPermission(anyOf =
{Manifest.permission.ANSWER_PHONE_CALLS, Manifest.permission.MODIFY_PHONE_STATE})
+ @Deprecated
public void acceptRingingCall() {
try {
if (isServiceConnected()) {
@@ -1442,9 +1465,12 @@ public class TelecomManager {
* {@link android.Manifest.permission#ANSWER_PHONE_CALLS}
*
* @param videoState The desired video state to answer the call with.
+ * @deprecated Companion apps for wearable devices should use the {@link InCallService} API
+ * instead.
*/
@RequiresPermission(anyOf =
{Manifest.permission.ANSWER_PHONE_CALLS, Manifest.permission.MODIFY_PHONE_STATE})
+ @Deprecated
public void acceptRingingCall(int videoState) {
try {
if (isServiceConnected()) {
@@ -1963,6 +1989,33 @@ public class TelecomManager {
}
/**
+ * Called by the default dialer to report to Telecom when the user has marked a previous
+ * incoming call as a nuisance call or not.
+ * <p>
+ * Where the user has chosen a {@link CallScreeningService} to fill the call screening role,
+ * Telecom will notify that {@link CallScreeningService} of the user's report.
+ * <p>
+ * Requires that the caller is the default dialer app.
+ *
+ * @param handle The phone number of an incoming call which the user is reporting as either a
+ * nuisance of non-nuisance call.
+ * @param isNuisanceCall {@code true} if the user is reporting the call as a nuisance call,
+ * {@code false} if the user is reporting the call as a non-nuisance call.
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ public void reportNuisanceCallStatus(@NonNull Uri handle, boolean isNuisanceCall) {
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ service.reportNuisanceCallStatus(handle, isNuisanceCall,
+ mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error calling ITelecomService#showCallScreen", e);
+ }
+ }
+ }
+
+ /**
* Handles {@link Intent#ACTION_CALL} intents trampolined from UserCallActivity.
* @param intent The {@link Intent#ACTION_CALL} intent to handle.
* @hide
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index e1d5c17d5e3a..5030f90afd3e 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -285,6 +285,8 @@ interface ITelecomService {
*/
boolean isInEmergencyCall();
+ oneway void reportNuisanceCallStatus(in Uri address, boolean isNuisance, String callingPackage);
+
/**
* @see TelecomServiceImpl#handleCallIntent
*/
@@ -299,4 +301,5 @@ interface ITelecomService {
void addOrRemoveTestCallCompanionApp(String packageName, boolean isAdded);
void setTestAutoModeApp(String packageName);
+
}
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index fb8f3e78ff84..50b8f79892fd 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -2054,6 +2054,648 @@ public final class Telephony {
}
/**
+ * Columns for the "rcs_*" tables used by {@link android.telephony.ims.RcsMessageStore} classes.
+ *
+ * @hide - not meant for public use
+ */
+ public interface RcsColumns {
+ // TODO(sahinc): Turn this to true once the schema finalizes, so that people can update
+ // their messaging databases. NOTE: move the switch/case update in MmsSmsDatabaseHelper to
+ // the latest version of the database before turning this flag to true.
+ boolean IS_RCS_TABLE_SCHEMA_CODE_COMPLETE = false;
+
+ /**
+ * The authority for the content provider
+ */
+ String AUTHORITY = "rcs";
+
+ /**
+ * The URI to start building upon to use {@link com.android.providers.telephony.RcsProvider}
+ */
+ Uri CONTENT_AND_AUTHORITY = Uri.parse("content://" + AUTHORITY);
+
+ /**
+ * The value to be used whenever a transaction that expects an integer to be returned
+ * failed.
+ */
+ int TRANSACTION_FAILED = Integer.MIN_VALUE;
+
+ /**
+ * The value that denotes a timestamp was not set before (e.g. a message that is not
+ * delivered yet will not have a DELIVERED_TIMESTAMP)
+ */
+ long TIMESTAMP_NOT_SET = 0;
+
+ /**
+ * The table that {@link android.telephony.ims.RcsThread} gets persisted to
+ */
+ interface RcsThreadColumns {
+ /**
+ * The path that should be used for referring to
+ * {@link android.telephony.ims.RcsThread}s in
+ * {@link com.android.providers.telephony.RcsProvider} URIs.
+ */
+ String RCS_THREAD_URI_PART = "thread";
+
+ /**
+ * The URI to query or modify {@link android.telephony.ims.RcsThread} via the content
+ * provider.
+ */
+ Uri RCS_THREAD_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY, RCS_THREAD_URI_PART);
+
+ /**
+ * The unique identifier of an {@link android.telephony.ims.RcsThread}
+ */
+ String RCS_THREAD_ID_COLUMN = "rcs_thread_id";
+ }
+
+ /**
+ * The table that {@link android.telephony.ims.Rcs1To1Thread} gets persisted to
+ */
+ interface Rcs1To1ThreadColumns extends RcsThreadColumns {
+ /**
+ * The path that should be used for referring to
+ * {@link android.telephony.ims.Rcs1To1Thread}s in
+ * {@link com.android.providers.telephony.RcsProvider} URIs.
+ */
+ String RCS_1_TO_1_THREAD_URI_PART = "p2p_thread";
+
+ /**
+ * The URI to query or modify {@link android.telephony.ims.Rcs1To1Thread}s via the
+ * content provider
+ */
+ Uri RCS_1_TO_1_THREAD_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY,
+ RCS_1_TO_1_THREAD_URI_PART);
+
+ /**
+ * The SMS/MMS thread to fallback to in case of an RCS outage
+ */
+ String FALLBACK_THREAD_ID_COLUMN = "rcs_fallback_thread_id";
+ }
+
+ /**
+ * The table that {@link android.telephony.ims.RcsGroupThread} gets persisted to
+ */
+ interface RcsGroupThreadColumns extends RcsThreadColumns {
+ /**
+ * The path that should be used for referring to
+ * {@link android.telephony.ims.RcsGroupThread}s in
+ * {@link com.android.providers.telephony.RcsProvider} URIs.
+ */
+ String RCS_GROUP_THREAD_URI_PART = "group_thread";
+
+ /**
+ * The URI to query or modify {@link android.telephony.ims.RcsGroupThread}s via the
+ * content provider
+ */
+ Uri RCS_GROUP_THREAD_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY,
+ RCS_GROUP_THREAD_URI_PART);
+
+ /**
+ * The owner/admin of the {@link android.telephony.ims.RcsGroupThread}
+ */
+ String OWNER_PARTICIPANT_COLUMN = "owner_participant";
+
+ /**
+ * The user visible name of the group
+ */
+ String GROUP_NAME_COLUMN = "group_name";
+
+ /**
+ * The user visible icon of the group
+ */
+ String GROUP_ICON_COLUMN = "group_icon";
+
+ /**
+ * The RCS conference URI for this group
+ */
+ String CONFERENCE_URI_COLUMN = "conference_uri";
+ }
+
+ /**
+ * The view that enables polling from all types of RCS threads at once
+ */
+ interface RcsUnifiedThreadColumns extends RcsThreadColumns, Rcs1To1ThreadColumns,
+ RcsGroupThreadColumns {
+ /**
+ * The type of this {@link android.telephony.ims.RcsThread}
+ */
+ String THREAD_TYPE_COLUMN = "thread_type";
+
+ /**
+ * Integer returned as a result from a database query that denotes the thread is 1 to 1
+ */
+ int THREAD_TYPE_1_TO_1 = 0;
+
+ /**
+ * Integer returned as a result from a database query that denotes the thread is 1 to 1
+ */
+ int THREAD_TYPE_GROUP = 1;
+ }
+
+ /**
+ * The table that {@link android.telephony.ims.RcsParticipant} gets persisted to
+ */
+ interface RcsParticipantColumns {
+ /**
+ * The path that should be used for referring to
+ * {@link android.telephony.ims.RcsParticipant}s in
+ * {@link com.android.providers.telephony.RcsProvider} URIs.
+ */
+ String RCS_PARTICIPANT_URI_PART = "participant";
+
+ /**
+ * The URI to query or modify {@link android.telephony.ims.RcsParticipant}s via the
+ * content provider
+ */
+ Uri RCS_PARTICIPANT_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY,
+ RCS_PARTICIPANT_URI_PART);
+
+ /**
+ * The unique identifier of the entry in the database
+ */
+ String RCS_PARTICIPANT_ID_COLUMN = "rcs_participant_id";
+
+ /**
+ * A foreign key on canonical_address table, also used by SMS/MMS
+ */
+ String CANONICAL_ADDRESS_ID_COLUMN = "canonical_address_id";
+
+ /**
+ * The user visible RCS alias for this participant.
+ */
+ String RCS_ALIAS_COLUMN = "rcs_alias";
+ }
+
+ /**
+ * Additional constants to enable access to {@link android.telephony.ims.RcsParticipant}
+ * related data
+ */
+ interface RcsParticipantHelpers extends RcsParticipantColumns {
+ /**
+ * The view that unifies "rcs_participant" and "canonical_addresses" tables for easy
+ * access to participant address.
+ */
+ String RCS_PARTICIPANT_WITH_ADDRESS_VIEW = "rcs_participant_with_address_view";
+
+ /**
+ * The view that unifies "rcs_participant", "canonical_addresses" and
+ * "rcs_thread_participant" junction table to get full information on participants that
+ * contribute to threads.
+ */
+ String RCS_PARTICIPANT_WITH_THREAD_VIEW = "rcs_participant_with_thread_view";
+ }
+
+ /**
+ * The table that {@link android.telephony.ims.RcsMessage} gets persisted to
+ */
+ interface RcsMessageColumns {
+ /**
+ * Denotes the type of this message (i.e.
+ * {@link android.telephony.ims.RcsIncomingMessage} or
+ * {@link android.telephony.ims.RcsOutgoingMessage}
+ */
+ String MESSAGE_TYPE_COLUMN = "rcs_message_type";
+
+ /**
+ * The unique identifier for the message in the database - i.e. the primary key.
+ */
+ String MESSAGE_ID_COLUMN = "rcs_message_row_id";
+
+ /**
+ * The globally unique RCS identifier for the message. Please see 4.4.5.2 - GSMA
+ * RCC.53 (RCS Device API 1.6 Specification)
+ */
+ String GLOBAL_ID_COLUMN = "rcs_message_global_id";
+
+ /**
+ * The subscription where this message was sent from/to.
+ */
+ String SUB_ID_COLUMN = "sub_id";
+
+ /**
+ * The sending status of the message.
+ * @see android.telephony.ims.RcsMessage.RcsMessageStatus
+ */
+ String STATUS_COLUMN = "status";
+
+ /**
+ * The creation timestamp of the message.
+ */
+ String ORIGINATION_TIMESTAMP_COLUMN = "origination_timestamp";
+
+ /**
+ * The text content of the message.
+ */
+ String MESSAGE_TEXT_COLUMN = "rcs_text";
+
+ /**
+ * The latitude content of the message, if it contains a location.
+ */
+ String LATITUDE_COLUMN = "latitude";
+
+ /**
+ * The longitude content of the message, if it contains a location.
+ */
+ String LONGITUDE_COLUMN = "longitude";
+ }
+
+ /**
+ * The table that additional information of {@link android.telephony.ims.RcsIncomingMessage}
+ * gets persisted to.
+ */
+ interface RcsIncomingMessageColumns extends RcsMessageColumns {
+ /**
+ The path that should be used for referring to
+ * {@link android.telephony.ims.RcsIncomingMessage}s in
+ * {@link com.android.providers.telephony.RcsProvider} URIs.
+ */
+ String INCOMING_MESSAGE_URI_PART = "incoming_message";
+
+ /**
+ * The URI to query incoming messages through
+ * {@link com.android.providers.telephony.RcsProvider}
+ */
+ Uri INCOMING_MESSAGE_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY,
+ INCOMING_MESSAGE_URI_PART);
+
+ /**
+ * The ID of the {@link android.telephony.ims.RcsParticipant} that sent this message
+ */
+ String SENDER_PARTICIPANT_ID_COLUMN = "sender_participant";
+
+ /**
+ * The timestamp of arrival for this message.
+ */
+ String ARRIVAL_TIMESTAMP_COLUMN = "arrival_timestamp";
+
+ /**
+ * The time when the recipient has read this message.
+ */
+ String SEEN_TIMESTAMP_COLUMN = "seen_timestamp";
+ }
+
+ /**
+ * The table that additional information of {@link android.telephony.ims.RcsOutgoingMessage}
+ * gets persisted to.
+ */
+ interface RcsOutgoingMessageColumns extends RcsMessageColumns {
+ /**
+ * The path that should be used for referring to
+ * {@link android.telephony.ims.RcsOutgoingMessage}s in
+ * {@link com.android.providers.telephony.RcsProvider} URIs.
+ */
+ String OUTGOING_MESSAGE_URI_PART = "outgoing_message";
+
+ /**
+ * The URI to query or modify {@link android.telephony.ims.RcsOutgoingMessage}s via the
+ * content provider
+ */
+ Uri OUTGOING_MESSAGE_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY,
+ OUTGOING_MESSAGE_URI_PART);
+ }
+
+ /**
+ * The delivery information of an {@link android.telephony.ims.RcsOutgoingMessage}
+ */
+ interface RcsMessageDeliveryColumns extends RcsOutgoingMessageColumns {
+ /**
+ * The path that should be used for referring to
+ * {@link android.telephony.ims.RcsOutgoingMessageDelivery}s in
+ * {@link com.android.providers.telephony.RcsProvider} URIs.
+ */
+ String DELIVERY_URI_PART = "delivery";
+
+ /**
+ * The timestamp of delivery of this message.
+ */
+ String DELIVERED_TIMESTAMP_COLUMN = "delivered_timestamp";
+
+ /**
+ * The time when the recipient has read this message.
+ */
+ String SEEN_TIMESTAMP_COLUMN = "seen_timestamp";
+ }
+
+ /**
+ * The views that allow querying {@link android.telephony.ims.RcsIncomingMessage} and
+ * {@link android.telephony.ims.RcsOutgoingMessage} at the same time.
+ */
+ interface RcsUnifiedMessageColumns extends RcsIncomingMessageColumns,
+ RcsOutgoingMessageColumns {
+ /**
+ * The path that is used to query all {@link android.telephony.ims.RcsMessage} in
+ * {@link com.android.providers.telephony.RcsProvider} URIs.
+ */
+ String UNIFIED_MESSAGE_URI_PART = "message";
+
+ /**
+ * The URI to query all types of {@link android.telephony.ims.RcsMessage}s
+ */
+ Uri UNIFIED_MESSAGE_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY,
+ UNIFIED_MESSAGE_URI_PART);
+
+ /**
+ * The name of the view that unites rcs_message and rcs_incoming_message tables.
+ */
+ String UNIFIED_INCOMING_MESSAGE_VIEW = "unified_incoming_message_view";
+
+ /**
+ * The name of the view that unites rcs_message and rcs_outgoing_message tables.
+ */
+ String UNIFIED_OUTGOING_MESSAGE_VIEW = "unified_outgoing_message_view";
+
+ /**
+ * The column that shows from which table the message entry came from.
+ */
+ String MESSAGE_TYPE_COLUMN = "message_type";
+
+ /**
+ * Integer returned as a result from a database query that denotes that the message is
+ * an incoming message
+ */
+ int MESSAGE_TYPE_INCOMING = 1;
+
+ /**
+ * Integer returned as a result from a database query that denotes that the message is
+ * an outgoing message
+ */
+ int MESSAGE_TYPE_OUTGOING = 0;
+ }
+
+ /**
+ * The table that {@link android.telephony.ims.RcsFileTransferPart} gets persisted to.
+ */
+ interface RcsFileTransferColumns {
+ /**
+ * The path that should be used for referring to
+ * {@link android.telephony.ims.RcsFileTransferPart}s in
+ * {@link com.android.providers.telephony.RcsProvider} URIs.
+ */
+ String FILE_TRANSFER_URI_PART = "file_transfer";
+
+ /**
+ * The URI to query or modify {@link android.telephony.ims.RcsFileTransferPart}s via the
+ * content provider
+ */
+ Uri FILE_TRANSFER_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY,
+ FILE_TRANSFER_URI_PART);
+
+ /**
+ * The globally unique file transfer ID for this RCS file transfer.
+ */
+ String FILE_TRANSFER_ID_COLUMN = "rcs_file_transfer_id";
+
+ /**
+ * The RCS session ID for this file transfer. The ID is implementation dependent but
+ * should be unique.
+ */
+ String SESSION_ID_COLUMN = "session_id";
+
+ /**
+ * The URI that points to the content of this file transfer
+ */
+ String CONTENT_URI_COLUMN = "content_uri";
+
+ /**
+ * The file type of this file transfer in bytes. The validity of types is not enforced
+ * in {@link android.telephony.ims.RcsMessageStore} APIs.
+ */
+ String CONTENT_TYPE_COLUMN = "content_type";
+
+ /**
+ * The size of the file transfer in bytes.
+ */
+ String FILE_SIZE_COLUMN = "file_size";
+
+ /**
+ * Number of bytes that was successfully transmitted for this file transfer
+ */
+ String SUCCESSFULLY_TRANSFERRED_BYTES = "transfer_offset";
+
+ /**
+ * The status of this file transfer
+ * @see android.telephony.ims.RcsFileTransferPart.RcsFileTransferStatus
+ */
+ String TRANSFER_STATUS_COLUMN = "transfer_status";
+
+ /**
+ * The on-screen width of the file transfer, if it contains multi-media
+ */
+ String WIDTH_COLUMN = "width";
+
+ /**
+ * The on-screen height of the file transfer, if it contains multi-media
+ */
+ String HEIGHT_COLUMN = "height";
+
+ /**
+ * The duration of the content in milliseconds if this file transfer contains
+ * multi-media
+ */
+ String DURATION_MILLIS_COLUMN = "duration";
+
+ /**
+ * The URI to the preview of the content of this file transfer
+ */
+ String PREVIEW_URI_COLUMN = "preview_uri";
+
+ /**
+ * The type of the preview of the content of this file transfer. The validity of types
+ * is not enforced in {@link android.telephony.ims.RcsMessageStore} APIs.
+ */
+ String PREVIEW_TYPE_COLUMN = "preview_type";
+ }
+
+ /**
+ * The table that holds the information for
+ * {@link android.telephony.ims.RcsGroupThreadEvent} and its subclasses.
+ */
+ interface RcsThreadEventColumns {
+ /**
+ * The string used in the {@link com.android.providers.telephony.RcsProvider} URI to
+ * refer to participant joined events (example URI:
+ * {@code content://rcs/group_thread/3/participant_joined_event})
+ */
+ String PARTICIPANT_JOINED_URI_PART = "participant_joined_event";
+
+ /**
+ * The string used in the {@link com.android.providers.telephony.RcsProvider} URI to
+ * refer to participant left events. (example URI:
+ * {@code content://rcs/group_thread/3/participant_left_event/4})
+ */
+ String PARTICIPANT_LEFT_URI_PART = "participant_left_event";
+
+ /**
+ * The string used in the {@link com.android.providers.telephony.RcsProvider} URI to
+ * refer to name changed events. (example URI:
+ * {@code content://rcs/group_thread/3/name_changed_event})
+ */
+ String NAME_CHANGED_URI_PART = "name_changed_event";
+
+ /**
+ * The string used in the {@link com.android.providers.telephony.RcsProvider} URI to
+ * refer to icon changed events. (example URI:
+ * {@code content://rcs/group_thread/3/icon_changed_event})
+ */
+ String ICON_CHANGED_URI_PART = "icon_changed_event";
+
+ /**
+ * The unique ID of this event in the database, i.e. the primary key
+ */
+ String EVENT_ID_COLUMN = "event_id";
+
+ /**
+ * The type of this event
+ *
+ * @see RcsEventTypes
+ */
+ String EVENT_TYPE_COLUMN = "event_type";
+
+ /**
+ * The timestamp in milliseconds of when this event happened
+ */
+ String TIMESTAMP_COLUMN = "origination_timestamp";
+
+ /**
+ * The participant that generated this event
+ */
+ String SOURCE_PARTICIPANT_ID_COLUMN = "source_participant";
+
+ /**
+ * The receiving participant of this event if this was an
+ * {@link android.telephony.ims.RcsGroupThreadParticipantJoinedEvent} or
+ * {@link android.telephony.ims.RcsGroupThreadParticipantLeftEvent}
+ */
+ String DESTINATION_PARTICIPANT_ID_COLUMN = "destination_participant";
+
+ /**
+ * The URI for the new icon of the group thread if this was an
+ * {@link android.telephony.ims.RcsGroupThreadIconChangedEvent}
+ */
+ String NEW_ICON_URI_COLUMN = "new_icon_uri";
+
+ /**
+ * The URI for the new name of the group thread if this was an
+ * {@link android.telephony.ims.RcsGroupThreadNameChangedEvent}
+ */
+ String NEW_NAME_COLUMN = "new_name";
+ }
+
+ /**
+ * The table that {@link android.telephony.ims.RcsParticipantAliasChangedEvent} gets
+ * persisted to
+ */
+ interface RcsParticipantEventColumns {
+ /**
+ * The path that should be used for referring to
+ * {@link android.telephony.ims.RcsParticipantAliasChangedEvent}s in
+ * {@link com.android.providers.telephony.RcsProvider} URIs.
+ */
+ String ALIAS_CHANGE_EVENT_URI_PART = "alias_change_event";
+
+ /**
+ * The new alias of the participant
+ */
+ String NEW_ALIAS_COLUMN = "new_alias";
+ }
+
+ /**
+ * These values are used in {@link com.android.providers.telephony.RcsProvider} to determine
+ * what kind of event is present in the storage.
+ */
+ interface RcsEventTypes {
+ /**
+ * Integer constant that is stored in the
+ * {@link com.android.providers.telephony.RcsProvider} database that denotes the event
+ * is of type {@link android.telephony.ims.RcsParticipantAliasChangedEvent}
+ */
+ int PARTICIPANT_ALIAS_CHANGED_EVENT_TYPE = 1;
+
+ /**
+ * Integer constant that is stored in the
+ * {@link com.android.providers.telephony.RcsProvider} database that denotes the event
+ * is of type {@link android.telephony.ims.RcsGroupThreadParticipantJoinedEvent}
+ */
+ int PARTICIPANT_JOINED_EVENT_TYPE = 2;
+
+ /**
+ * Integer constant that is stored in the
+ * {@link com.android.providers.telephony.RcsProvider} database that denotes the event
+ * is of type {@link android.telephony.ims.RcsGroupThreadParticipantLeftEvent}
+ */
+ int PARTICIPANT_LEFT_EVENT_TYPE = 4;
+
+ /**
+ * Integer constant that is stored in the
+ * {@link com.android.providers.telephony.RcsProvider} database that denotes the event
+ * is of type {@link android.telephony.ims.RcsGroupThreadIconChangedEvent}
+ */
+ int ICON_CHANGED_EVENT_TYPE = 8;
+
+ /**
+ * Integer constant that is stored in the
+ * {@link com.android.providers.telephony.RcsProvider} database that denotes the event
+ * is of type {@link android.telephony.ims.RcsGroupThreadNameChangedEvent}
+ */
+ int NAME_CHANGED_EVENT_TYPE = 16;
+ }
+
+ /**
+ * The view that allows unified querying across all events
+ */
+ interface RcsUnifiedEventHelper extends RcsParticipantEventColumns, RcsThreadEventColumns {
+ /**
+ * The path that should be used for referring to
+ * {@link android.telephony.ims.RcsEvent}s in
+ * {@link com.android.providers.telephony.RcsProvider} URIs.
+ */
+ String RCS_EVENT_QUERY_URI_PATH = "event";
+
+ /**
+ * The URI to query {@link android.telephony.ims.RcsEvent}s via the content provider.
+ */
+ Uri RCS_EVENT_QUERY_URI = Uri.withAppendedPath(CONTENT_AND_AUTHORITY,
+ RCS_EVENT_QUERY_URI_PATH);
+ }
+
+ /**
+ * Allows RCS specific canonical address handling.
+ */
+ interface RcsCanonicalAddressHelper {
+ /**
+ * Returns the canonical address ID for a canonical address, if now row exists, this
+ * will add a row and return its ID. This helper works against the same table used by
+ * the SMS and MMS threads, but is accessible only by the phone process for use by RCS
+ * message storage.
+ *
+ * @throws IllegalArgumentException if unable to retrieve or create the canonical
+ * address entry.
+ */
+ static long getOrCreateCanonicalAddressId(
+ ContentResolver contentResolver, String canonicalAddress) {
+
+ Uri.Builder uriBuilder = CONTENT_AND_AUTHORITY.buildUpon();
+ uriBuilder.appendPath("canonical-address");
+ uriBuilder.appendQueryParameter("address", canonicalAddress);
+ Uri uri = uriBuilder.build();
+
+ try (Cursor cursor = contentResolver.query(uri, null, null, null)) {
+ if (cursor != null && cursor.moveToFirst()) {
+ return cursor.getLong(cursor.getColumnIndex(CanonicalAddressesColumns._ID));
+ } else {
+ Rlog.e(TAG, "getOrCreateCanonicalAddressId returned no rows");
+ }
+ }
+
+ Rlog.e(TAG, "getOrCreateCanonicalAddressId failed");
+ throw new IllegalArgumentException(
+ "Unable to find or allocate a canonical address ID");
+ }
+ }
+ }
+
+ /**
* Contains all MMS messages.
*/
public static final class Mms implements BaseMmsColumns {
diff --git a/telephony/java/android/telephony/AccessNetworkConstants.java b/telephony/java/android/telephony/AccessNetworkConstants.java
index 9c64cf6ddd8f..75165afe097e 100644
--- a/telephony/java/android/telephony/AccessNetworkConstants.java
+++ b/telephony/java/android/telephony/AccessNetworkConstants.java
@@ -54,8 +54,15 @@ public final class AccessNetworkConstants {
*/
@SystemApi
public static final class TransportType {
+ /**
+ * Invalid transport type.
+ * @hide
+ */
+ public static final int INVALID = -1;
+
/** Wireless Wide Area Networks (i.e. Cellular) */
public static final int WWAN = 1;
+
/** Wireless Local Area Networks (i.e. Wifi) */
public static final int WLAN = 2;
@@ -65,6 +72,7 @@ public final class AccessNetworkConstants {
/** @hide */
public static String toString(int type) {
switch (type) {
+ case INVALID: return "INVALID";
case WWAN: return "WWAN";
case WLAN: return "WLAN";
default: return Integer.toString(type);
diff --git a/telephony/java/android/telephony/AvailableNetworkInfo.java b/telephony/java/android/telephony/AvailableNetworkInfo.java
index 4da79b34a55e..b407b2a03bc4 100644
--- a/telephony/java/android/telephony/AvailableNetworkInfo.java
+++ b/telephony/java/android/telephony/AvailableNetworkInfo.java
@@ -114,7 +114,7 @@ public final class AvailableNetworkInfo implements Parcelable {
in.readStringList(mMccMncs);
}
- public AvailableNetworkInfo(int subId, int priority, ArrayList<String> mccMncs) {
+ public AvailableNetworkInfo(int subId, int priority, List<String> mccMncs) {
mSubId = subId;
mPriority = priority;
mMccMncs = new ArrayList<String>(mccMncs);
diff --git a/telephony/java/android/telephony/CallAttributes.java b/telephony/java/android/telephony/CallAttributes.java
index 2d29875aadb4..0d4f09f98b43 100644
--- a/telephony/java/android/telephony/CallAttributes.java
+++ b/telephony/java/android/telephony/CallAttributes.java
@@ -50,10 +50,9 @@ public class CallAttributes implements Parcelable {
}
private CallAttributes(Parcel in) {
- mPreciseCallState = (PreciseCallState)
- in.readValue(PreciseCallState.class.getClassLoader());
- mNetworkType = in.readInt();
- mCallQuality = (CallQuality) in.readValue(CallQuality.class.getClassLoader());
+ this.mPreciseCallState = in.readParcelable(PreciseCallState.class.getClassLoader());
+ this.mNetworkType = in.readInt();
+ this.mCallQuality = in.readParcelable(CallQuality.class.getClassLoader());
}
// getters
@@ -118,9 +117,9 @@ public class CallAttributes implements Parcelable {
CallAttributes s = (CallAttributes) o;
- return (mPreciseCallState == s.mPreciseCallState
+ return (Objects.equals(mPreciseCallState, s.mPreciseCallState)
&& mNetworkType == s.mNetworkType
- && mCallQuality == s.mCallQuality);
+ && Objects.equals(mCallQuality, s.mCallQuality));
}
/**
@@ -134,9 +133,9 @@ public class CallAttributes implements Parcelable {
* {@link Parcelable#writeToParcel}
*/
public void writeToParcel(Parcel dest, @Parcelable.WriteFlags int flags) {
- mPreciseCallState.writeToParcel(dest, flags);
+ dest.writeParcelable(mPreciseCallState, flags);
dest.writeInt(mNetworkType);
- mCallQuality.writeToParcel(dest, flags);
+ dest.writeParcelable(mCallQuality, flags);
}
public static final Parcelable.Creator<CallAttributes> CREATOR = new Parcelable.Creator() {
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 190e82b6cc5b..5c5d8569f3e8 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2371,6 +2371,14 @@ public class CarrierConfigManager {
"support_emergency_dialer_shortcut_bool";
/**
+ * Support ASCII 7-BIT encoding for long SMS. This carrier config is used to enable
+ * this feature.
+ * @hide
+ */
+ public static final String KEY_ASCII_7_BIT_SUPPORT_FOR_LONG_MESSAGE_BOOL =
+ "ascii_7_bit_support_for_long_message_bool";
+
+ /**
* Controls RSRP threshold at which OpportunisticNetworkService will decide whether
* the opportunistic network is good enough for internet data.
*/
@@ -2780,6 +2788,7 @@ public class CarrierConfigManager {
sDefaults.putBoolean(KEY_CALL_WAITING_OVER_UT_WARNING_BOOL, false);
sDefaults.putBoolean(KEY_SUPPORT_CLIR_NETWORK_DEFAULT_BOOL, true);
sDefaults.putBoolean(KEY_SUPPORT_EMERGENCY_DIALER_SHORTCUT_BOOL, true);
+ sDefaults.putBoolean(KEY_ASCII_7_BIT_SUPPORT_FOR_LONG_MESSAGE_BOOL, false);
/* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_GOOD */
sDefaults.putInt(KEY_OPPORTUNISTIC_NETWORK_ENTRY_THRESHOLD_RSRP_INT, -108);
/* Default value is minimum RSRP level needed for SIGNAL_STRENGTH_MODERATE */
diff --git a/telephony/java/android/telephony/CarrierRestrictionRules.java b/telephony/java/android/telephony/CarrierRestrictionRules.java
index 37847aef9167..d47b55ca4372 100644
--- a/telephony/java/android/telephony/CarrierRestrictionRules.java
+++ b/telephony/java/android/telephony/CarrierRestrictionRules.java
@@ -27,6 +27,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
/**
* Contains the list of carrier restrictions.
@@ -93,6 +94,9 @@ public final class CarrierRestrictionRules implements Parcelable {
value = {CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED, CARRIER_RESTRICTION_DEFAULT_ALLOWED})
public @interface CarrierRestrictionDefault {}
+ /* Wild character for comparison */
+ private static final char WILD_CHARACTER = '?';
+
private List<CarrierIdentifier> mAllowedCarriers;
private List<CarrierIdentifier> mExcludedCarriers;
@CarrierRestrictionDefault
@@ -166,6 +170,124 @@ public final class CarrierRestrictionRules implements Parcelable {
}
/**
+ * Tests an array of carriers with the carrier restriction configuration. The list of carrier
+ * ids passed as argument does not need to be the same as currently present in the device.
+ *
+ * @param carrierIds list of {@link CarrierIdentifier}, one for each SIM slot on the device
+ * @return a list of boolean with the same size as input, indicating if each
+ * {@link CarrierIdentifier} is allowed or not.
+ */
+ public List<Boolean> isCarrierIdentifiersAllowed(@NonNull List<CarrierIdentifier> carrierIds) {
+ ArrayList<Boolean> result = new ArrayList<>(carrierIds.size());
+
+ // First calculate the result for each slot independently
+ for (int i = 0; i < carrierIds.size(); i++) {
+ boolean inAllowedList = isCarrierIdInList(carrierIds.get(i), mAllowedCarriers);
+ boolean inExcludedList = isCarrierIdInList(carrierIds.get(i), mExcludedCarriers);
+ if (mCarrierRestrictionDefault == CARRIER_RESTRICTION_DEFAULT_NOT_ALLOWED) {
+ result.add((inAllowedList && !inExcludedList) ? true : false);
+ } else {
+ result.add((inExcludedList && !inAllowedList) ? false : true);
+ }
+ }
+ // Apply the multi-slot policy, if needed.
+ if (mMultiSimPolicy == MULTISIM_POLICY_ONE_VALID_SIM_MUST_BE_PRESENT) {
+ for (boolean b : result) {
+ if (b) {
+ result.replaceAll(x -> true);
+ break;
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Indicates if a certain carrier {@code id} is present inside a {@code list}
+ *
+ * @return true if the carrier {@code id} is present, false otherwise
+ */
+ private static boolean isCarrierIdInList(CarrierIdentifier id, List<CarrierIdentifier> list) {
+ for (CarrierIdentifier listItem : list) {
+ // Compare MCC and MNC
+ if (!patternMatch(id.getMcc(), listItem.getMcc())
+ || !patternMatch(id.getMnc(), listItem.getMnc())) {
+ continue;
+ }
+
+ // Compare SPN. Comparison is on the complete strings, case insensitive and with wild
+ // characters.
+ String listItemValue = convertNullToEmpty(listItem.getSpn());
+ String idValue = convertNullToEmpty(id.getSpn());
+ if (!listItemValue.isEmpty()) {
+ if (!patternMatch(idValue, listItemValue)) {
+ continue;
+ }
+ }
+
+ // The IMSI of the configuration can be shorter than actual IMSI in the SIM card.
+ listItemValue = convertNullToEmpty(listItem.getImsi());
+ idValue = convertNullToEmpty(id.getImsi());
+ if (!patternMatch(
+ idValue.substring(0, Math.min(idValue.length(), listItemValue.length())),
+ listItemValue)) {
+ continue;
+ }
+
+ // The GID1 of the configuration can be shorter than actual GID1 in the SIM card.
+ listItemValue = convertNullToEmpty(listItem.getGid1());
+ idValue = convertNullToEmpty(id.getGid1());
+ if (!patternMatch(
+ idValue.substring(0, Math.min(idValue.length(), listItemValue.length())),
+ listItemValue)) {
+ continue;
+ }
+
+ // The GID2 of the configuration can be shorter than actual GID2 in the SIM card.
+ listItemValue = convertNullToEmpty(listItem.getGid2());
+ idValue = convertNullToEmpty(id.getGid2());
+ if (!patternMatch(
+ idValue.substring(0, Math.min(idValue.length(), listItemValue.length())),
+ listItemValue)) {
+ continue;
+ }
+
+ // Valid match was found in the list
+ return true;
+ }
+ return false;
+ }
+
+ private static String convertNullToEmpty(String value) {
+ return Objects.toString(value, "");
+ }
+
+ /**
+ * Performs a case insensitive string comparison against a given pattern. The character '?'
+ * is used in the pattern as wild character in the comparison. The string must have the same
+ * length as the pattern.
+ *
+ * @param str string to match
+ * @param pattern string containing the pattern
+ * @return true in case of match, false otherwise
+ */
+ private static boolean patternMatch(String str, String pattern) {
+ if (str.length() != pattern.length()) {
+ return false;
+ }
+ String lowerCaseStr = str.toLowerCase();
+ String lowerCasePattern = pattern.toLowerCase();
+
+ for (int i = 0; i < lowerCasePattern.length(); i++) {
+ if (lowerCasePattern.charAt(i) != lowerCaseStr.charAt(i)
+ && lowerCasePattern.charAt(i) != WILD_CHARACTER) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
* {@link Parcelable#writeToParcel}
*/
@Override
diff --git a/telephony/java/android/telephony/CellIdentityNr.java b/telephony/java/android/telephony/CellIdentityNr.java
index 6b1b84cd3458..856f08107fd7 100644
--- a/telephony/java/android/telephony/CellIdentityNr.java
+++ b/telephony/java/android/telephony/CellIdentityNr.java
@@ -30,6 +30,7 @@ public final class CellIdentityNr extends CellIdentity {
private final int mNrArfcn;
private final int mPci;
private final int mTac;
+ private final long mNci;
/**
*
@@ -44,11 +45,12 @@ public final class CellIdentityNr extends CellIdentity {
* @hide
*/
public CellIdentityNr(int pci, int tac, int nrArfcn, String mccStr, String mncStr,
- String alphal, String alphas) {
+ long nci, String alphal, String alphas) {
super(TAG, CellInfo.TYPE_NR, mccStr, mncStr, alphal, alphas);
mPci = pci;
mTac = tac;
mNrArfcn = nrArfcn;
+ mNci = nci;
}
/**
@@ -62,7 +64,7 @@ public final class CellIdentityNr extends CellIdentity {
@Override
public int hashCode() {
- return Objects.hash(super.hashCode(), mPci, mTac, mNrArfcn);
+ return Objects.hash(super.hashCode(), mPci, mTac, mNrArfcn, mNci);
}
@Override
@@ -72,7 +74,17 @@ public final class CellIdentityNr extends CellIdentity {
}
CellIdentityNr o = (CellIdentityNr) other;
- return super.equals(o) && mPci == o.mPci && mTac == o.mTac && mNrArfcn == o.mNrArfcn;
+ return super.equals(o) && mPci == o.mPci && mTac == o.mTac && mNrArfcn == o.mNrArfcn
+ && mNci == o.mNci;
+ }
+
+ /**
+ * Get the NR Cell Identity.
+ *
+ * @return The NR Cell Identity in range [0, 68719476735] or Long.MAX_VALUE if unknown.
+ */
+ public long getNci() {
+ return mNci;
}
/**
@@ -122,6 +134,7 @@ public final class CellIdentityNr extends CellIdentity {
.append(" mNrArfcn = ").append(mNrArfcn)
.append(" mMcc = ").append(mMccStr)
.append(" mMnc = ").append(mMncStr)
+ .append(" mNci = ").append(mNci)
.append(" mAlphaLong = ").append(mAlphaLong)
.append(" mAlphaShort = ").append(mAlphaShort)
.append(" }")
@@ -134,6 +147,7 @@ public final class CellIdentityNr extends CellIdentity {
dest.writeInt(mPci);
dest.writeInt(mTac);
dest.writeInt(mNrArfcn);
+ dest.writeLong(mNci);
}
/** Construct from Parcel, type has already been processed */
@@ -142,6 +156,7 @@ public final class CellIdentityNr extends CellIdentity {
mPci = in.readInt();
mTac = in.readInt();
mNrArfcn = in.readInt();
+ mNci = in.readLong();
}
/** Implement the Parcelable interface */
diff --git a/telephony/java/android/telephony/CellIdentityTdscdma.java b/telephony/java/android/telephony/CellIdentityTdscdma.java
index 38143335dbf1..dba437a3a007 100644
--- a/telephony/java/android/telephony/CellIdentityTdscdma.java
+++ b/telephony/java/android/telephony/CellIdentityTdscdma.java
@@ -141,6 +141,14 @@ public final class CellIdentityTdscdma extends CellIdentity {
return mCpid;
}
+ /**
+ * @return 16-bit UMTS Absolute RF Channel Number,
+ * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable.
+ */
+ public int getUarfcn() {
+ return mUarfcn;
+ }
+
/** @hide */
@Override
public int getChannelNumber() {
diff --git a/telephony/java/android/telephony/CellSignalStrengthGsm.java b/telephony/java/android/telephony/CellSignalStrengthGsm.java
index 30e641d61143..a4207c99ce4d 100644
--- a/telephony/java/android/telephony/CellSignalStrengthGsm.java
+++ b/telephony/java/android/telephony/CellSignalStrengthGsm.java
@@ -63,6 +63,10 @@ public final class CellSignalStrengthGsm extends CellSignalStrength implements P
public CellSignalStrengthGsm(android.hardware.radio.V1_0.GsmSignalStrength gsm) {
// Convert from HAL values as part of construction.
this(getRssiDbmFromAsu(gsm.signalStrength), gsm.bitErrorRate, gsm.timingAdvance);
+
+ if (mRssi == CellInfo.UNAVAILABLE) {
+ setDefaultValues();
+ }
}
/** @hide */
diff --git a/telephony/java/android/telephony/CellSignalStrengthNr.java b/telephony/java/android/telephony/CellSignalStrengthNr.java
index 061cd4b33950..6f84ec58be60 100644
--- a/telephony/java/android/telephony/CellSignalStrengthNr.java
+++ b/telephony/java/android/telephony/CellSignalStrengthNr.java
@@ -77,6 +77,14 @@ public final class CellSignalStrengthNr extends CellSignalStrength implements Pa
}
/**
+ * @hide
+ * @param ss signal strength from modem.
+ */
+ public CellSignalStrengthNr(android.hardware.radio.V1_4.NrSignalStrength ss) {
+ this(ss.csiRsrp, ss.csiRsrq, ss.csiSinr, ss.ssRsrp, ss.ssRsrq, ss.ssSinr);
+ }
+
+ /**
* Reference: 3GPP TS 38.215.
* Range: -140 dBm to -44 dBm.
* @return SS reference signal received power, {@link CellInfo#UNAVAILABLE} means unreported
diff --git a/telephony/java/android/telephony/CellSignalStrengthTdscdma.java b/telephony/java/android/telephony/CellSignalStrengthTdscdma.java
index 6f52b853d23b..5ae89b0f8b3d 100644
--- a/telephony/java/android/telephony/CellSignalStrengthTdscdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthTdscdma.java
@@ -72,6 +72,10 @@ public final class CellSignalStrengthTdscdma extends CellSignalStrength implemen
// Convert from HAL values as part of construction.
this(CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE,
tdscdma.rscp != CellInfo.UNAVAILABLE ? -tdscdma.rscp : tdscdma.rscp);
+
+ if (mRssi == CellInfo.UNAVAILABLE && mRscp == CellInfo.UNAVAILABLE) {
+ setDefaultValues();
+ }
}
/** @hide */
@@ -79,6 +83,10 @@ public final class CellSignalStrengthTdscdma extends CellSignalStrength implemen
// Convert from HAL values as part of construction.
this(getRssiDbmFromAsu(tdscdma.signalStrength),
tdscdma.bitErrorRate, getRscpDbmFromAsu(tdscdma.rscp));
+
+ if (mRssi == CellInfo.UNAVAILABLE && mRscp == CellInfo.UNAVAILABLE) {
+ setDefaultValues();
+ }
}
/** @hide */
diff --git a/telephony/java/android/telephony/CellSignalStrengthWcdma.java b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
index 0760407171ae..efa3647f0e9b 100644
--- a/telephony/java/android/telephony/CellSignalStrengthWcdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
@@ -92,8 +92,12 @@ public final class CellSignalStrengthWcdma extends CellSignalStrength implements
/** @hide */
public CellSignalStrengthWcdma(android.hardware.radio.V1_0.WcdmaSignalStrength wcdma) {
// Convert from HAL values as part of construction.
- this(getRssiDbmFromAsu(wcdma.signalStrength),
- wcdma.bitErrorRate, CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE);
+ this(getRssiDbmFromAsu(wcdma.signalStrength), wcdma.bitErrorRate,
+ CellInfo.UNAVAILABLE, CellInfo.UNAVAILABLE);
+
+ if (mRssi == CellInfo.UNAVAILABLE && mRscp == CellInfo.UNAVAILABLE) {
+ setDefaultValues();
+ }
}
/** @hide */
@@ -103,6 +107,10 @@ public final class CellSignalStrengthWcdma extends CellSignalStrength implements
wcdma.base.bitErrorRate,
getRscpDbmFromAsu(wcdma.rscp),
getEcNoDbFromAsu(wcdma.ecno));
+
+ if (mRssi == CellInfo.UNAVAILABLE && mRscp == CellInfo.UNAVAILABLE) {
+ setDefaultValues();
+ }
}
/** @hide */
diff --git a/telephony/java/android/telephony/DebugEventReporter.java b/telephony/java/android/telephony/DebugEventReporter.java
new file mode 100644
index 000000000000..14b7dd6d1b72
--- /dev/null
+++ b/telephony/java/android/telephony/DebugEventReporter.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2019 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.NonNull;
+import android.annotation.RequiresPermission;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.ParcelUuid;
+
+import com.android.internal.util.IndentingPrintWriter;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.List;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * A Simple Surface for Telephony to notify a loosely-coupled debugger of particular issues.
+ *
+ * DebugEventReporter allows an optional external logging component to receive events detected by
+ * the framework and take action. This log surface is designed to provide maximium flexibility
+ * to the receiver of these events. Envisioned use cases of this include notifying a vendor
+ * component of: an event that necessitates (timely) log collection on non-AOSP components;
+ * notifying a vendor component of a rare event that should prompt further action such as a
+ * bug report or user intervention for debug purposes.
+ *
+ * <p>This surface is not intended to enable a diagnostic monitor, nor is it intended to support
+ * streaming logs.
+ *
+ * @hide
+ */
+public final class DebugEventReporter {
+ private static final String TAG = "DebugEventReporter";
+
+ private static Context sContext = null;
+
+ private static Map<UUID, Integer> sEvents = new ConcurrentHashMap<>();
+
+ /*
+ * Because this is only supporting system packages, once we find a package, it will be the
+ * same package until the next system upgrade. Thus, to save time in processing debug events
+ * we can cache this info and skip the resolution process after it's done the first time.
+ */
+ private static String sDebugPackageName = null;
+
+ private DebugEventReporter() {};
+
+ /**
+ * If enabled, build and send an intent to a Debug Service for logging.
+ *
+ * This method sends the {@link TelephonyManager#DEBUG_EVENT DEBUG_EVENT} broadcast, which is
+ * system protected. Invoking this method unless you are the system will result in an error.
+ *
+ * @param eventId a fixed event ID that will be sent for each instance of the same event. This
+ * ID should be generated randomly.
+ * @param description an optional description, that if included will be used as the subject for
+ * identification and discussion of this event. This description should ideally be
+ * static and must not contain any sensitive information (especially PII).
+ */
+ public static void sendEvent(@NonNull UUID eventId, String description) {
+ if (sContext == null) {
+ Rlog.w(TAG, "DebugEventReporter not yet initialized, dropping event=" + eventId);
+ return;
+ }
+
+ // If this event has already occurred, skip sending intents for it; regardless log its
+ // invocation here.
+ Integer count = sEvents.containsKey(eventId) ? sEvents.get(eventId) + 1 : 1;
+ sEvents.put(eventId, count);
+ if (count > 1) return;
+
+ // Even if we are initialized, that doesn't mean that a package name has been found.
+ // This is normal in many cases, such as when no debug package is installed on the system,
+ // so drop these events silently.
+ if (sDebugPackageName == null) return;
+
+ Intent dbgIntent = new Intent(TelephonyManager.ACTION_DEBUG_EVENT);
+ dbgIntent.putExtra(TelephonyManager.EXTRA_DEBUG_EVENT_ID, new ParcelUuid(eventId));
+ if (description != null) {
+ dbgIntent.putExtra(TelephonyManager.EXTRA_DEBUG_EVENT_DESCRIPTION, description);
+ }
+ dbgIntent.setPackage(sDebugPackageName);
+ sContext.sendBroadcast(dbgIntent, android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE);
+ }
+
+ /**
+ * Initialize the DebugEventReporter with the current context.
+ *
+ * This method must be invoked before any calls to sendEvent() will succeed. This method should
+ * only be invoked at most once.
+ *
+ * @param context a Context object used to initialize this singleton DebugEventReporter in
+ * the current process.
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public static void initialize(@NonNull Context context) {
+ if (context == null) {
+ throw new IllegalArgumentException("DebugEventReporter needs a non-null context.");
+ }
+
+ // Ensure that this context has sufficient permissions to send debug events.
+ context.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE,
+ "This app does not have privileges to send debug events");
+
+ sContext = context;
+
+ // Check to see if there is a valid debug package; if there are multiple, that's a config
+ // error, so just take the first one.
+ PackageManager pm = sContext.getPackageManager();
+ if (pm == null) return;
+ List<ResolveInfo> packages = pm.queryBroadcastReceivers(
+ new Intent(TelephonyManager.ACTION_DEBUG_EVENT),
+ PackageManager.MATCH_SYSTEM_ONLY
+ | PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
+ if (packages == null || packages.isEmpty()) return;
+ if (packages.size() > 1) {
+ Rlog.e(TAG, "Multiple DebugEvent Receivers installed.");
+ }
+
+ for (ResolveInfo r : packages) {
+ if (r.activityInfo == null
+ || pm.checkPermission(
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ r.activityInfo.packageName)
+ != PackageManager.PERMISSION_GRANTED) {
+ Rlog.w(TAG,
+ "Found package without proper permissions or no activity"
+ + r.activityInfo.packageName);
+ continue;
+ }
+ Rlog.d(TAG, "Found a valid package " + r.activityInfo.packageName);
+ sDebugPackageName = r.activityInfo.packageName;
+ break;
+ }
+ // Initialization may only be performed once.
+ }
+
+ /** Dump the contents of the DebugEventReporter */
+ public static void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
+ if (sContext == null) return;
+ IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " ");
+ sContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, "Requires DUMP");
+ pw.println("Initialized=" + (sContext != null ? "Yes" : "No"));
+ pw.println("Debug Package=" + sDebugPackageName);
+ pw.println("Event Counts:");
+ pw.increaseIndent();
+ for (UUID event : sEvents.keySet()) {
+ pw.println(event + ": " + sEvents.get(event));
+ }
+ pw.decreaseIndent();
+ pw.flush();
+ }
+}
diff --git a/telephony/java/android/telephony/LocationAccessPolicy.java b/telephony/java/android/telephony/LocationAccessPolicy.java
index 53d69f447a56..24db438580c9 100644
--- a/telephony/java/android/telephony/LocationAccessPolicy.java
+++ b/telephony/java/android/telephony/LocationAccessPolicy.java
@@ -26,11 +26,12 @@ import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.location.LocationManager;
import android.os.Binder;
+import android.os.Build;
import android.os.Process;
-import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
+import android.widget.Toast;
import java.util.List;
@@ -41,61 +42,236 @@ import java.util.List;
public final class LocationAccessPolicy {
private static final String TAG = "LocationAccessPolicy";
private static final boolean DBG = false;
+ public static final int MAX_SDK_FOR_ANY_ENFORCEMENT = Build.VERSION_CODES.P;
- /**
- * API to determine if the caller has permissions to get cell location.
- *
- * @param pkgName Package name of the application requesting access
- * @param uid The uid of the package
- * @param pid The pid of the package
- * @param throwOnDeniedPermission Whether to throw if the location permission is denied.
- * @return boolean true or false if permissions is granted
- */
- public static boolean canAccessCellLocation(@NonNull Context context, @NonNull String pkgName,
- int uid, int pid, boolean throwOnDeniedPermission) throws SecurityException {
- Trace.beginSection("TelephonyLocationCheck");
- try {
- // Always allow the phone process and system server to access location. This avoid
- // breaking legacy code that rely on public-facing APIs to access cell location, and
- // it doesn't create an info leak risk because the cell location is stored in the phone
- // process anyway, and the system server already has location access.
- if (uid == Process.PHONE_UID || uid == Process.SYSTEM_UID || uid == Process.ROOT_UID) {
- return true;
+ public enum LocationPermissionResult {
+ ALLOWED,
+ /**
+ * Indicates that the denial is due to a transient device state
+ * (e.g. app-ops, location master switch)
+ */
+ DENIED_SOFT,
+ /**
+ * Indicates that the denial is due to a misconfigured app (e.g. missing entry in manifest)
+ */
+ DENIED_HARD,
+ }
+
+ public static class LocationPermissionQuery {
+ public final String callingPackage;
+ public final int callingUid;
+ public final int callingPid;
+ public final int minSdkVersionForCoarse;
+ public final int minSdkVersionForFine;
+ public final String method;
+
+ private LocationPermissionQuery(String callingPackage, int callingUid, int callingPid,
+ int minSdkVersionForCoarse, int minSdkVersionForFine, String method) {
+ this.callingPackage = callingPackage;
+ this.callingUid = callingUid;
+ this.callingPid = callingPid;
+ this.minSdkVersionForCoarse = minSdkVersionForCoarse;
+ this.minSdkVersionForFine = minSdkVersionForFine;
+ this.method = method;
+ }
+
+ public static class Builder {
+ private String mCallingPackage;
+ private int mCallingUid;
+ private int mCallingPid;
+ private int mMinSdkVersionForCoarse = Integer.MAX_VALUE;
+ private int mMinSdkVersionForFine = Integer.MAX_VALUE;
+ private String mMethod;
+
+ /**
+ * Mandatory parameter, used for performing permission checks.
+ */
+ public Builder setCallingPackage(String callingPackage) {
+ mCallingPackage = callingPackage;
+ return this;
}
- // We always require the location permission and also require the
- // location mode to be on for non-legacy apps. Legacy apps are
- // required to be in the foreground to at least mitigate the case
- // where a legacy app the user is not using tracks their location.
- // Granting ACCESS_FINE_LOCATION to an app automatically grants it
- // ACCESS_COARSE_LOCATION.
- if (throwOnDeniedPermission) {
- context.enforcePermission(Manifest.permission.ACCESS_COARSE_LOCATION,
- pid, uid, "canAccessCellLocation");
- } else if (context.checkPermission(Manifest.permission.ACCESS_COARSE_LOCATION,
- pid, uid) == PackageManager.PERMISSION_DENIED) {
- if (DBG) Log.w(TAG, "Permission checked failed (" + pid + "," + uid + ")");
- return false;
+ /**
+ * Mandatory parameter, used for performing permission checks.
+ */
+ public Builder setCallingUid(int callingUid) {
+ mCallingUid = callingUid;
+ return this;
}
- final int opCode = AppOpsManager.permissionToOpCode(
- Manifest.permission.ACCESS_COARSE_LOCATION);
- if (opCode != AppOpsManager.OP_NONE && context.getSystemService(AppOpsManager.class)
- .noteOpNoThrow(opCode, uid, pkgName) != AppOpsManager.MODE_ALLOWED) {
- if (DBG) Log.w(TAG, "AppOp check failed (" + uid + "," + pkgName + ")");
- return false;
+
+ /**
+ * Mandatory parameter, used for performing permission checks.
+ */
+ public Builder setCallingPid(int callingPid) {
+ mCallingPid = callingPid;
+ return this;
}
- if (!isLocationModeEnabled(context, UserHandle.getUserId(uid))) {
- if (DBG) Log.w(TAG, "Location disabled, failed, (" + uid + ")");
- return false;
+
+ /**
+ * Apps that target at least this sdk version will be checked for coarse location
+ * permission. Defaults to INT_MAX (which means don't check)
+ */
+ public Builder setMinSdkVersionForCoarse(
+ int minSdkVersionForCoarse) {
+ mMinSdkVersionForCoarse = minSdkVersionForCoarse;
+ return this;
}
- // If the user or profile is current, permission is granted.
- // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission.
- return isCurrentProfile(context, uid) || checkInteractAcrossUsersFull(context);
- } finally {
- Trace.endSection();
+
+ /**
+ * Apps that target at least this sdk version will be checked for fine location
+ * permission. Defaults to INT_MAX (which means don't check)
+ */
+ public Builder setMinSdkVersionForFine(
+ int minSdkVersionForFine) {
+ mMinSdkVersionForFine = minSdkVersionForFine;
+ return this;
+ }
+
+ /**
+ * Optional, for logging purposes only.
+ */
+ public Builder setMethod(String method) {
+ mMethod = method;
+ return this;
+ }
+
+ public LocationPermissionQuery build() {
+ return new LocationPermissionQuery(mCallingPackage, mCallingUid,
+ mCallingPid, mMinSdkVersionForCoarse, mMinSdkVersionForFine, mMethod);
+ }
+ }
+ }
+
+ private static void logError(Context context, String errorMsg) {
+ Log.e(TAG, errorMsg);
+ try {
+ if (Build.IS_DEBUGGABLE) {
+ Toast.makeText(context, errorMsg, Toast.LENGTH_SHORT).show();
+ }
+ } catch (Throwable t) {
+ // whatever, not important
+ }
+ }
+
+ private static LocationPermissionResult appOpsModeToPermissionResult(int appOpsMode) {
+ switch (appOpsMode) {
+ case AppOpsManager.MODE_ALLOWED:
+ return LocationPermissionResult.ALLOWED;
+ case AppOpsManager.MODE_ERRORED:
+ return LocationPermissionResult.DENIED_HARD;
+ default:
+ return LocationPermissionResult.DENIED_SOFT;
}
}
+ private static LocationPermissionResult checkAppLocationPermissionHelper(Context context,
+ LocationPermissionQuery query, String permissionToCheck) {
+ String locationTypeForLog =
+ Manifest.permission.ACCESS_FINE_LOCATION.equals(permissionToCheck)
+ ? "fine" : "coarse";
+
+ // Do the app-ops and the manifest check without any of the allow-overrides first.
+ boolean hasManifestPermission = checkManifestPermission(context, query.callingPid,
+ query.callingUid, permissionToCheck);
+
+ int appOpMode = context.getSystemService(AppOpsManager.class)
+ .noteOpNoThrow(AppOpsManager.permissionToOpCode(permissionToCheck),
+ query.callingUid, query.callingPackage);
+
+ if (hasManifestPermission && appOpMode == AppOpsManager.MODE_ALLOWED) {
+ // If the app did everything right, return without logging.
+ return LocationPermissionResult.ALLOWED;
+ }
+
+ // If the app has the manifest permission but not the app-op permission, it means that
+ // it's aware of the requirement and the user denied permission explicitly. If we see
+ // this, don't let any of the overrides happen.
+ if (hasManifestPermission) {
+ Log.i(TAG, query.callingPackage + " is aware of " + locationTypeForLog + " but the"
+ + " app-ops permission is specifically denied.");
+ return appOpsModeToPermissionResult(appOpMode);
+ }
+
+ int minSdkVersion = Manifest.permission.ACCESS_FINE_LOCATION.equals(permissionToCheck)
+ ? query.minSdkVersionForFine : query.minSdkVersionForCoarse;
+
+ // If the app fails for some reason, see if it should be allowed to proceed.
+ if (minSdkVersion > MAX_SDK_FOR_ANY_ENFORCEMENT) {
+ String errorMsg = "Allowing " + query.callingPackage + " " + locationTypeForLog
+ + " because we're not enforcing API " + query.minSdkVersionForFine + " yet."
+ + " Please fix this app because it will break in the future. Called from "
+ + query.method;
+ logError(context, errorMsg);
+ return null;
+ } else if (!isAppAtLeastSdkVersion(context, query.callingPackage, minSdkVersion)) {
+ String errorMsg = "Allowing " + query.callingPackage + " " + locationTypeForLog
+ + " because it doesn't target API " + query.minSdkVersionForFine + " yet."
+ + " Please fix this app. Called from " + query.method;
+ logError(context, errorMsg);
+ return null;
+ } else {
+ // If we're not allowing it due to the above two conditions, this means that the app
+ // did not declare the permission in their manifest.
+ return LocationPermissionResult.DENIED_HARD;
+ }
+ }
+
+ public static LocationPermissionResult checkLocationPermission(
+ Context context, LocationPermissionQuery query) {
+ // Always allow the phone process and system server to access location. This avoid
+ // breaking legacy code that rely on public-facing APIs to access cell location, and
+ // it doesn't create an info leak risk because the cell location is stored in the phone
+ // process anyway, and the system server already has location access.
+ if (query.callingUid == Process.PHONE_UID || query.callingUid == Process.SYSTEM_UID
+ || query.callingUid == Process.ROOT_UID) {
+ return LocationPermissionResult.ALLOWED;
+ }
+
+ // Check the system-wide requirements. If the location master switch is off or
+ // the app's profile isn't in foreground, return a soft denial.
+ if (!checkSystemLocationAccess(context, query.callingUid, query.callingPid)) {
+ return LocationPermissionResult.DENIED_SOFT;
+ }
+
+ // Do the check for fine, then for coarse.
+ if (query.minSdkVersionForFine < Integer.MAX_VALUE) {
+ LocationPermissionResult resultForFine = checkAppLocationPermissionHelper(
+ context, query, Manifest.permission.ACCESS_FINE_LOCATION);
+ if (resultForFine != null) {
+ return resultForFine;
+ }
+ }
+
+ if (query.minSdkVersionForCoarse < Integer.MAX_VALUE) {
+ LocationPermissionResult resultForCoarse = checkAppLocationPermissionHelper(
+ context, query, Manifest.permission.ACCESS_COARSE_LOCATION);
+ if (resultForCoarse != null) {
+ return resultForCoarse;
+ }
+ }
+
+ // At this point, we're out of location checks to do. If the app bypassed all the previous
+ // ones due to the SDK grandfathering schemes, allow it access.
+ return LocationPermissionResult.ALLOWED;
+ }
+
+
+ private static boolean checkManifestPermission(Context context, int pid, int uid,
+ String permissionToCheck) {
+ return context.checkPermission(permissionToCheck, pid, uid)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ private static boolean checkSystemLocationAccess(@NonNull Context context, int uid, int pid) {
+ if (!isLocationModeEnabled(context, UserHandle.getUserId(uid))) {
+ if (DBG) Log.w(TAG, "Location disabled, failed, (" + uid + ")");
+ return false;
+ }
+ // If the user or profile is current, permission is granted.
+ // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission.
+ return isCurrentProfile(context, uid) || checkInteractAcrossUsersFull(context, uid, pid);
+ }
+
private static boolean isLocationModeEnabled(@NonNull Context context, @UserIdInt int userId) {
LocationManager locationManager = context.getSystemService(LocationManager.class);
if (locationManager == null) {
@@ -105,10 +281,10 @@ public final class LocationAccessPolicy {
return locationManager.isLocationEnabledForUser(UserHandle.of(userId));
}
- private static boolean checkInteractAcrossUsersFull(@NonNull Context context) {
- return context.checkCallingOrSelfPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
- == PackageManager.PERMISSION_GRANTED;
+ private static boolean checkInteractAcrossUsersFull(
+ @NonNull Context context, int pid, int uid) {
+ return checkManifestPermission(context, pid, uid,
+ Manifest.permission.INTERACT_ACROSS_USERS_FULL);
}
private static boolean isCurrentProfile(@NonNull Context context, int uid) {
@@ -132,4 +308,18 @@ public final class LocationAccessPolicy {
Binder.restoreCallingIdentity(token);
}
}
-}
+
+ private static boolean isAppAtLeastSdkVersion(Context context, String pkgName, int sdkVersion) {
+ try {
+ if (context.getPackageManager().getApplicationInfo(pkgName, 0).targetSdkVersion
+ >= sdkVersion) {
+ return true;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // In case of exception, assume known app (more strict checking)
+ // Note: This case will never happen since checkPackage is
+ // called to verify validity before checking app's version.
+ }
+ return false;
+ }
+} \ No newline at end of file
diff --git a/telephony/java/android/telephony/NetworkRegistrationState.java b/telephony/java/android/telephony/NetworkRegistrationState.java
index ceb76b57ae0c..6e6d59e62148 100644
--- a/telephony/java/android/telephony/NetworkRegistrationState.java
+++ b/telephony/java/android/telephony/NetworkRegistrationState.java
@@ -27,6 +27,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.Objects;
+import java.util.stream.Collectors;
/**
* Description of a mobile network registration state
@@ -151,7 +152,7 @@ public class NetworkRegistrationState implements Parcelable {
private final int[] mAvailableServices;
@Nullable
- private final CellIdentity mCellIdentity;
+ private CellIdentity mCellIdentity;
@Nullable
private VoiceSpecificRegistrationStates mVoiceSpecificStates;
@@ -360,7 +361,34 @@ public class NetworkRegistrationState implements Parcelable {
return 0;
}
- private static String regStateToString(int regState) {
+ /**
+ * Convert service type to string
+ *
+ * @hide
+ *
+ * @param serviceType The service type
+ * @return The service type in string format
+ */
+ public static String serviceTypeToString(@ServiceType int serviceType) {
+ switch (serviceType) {
+ case SERVICE_TYPE_VOICE: return "VOICE";
+ case SERVICE_TYPE_DATA: return "DATA";
+ case SERVICE_TYPE_SMS: return "SMS";
+ case SERVICE_TYPE_VIDEO: return "VIDEO";
+ case SERVICE_TYPE_EMERGENCY: return "EMERGENCY";
+ }
+ return "Unknown service type " + serviceType;
+ }
+
+ /**
+ * Convert registration state to string
+ *
+ * @hide
+ *
+ * @param regState The registration state
+ * @return The reg state in string
+ */
+ public static String regStateToString(@RegState int regState) {
switch (regState) {
case REG_STATE_NOT_REG_NOT_SEARCHING: return "NOT_REG_NOT_SEARCHING";
case REG_STATE_HOME: return "HOME";
@@ -389,14 +417,17 @@ public class NetworkRegistrationState implements Parcelable {
public String toString() {
return new StringBuilder("NetworkRegistrationState{")
.append(" domain=").append((mDomain == DOMAIN_CS) ? "CS" : "PS")
- .append("transportType=").append(mTransportType)
+ .append(" transportType=").append(TransportType.toString(mTransportType))
.append(" regState=").append(regStateToString(mRegState))
- .append(" roamingType=").append(mRoamingType)
+ .append(" roamingType=").append(ServiceState.roamingTypeToString(mRoamingType))
.append(" accessNetworkTechnology=")
.append(TelephonyManager.getNetworkTypeName(mAccessNetworkTechnology))
.append(" rejectCause=").append(mRejectCause)
.append(" emergencyEnabled=").append(mEmergencyOnly)
- .append(" supportedServices=").append(mAvailableServices)
+ .append(" availableServices=").append("[" + (mAvailableServices != null
+ ? Arrays.stream(mAvailableServices)
+ .mapToObj(type -> serviceTypeToString(type))
+ .collect(Collectors.joining(",")) : null) + "]")
.append(" cellIdentity=").append(mCellIdentity)
.append(" voiceSpecificStates=").append(mVoiceSpecificStates)
.append(" dataSpecificStates=").append(mDataSpecificStates)
@@ -490,4 +521,22 @@ public class NetworkRegistrationState implements Parcelable {
return new NetworkRegistrationState[size];
}
};
+
+ /**
+ * @hide
+ */
+ public NetworkRegistrationState sanitizeLocationInfo() {
+ NetworkRegistrationState result = copy();
+ result.mCellIdentity = null;
+ return result;
+ }
+
+ private NetworkRegistrationState copy() {
+ Parcel p = Parcel.obtain();
+ this.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ NetworkRegistrationState result = new NetworkRegistrationState(p);
+ p.recycle();
+ return result;
+ }
}
diff --git a/telephony/java/android/telephony/PhoneCapability.java b/telephony/java/android/telephony/PhoneCapability.java
index 2ebfa53ead23..21dad77d29b7 100644
--- a/telephony/java/android/telephony/PhoneCapability.java
+++ b/telephony/java/android/telephony/PhoneCapability.java
@@ -33,15 +33,17 @@ public class PhoneCapability implements Parcelable {
public final int maxActiveVoiceCalls;
public final int maxActiveData;
public final int max5G;
+ public final boolean validationBeforeSwitchSupported;
public final List<ModemInfo> logicalModemList;
public PhoneCapability(int maxActiveVoiceCalls, int maxActiveData, int max5G,
- List<ModemInfo> logicalModemList) {
+ List<ModemInfo> logicalModemList, boolean validationBeforeSwitchSupported) {
this.maxActiveVoiceCalls = maxActiveVoiceCalls;
this.maxActiveData = maxActiveData;
this.max5G = max5G;
// Make sure it's not null.
this.logicalModemList = logicalModemList == null ? new ArrayList<>() : logicalModemList;
+ this.validationBeforeSwitchSupported = validationBeforeSwitchSupported;
}
@Override
@@ -55,13 +57,15 @@ public class PhoneCapability implements Parcelable {
maxActiveVoiceCalls = in.readInt();
maxActiveData = in.readInt();
max5G = in.readInt();
+ validationBeforeSwitchSupported = in.readBoolean();
logicalModemList = new ArrayList<>();
in.readList(logicalModemList, ModemInfo.class.getClassLoader());
}
@Override
public int hashCode() {
- return Objects.hash(maxActiveVoiceCalls, maxActiveData, max5G, logicalModemList);
+ return Objects.hash(maxActiveVoiceCalls, maxActiveData, max5G, logicalModemList,
+ validationBeforeSwitchSupported);
}
@Override
@@ -79,6 +83,7 @@ public class PhoneCapability implements Parcelable {
return (maxActiveVoiceCalls == s.maxActiveVoiceCalls
&& maxActiveData == s.maxActiveData
&& max5G == s.max5G
+ && validationBeforeSwitchSupported == s.validationBeforeSwitchSupported
&& logicalModemList.equals(s.logicalModemList));
}
@@ -96,6 +101,7 @@ public class PhoneCapability implements Parcelable {
dest.writeInt(maxActiveVoiceCalls);
dest.writeInt(maxActiveData);
dest.writeInt(max5G);
+ dest.writeBoolean(validationBeforeSwitchSupported);
dest.writeList(logicalModemList);
}
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index fea1b7b08a20..3ce646cb400b 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -45,7 +45,8 @@ import java.util.concurrent.Executor;
* <p>
* Override the methods for the state that you wish to receive updates for, and
* pass your PhoneStateListener object, along with bitwise-or of the LISTEN_
- * flags to {@link TelephonyManager#listen TelephonyManager.listen()}.
+ * flags to {@link TelephonyManager#listen TelephonyManager.listen()}. Methods are
+ * called when the state changes, as well as once on initial registration.
* <p>
* Note that access to some telephony information is
* permission-protected. Your application won't receive updates for protected
diff --git a/telephony/java/android/telephony/PreciseCallState.java b/telephony/java/android/telephony/PreciseCallState.java
index 59f3e1f0e7f7..19e1931a30df 100644
--- a/telephony/java/android/telephony/PreciseCallState.java
+++ b/telephony/java/android/telephony/PreciseCallState.java
@@ -287,11 +287,11 @@ public final class PreciseCallState implements Parcelable {
return false;
}
PreciseCallState other = (PreciseCallState) obj;
- return (mRingingCallState != other.mRingingCallState &&
- mForegroundCallState != other.mForegroundCallState &&
- mBackgroundCallState != other.mBackgroundCallState &&
- mDisconnectCause != other.mDisconnectCause &&
- mPreciseDisconnectCause != other.mPreciseDisconnectCause);
+ return (mRingingCallState == other.mRingingCallState
+ && mForegroundCallState == other.mForegroundCallState
+ && mBackgroundCallState == other.mBackgroundCallState
+ && mDisconnectCause == other.mDisconnectCause
+ && mPreciseDisconnectCause == other.mPreciseDisconnectCause);
}
@Override
diff --git a/telephony/java/android/telephony/RadioAccessFamily.java b/telephony/java/android/telephony/RadioAccessFamily.java
index f63b753e075e..c1786befb096 100644
--- a/telephony/java/android/telephony/RadioAccessFamily.java
+++ b/telephony/java/android/telephony/RadioAccessFamily.java
@@ -17,12 +17,15 @@
package android.telephony;
import android.annotation.UnsupportedAppUsage;
+import android.hardware.radio.V1_0.RadioTechnology;
+import android.hardware.radio.V1_4.CellInfo.Info;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import com.android.internal.telephony.RILConstants;
+
/**
* Object to indicate the phone radio type and access technology.
*
@@ -33,29 +36,34 @@ public class RadioAccessFamily implements Parcelable {
/**
* TODO: get rid of RAF definition in RadioAccessFamily and
* use {@link TelephonyManager.NetworkTypeBitMask}
+ * TODO: public definition {@link TelephonyManager.NetworkTypeBitMask} is long.
+ * TODO: Convert from int to long everywhere including HAL definitions.
*/
// 2G
- public static final int RAF_UNKNOWN = TelephonyManager.NETWORK_TYPE_BITMASK_UNKNOWN;
- public static final int RAF_GSM = TelephonyManager.NETWORK_TYPE_BITMASK_GSM;
- public static final int RAF_GPRS = TelephonyManager.NETWORK_TYPE_BITMASK_GPRS;
- public static final int RAF_EDGE = TelephonyManager.NETWORK_TYPE_BITMASK_EDGE;
- public static final int RAF_IS95A = TelephonyManager.NETWORK_TYPE_BITMASK_CDMA;
- public static final int RAF_IS95B = TelephonyManager.NETWORK_TYPE_BITMASK_CDMA;
- public static final int RAF_1xRTT = TelephonyManager.NETWORK_TYPE_BITMASK_1xRTT;
+ public static final int RAF_UNKNOWN = (int) TelephonyManager.NETWORK_TYPE_BITMASK_UNKNOWN;
+ public static final int RAF_GSM = (int) TelephonyManager.NETWORK_TYPE_BITMASK_GSM;
+ public static final int RAF_GPRS = (int) TelephonyManager.NETWORK_TYPE_BITMASK_GPRS;
+ public static final int RAF_EDGE = (int) TelephonyManager.NETWORK_TYPE_BITMASK_EDGE;
+ public static final int RAF_IS95A = (int) TelephonyManager.NETWORK_TYPE_BITMASK_CDMA;
+ public static final int RAF_IS95B = (int) TelephonyManager.NETWORK_TYPE_BITMASK_CDMA;
+ public static final int RAF_1xRTT = (int) TelephonyManager.NETWORK_TYPE_BITMASK_1xRTT;
// 3G
- public static final int RAF_EVDO_0 = TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_0;
- public static final int RAF_EVDO_A = TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_A;
- public static final int RAF_EVDO_B = TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_B;
- public static final int RAF_EHRPD = TelephonyManager.NETWORK_TYPE_BITMASK_EHRPD;
- public static final int RAF_HSUPA = TelephonyManager.NETWORK_TYPE_BITMASK_HSUPA;
- public static final int RAF_HSDPA = TelephonyManager.NETWORK_TYPE_BITMASK_HSDPA;
- public static final int RAF_HSPA = TelephonyManager.NETWORK_TYPE_BITMASK_HSPA;
- public static final int RAF_HSPAP = TelephonyManager.NETWORK_TYPE_BITMASK_HSPAP;
- public static final int RAF_UMTS = TelephonyManager.NETWORK_TYPE_BITMASK_UMTS;
- public static final int RAF_TD_SCDMA = TelephonyManager.NETWORK_TYPE_BITMASK_TD_SCDMA;
+ public static final int RAF_EVDO_0 = (int) TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_0;
+ public static final int RAF_EVDO_A = (int) TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_A;
+ public static final int RAF_EVDO_B = (int) TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_B;
+ public static final int RAF_EHRPD = (int) TelephonyManager.NETWORK_TYPE_BITMASK_EHRPD;
+ public static final int RAF_HSUPA = (int) TelephonyManager.NETWORK_TYPE_BITMASK_HSUPA;
+ public static final int RAF_HSDPA = (int) TelephonyManager.NETWORK_TYPE_BITMASK_HSDPA;
+ public static final int RAF_HSPA = (int) TelephonyManager.NETWORK_TYPE_BITMASK_HSPA;
+ public static final int RAF_HSPAP = (int) TelephonyManager.NETWORK_TYPE_BITMASK_HSPAP;
+ public static final int RAF_UMTS = (int) TelephonyManager.NETWORK_TYPE_BITMASK_UMTS;
+ public static final int RAF_TD_SCDMA = (int) TelephonyManager.NETWORK_TYPE_BITMASK_TD_SCDMA;
// 4G
- public static final int RAF_LTE = TelephonyManager.NETWORK_TYPE_BITMASK_LTE;
- public static final int RAF_LTE_CA = TelephonyManager.NETWORK_TYPE_BITMASK_LTE_CA;
+ public static final int RAF_LTE = (int) TelephonyManager.NETWORK_TYPE_BITMASK_LTE;
+ public static final int RAF_LTE_CA = (int) TelephonyManager.NETWORK_TYPE_BITMASK_LTE_CA;
+
+ // 5G
+ public static final int RAF_NR = (int) TelephonyManager.NETWORK_TYPE_BITMASK_NR;
// Grouping of RAFs
// 2G
@@ -68,6 +76,9 @@ public class RadioAccessFamily implements Parcelable {
// 4G
private static final int LTE = RAF_LTE | RAF_LTE_CA;
+ // 5G
+ private static final int NR = RAF_NR;
+
/* Phone ID of phone */
private int mPhoneId;
@@ -141,103 +152,97 @@ public class RadioAccessFamily implements Parcelable {
/**
* Implement the Parcelable interface.
*/
- public static final Creator<RadioAccessFamily> CREATOR =
- new Creator<RadioAccessFamily>() {
+ public static final Creator<android.telephony.RadioAccessFamily> CREATOR =
+ new Creator<android.telephony.RadioAccessFamily>() {
@Override
- public RadioAccessFamily createFromParcel(Parcel in) {
+ public android.telephony.RadioAccessFamily createFromParcel(Parcel in) {
int phoneId = in.readInt();
int radioAccessFamily = in.readInt();
- return new RadioAccessFamily(phoneId, radioAccessFamily);
+ return new android.telephony.RadioAccessFamily(phoneId, radioAccessFamily);
}
@Override
- public RadioAccessFamily[] newArray(int size) {
- return new RadioAccessFamily[size];
+ public android.telephony.RadioAccessFamily[] newArray(int size) {
+ return new android.telephony.RadioAccessFamily[size];
}
};
@UnsupportedAppUsage
public static int getRafFromNetworkType(int type) {
- int raf;
-
switch (type) {
case RILConstants.NETWORK_MODE_WCDMA_PREF:
- raf = GSM | WCDMA;
- break;
+ return GSM | WCDMA;
case RILConstants.NETWORK_MODE_GSM_ONLY:
- raf = GSM;
- break;
+ return GSM;
case RILConstants.NETWORK_MODE_WCDMA_ONLY:
- raf = WCDMA;
- break;
+ return WCDMA;
case RILConstants.NETWORK_MODE_GSM_UMTS:
- raf = GSM | WCDMA;
- break;
+ return GSM | WCDMA;
case RILConstants.NETWORK_MODE_CDMA:
- raf = CDMA | EVDO;
- break;
+ return CDMA | EVDO;
case RILConstants.NETWORK_MODE_LTE_CDMA_EVDO:
- raf = LTE | CDMA | EVDO;
- break;
+ return LTE | CDMA | EVDO;
case RILConstants.NETWORK_MODE_LTE_GSM_WCDMA:
- raf = LTE | GSM | WCDMA;
- break;
+ return LTE | GSM | WCDMA;
case RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA:
- raf = LTE | CDMA | EVDO | GSM | WCDMA;
- break;
+ return LTE | CDMA | EVDO | GSM | WCDMA;
case RILConstants.NETWORK_MODE_LTE_ONLY:
- raf = LTE;
- break;
+ return LTE;
case RILConstants.NETWORK_MODE_LTE_WCDMA:
- raf = LTE | WCDMA;
- break;
+ return LTE | WCDMA;
case RILConstants.NETWORK_MODE_CDMA_NO_EVDO:
- raf = CDMA;
- break;
+ return CDMA;
case RILConstants.NETWORK_MODE_EVDO_NO_CDMA:
- raf = EVDO;
- break;
+ return EVDO;
case RILConstants.NETWORK_MODE_GLOBAL:
- raf = GSM | WCDMA | CDMA | EVDO;
- break;
+ return GSM | WCDMA | CDMA | EVDO;
case RILConstants.NETWORK_MODE_TDSCDMA_ONLY:
- raf = RAF_TD_SCDMA;
- break;
+ return RAF_TD_SCDMA;
case RILConstants.NETWORK_MODE_TDSCDMA_WCDMA:
- raf = RAF_TD_SCDMA | WCDMA;
- break;
+ return RAF_TD_SCDMA | WCDMA;
case RILConstants.NETWORK_MODE_LTE_TDSCDMA:
- raf = LTE | RAF_TD_SCDMA;
- break;
+ return LTE | RAF_TD_SCDMA;
case RILConstants.NETWORK_MODE_TDSCDMA_GSM:
- raf = RAF_TD_SCDMA | GSM;
- break;
+ return RAF_TD_SCDMA | GSM;
case RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM:
- raf = LTE | RAF_TD_SCDMA | GSM;
- break;
+ return LTE | RAF_TD_SCDMA | GSM;
case RILConstants.NETWORK_MODE_TDSCDMA_GSM_WCDMA:
- raf = RAF_TD_SCDMA | GSM | WCDMA;
- break;
+ return RAF_TD_SCDMA | GSM | WCDMA;
case RILConstants.NETWORK_MODE_LTE_TDSCDMA_WCDMA:
- raf = LTE | RAF_TD_SCDMA | WCDMA;
- break;
+ return LTE | RAF_TD_SCDMA | WCDMA;
case RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA:
- raf = LTE | RAF_TD_SCDMA | GSM | WCDMA;
- break;
+ return LTE | RAF_TD_SCDMA | GSM | WCDMA;
case RILConstants.NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA:
- raf = RAF_TD_SCDMA | CDMA | EVDO | GSM | WCDMA;
- break;
+ return RAF_TD_SCDMA | CDMA | EVDO | GSM | WCDMA;
case RILConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA:
- raf = LTE | RAF_TD_SCDMA | CDMA | EVDO | GSM | WCDMA;
- break;
+ return LTE | RAF_TD_SCDMA | CDMA | EVDO | GSM | WCDMA;
+ case (RILConstants.NETWORK_MODE_NR_ONLY):
+ return NR;
+ case (RILConstants.NETWORK_MODE_NR_LTE):
+ return NR | LTE;
+ case (RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO):
+ return NR | LTE | CDMA | EVDO;
+ case (RILConstants.NETWORK_MODE_NR_LTE_GSM_WCDMA):
+ return NR | LTE | GSM | WCDMA;
+ case (RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA):
+ return NR | LTE | CDMA | EVDO | GSM | WCDMA;
+ case (RILConstants.NETWORK_MODE_NR_LTE_WCDMA):
+ return NR | LTE | WCDMA;
+ case (RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA):
+ return NR | LTE | RAF_TD_SCDMA;
+ case (RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_GSM):
+ return NR | LTE | RAF_TD_SCDMA | GSM;
+ case (RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_WCDMA):
+ return NR | LTE | RAF_TD_SCDMA | WCDMA;
+ case (RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA):
+ return NR | LTE | RAF_TD_SCDMA | GSM | WCDMA;
+ case (RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA):
+ return NR | LTE | RAF_TD_SCDMA | CDMA | EVDO | GSM | WCDMA;
default:
- raf = RAF_UNKNOWN;
- break;
+ return RAF_UNKNOWN;
}
-
- return raf;
}
/**
@@ -250,6 +255,7 @@ public class RadioAccessFamily implements Parcelable {
raf = ((CDMA & raf) > 0) ? (CDMA | raf) : raf;
raf = ((EVDO & raf) > 0) ? (EVDO | raf) : raf;
raf = ((LTE & raf) > 0) ? (LTE | raf) : raf;
+ raf = ((NR & raf) > 0) ? (NR | raf) : raf;
return raf;
}
@@ -274,83 +280,78 @@ public class RadioAccessFamily implements Parcelable {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public static int getNetworkTypeFromRaf(int raf) {
- int type;
-
raf = getAdjustedRaf(raf);
switch (raf) {
case (GSM | WCDMA):
- type = RILConstants.NETWORK_MODE_WCDMA_PREF;
- break;
+ return RILConstants.NETWORK_MODE_WCDMA_PREF;
case GSM:
- type = RILConstants.NETWORK_MODE_GSM_ONLY;
- break;
+ return RILConstants.NETWORK_MODE_GSM_ONLY;
case WCDMA:
- type = RILConstants.NETWORK_MODE_WCDMA_ONLY;
- break;
+ return RILConstants.NETWORK_MODE_WCDMA_ONLY;
case (CDMA | EVDO):
- type = RILConstants.NETWORK_MODE_CDMA;
- break;
+ return RILConstants.NETWORK_MODE_CDMA;
case (LTE | CDMA | EVDO):
- type = RILConstants.NETWORK_MODE_LTE_CDMA_EVDO;
- break;
+ return RILConstants.NETWORK_MODE_LTE_CDMA_EVDO;
case (LTE | GSM | WCDMA):
- type = RILConstants.NETWORK_MODE_LTE_GSM_WCDMA;
- break;
+ return RILConstants.NETWORK_MODE_LTE_GSM_WCDMA;
case (LTE | CDMA | EVDO | GSM | WCDMA):
- type = RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA;
- break;
+ return RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA;
case LTE:
- type = RILConstants.NETWORK_MODE_LTE_ONLY;
- break;
+ return RILConstants.NETWORK_MODE_LTE_ONLY;
case (LTE | WCDMA):
- type = RILConstants.NETWORK_MODE_LTE_WCDMA;
- break;
+ return RILConstants.NETWORK_MODE_LTE_WCDMA;
case CDMA:
- type = RILConstants.NETWORK_MODE_CDMA_NO_EVDO;
- break;
+ return RILConstants.NETWORK_MODE_CDMA_NO_EVDO;
case EVDO:
- type = RILConstants.NETWORK_MODE_EVDO_NO_CDMA;
- break;
+ return RILConstants.NETWORK_MODE_EVDO_NO_CDMA;
case (GSM | WCDMA | CDMA | EVDO):
- type = RILConstants.NETWORK_MODE_GLOBAL;
- break;
+ return RILConstants.NETWORK_MODE_GLOBAL;
case RAF_TD_SCDMA:
- type = RILConstants.NETWORK_MODE_TDSCDMA_ONLY;
- break;
+ return RILConstants.NETWORK_MODE_TDSCDMA_ONLY;
case (RAF_TD_SCDMA | WCDMA):
- type = RILConstants.NETWORK_MODE_TDSCDMA_WCDMA;
- break;
+ return RILConstants.NETWORK_MODE_TDSCDMA_WCDMA;
case (LTE | RAF_TD_SCDMA):
- type = RILConstants.NETWORK_MODE_LTE_TDSCDMA;
- break;
+ return RILConstants.NETWORK_MODE_LTE_TDSCDMA;
case (RAF_TD_SCDMA | GSM):
- type = RILConstants.NETWORK_MODE_TDSCDMA_GSM;
- break;
+ return RILConstants.NETWORK_MODE_TDSCDMA_GSM;
case (LTE | RAF_TD_SCDMA | GSM):
- type = RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM;
- break;
+ return RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM;
case (RAF_TD_SCDMA | GSM | WCDMA):
- type = RILConstants.NETWORK_MODE_TDSCDMA_GSM_WCDMA;
- break;
+ return RILConstants.NETWORK_MODE_TDSCDMA_GSM_WCDMA;
case (LTE | RAF_TD_SCDMA | WCDMA):
- type = RILConstants.NETWORK_MODE_LTE_TDSCDMA_WCDMA;
- break;
+ return RILConstants.NETWORK_MODE_LTE_TDSCDMA_WCDMA;
case (LTE | RAF_TD_SCDMA | GSM | WCDMA):
- type = RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA;
- break;
+ return RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA;
case (RAF_TD_SCDMA | CDMA | EVDO | GSM | WCDMA):
- type = RILConstants.NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
- break;
+ return RILConstants.NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
case (LTE | RAF_TD_SCDMA | CDMA | EVDO | GSM | WCDMA):
- type = RILConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
- break;
+ return RILConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
+ case (NR):
+ return RILConstants.NETWORK_MODE_NR_ONLY;
+ case (NR | LTE):
+ return RILConstants.NETWORK_MODE_NR_LTE;
+ case (NR | LTE | CDMA | EVDO):
+ return RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO;
+ case (NR | LTE | GSM | WCDMA):
+ return RILConstants.NETWORK_MODE_NR_LTE_GSM_WCDMA;
+ case (NR | LTE | CDMA | EVDO | GSM | WCDMA):
+ return RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA;
+ case (NR | LTE | WCDMA):
+ return RILConstants.NETWORK_MODE_NR_LTE_WCDMA;
+ case (NR | LTE | RAF_TD_SCDMA):
+ return RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA;
+ case (NR | LTE | RAF_TD_SCDMA | GSM):
+ return RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_GSM;
+ case (NR | LTE | RAF_TD_SCDMA | WCDMA):
+ return RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_WCDMA;
+ case (NR | LTE | RAF_TD_SCDMA | GSM | WCDMA):
+ return RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA;
+ case (NR | LTE | RAF_TD_SCDMA | CDMA | EVDO | GSM | WCDMA):
+ return RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
default:
- type = RILConstants.PREFERRED_NETWORK_MODE ;
- break;
+ return RILConstants.PREFERRED_NETWORK_MODE;
}
-
- return type;
}
public static int singleRafTypeFromString(String rafString) {
@@ -377,6 +378,7 @@ public class RadioAccessFamily implements Parcelable {
case "EVDO": return EVDO;
case "WCDMA": return WCDMA;
case "LTE_CA": return RAF_LTE_CA;
+ case "NR": return RAF_NR;
default: return RAF_UNKNOWN;
}
}
@@ -394,76 +396,78 @@ public class RadioAccessFamily implements Parcelable {
}
/**
- * convert RAF from {@link ServiceState.RilRadioTechnology} bitmask to
+ * convert RAF from {@link android.hardware.radio.V1_0.RadioAccessFamily} to
* {@link TelephonyManager.NetworkTypeBitMask}, the bitmask represented by
- * {@link TelephonyManager.NetworkType}. Reasons are {@link TelephonyManager.NetworkType} are
- * public while {@link ServiceState.RilRadioTechnology} are hidden. We
- * don't want to expose two sets of definition to public.
+ * {@link TelephonyManager.NetworkType}.
*
- * @param raf bitmask represented by {@link ServiceState.RilRadioTechnology}
+ * @param raf {@link android.hardware.radio.V1_0.RadioAccessFamily}
* @return {@link TelephonyManager.NetworkTypeBitMask}
*/
public static int convertToNetworkTypeBitMask(int raf) {
int networkTypeRaf = 0;
- if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_GSM)) != 0) {
+ if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.GSM) != 0) {
networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_GSM;
}
- if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_GPRS)) != 0) {
+ if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.GPRS) != 0) {
networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_GPRS;
}
- if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_EDGE)) != 0) {
+ if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.EDGE) != 0) {
networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_EDGE;
}
// convert both IS95A/IS95B to CDMA as network mode doesn't support CDMA
- if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_IS95A)) != 0) {
+ if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.IS95A) != 0) {
networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_CDMA;
}
- if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_IS95B)) != 0) {
+ if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.IS95B) != 0) {
networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_CDMA;
}
- if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT)) != 0) {
+ if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.ONE_X_RTT) != 0) {
networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_1xRTT;
}
- if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0)) != 0) {
+ if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.EVDO_0) != 0) {
networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_0;
}
- if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A)) != 0) {
+ if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.EVDO_A) != 0) {
networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_A;
}
- if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_B)) != 0) {
+ if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.EVDO_B) != 0) {
networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_EVDO_B;
}
- if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD)) != 0) {
+ if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.EHRPD) != 0) {
networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_EHRPD;
}
- if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_HSUPA)) != 0) {
+ if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.HSUPA) != 0) {
networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_HSUPA;
}
- if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_HSDPA)) != 0) {
+ if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.HSDPA) != 0) {
networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_HSDPA;
}
- if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_HSPA)) != 0) {
+ if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.HSPA) != 0) {
networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_HSPA;
}
- if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_HSPAP)) != 0) {
+ if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.HSPAP) != 0) {
networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_HSPAP;
}
- if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_UMTS)) != 0) {
+ if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.UMTS) != 0) {
networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_UMTS;
}
- if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_TD_SCDMA)) != 0) {
+ if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.TD_SCDMA) != 0) {
networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_TD_SCDMA;
}
- if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_LTE)) != 0) {
+ if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.LTE) != 0) {
networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_LTE;
}
- if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA)) != 0) {
+ if ((raf & android.hardware.radio.V1_0.RadioAccessFamily.LTE_CA) != 0) {
networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_LTE_CA;
}
- if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_NR)) != 0) {
+ if ((raf & android.hardware.radio.V1_4.RadioAccessFamily.NR) != 0) {
networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_NR;
}
+ // TODO: need hal definition
+ if ((raf & (1 << ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN)) != 0) {
+ networkTypeRaf |= TelephonyManager.NETWORK_TYPE_BITMASK_IWLAN;
+ }
return (networkTypeRaf == 0) ? TelephonyManager.NETWORK_TYPE_UNKNOWN : networkTypeRaf;
}
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 33178766f3a3..a1aee6d8217f 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -36,6 +36,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
+import java.util.stream.Collectors;
/**
* Contains phone state and service related information.
@@ -887,6 +888,24 @@ public class ServiceState implements Parcelable {
}
/**
+ * Convert roaming type to string
+ *
+ * @param roamingType roaming type
+ * @return The roaming type in string format
+ *
+ * @hide
+ */
+ public static String roamingTypeToString(@RoamingType int roamingType) {
+ switch (roamingType) {
+ case ROAMING_TYPE_NOT_ROAMING: return "NOT_ROAMING";
+ case ROAMING_TYPE_UNKNOWN: return "UNKNOWN";
+ case ROAMING_TYPE_DOMESTIC: return "DOMESTIC";
+ case ROAMING_TYPE_INTERNATIONAL: return "INTERNATIONAL";
+ }
+ return "Unknown roaming type " + roamingType;
+ }
+
+ /**
* Convert radio technology to String
*
* @param rt radioTechnology
@@ -1867,4 +1886,29 @@ public class ServiceState implements Parcelable {
? range1
: range2;
}
+
+ /**
+ * Returns a copy of self with location-identifying information removed.
+ * Always clears the NetworkRegistrationState's CellIdentity fields, but if removeCoarseLocation
+ * is true, clears other info as well.
+ * @hide
+ */
+ public ServiceState sanitizeLocationInfo(boolean removeCoarseLocation) {
+ ServiceState state = new ServiceState(this);
+ if (state.mNetworkRegistrationStates != null) {
+ state.mNetworkRegistrationStates = state.mNetworkRegistrationStates.stream()
+ .map(NetworkRegistrationState::sanitizeLocationInfo)
+ .collect(Collectors.toList());
+ }
+ if (!removeCoarseLocation) return state;
+
+ state.mDataOperatorAlphaLong = null;
+ state.mDataOperatorAlphaShort = null;
+ state.mDataOperatorNumeric = null;
+ state.mVoiceOperatorAlphaLong = null;
+ state.mVoiceOperatorAlphaShort = null;
+ state.mVoiceOperatorNumeric = null;
+
+ return state;
+ }
}
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index 91375bc2f11e..d2ae106b5545 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -85,6 +85,7 @@ public class SignalStrength implements Parcelable {
CellSignalStrengthWcdma mWcdma;
CellSignalStrengthTdscdma mTdscdma;
CellSignalStrengthLte mLte;
+ CellSignalStrengthNr mNr;
/**
* Create a new SignalStrength from a intent notifier Bundle
@@ -116,7 +117,7 @@ public class SignalStrength implements Parcelable {
public SignalStrength() {
this(new CellSignalStrengthCdma(), new CellSignalStrengthGsm(),
new CellSignalStrengthWcdma(), new CellSignalStrengthTdscdma(),
- new CellSignalStrengthLte());
+ new CellSignalStrengthLte(), new CellSignalStrengthNr());
}
/**
@@ -129,12 +130,14 @@ public class SignalStrength implements Parcelable {
@NonNull CellSignalStrengthGsm gsm,
@NonNull CellSignalStrengthWcdma wcdma,
@NonNull CellSignalStrengthTdscdma tdscdma,
- @NonNull CellSignalStrengthLte lte) {
+ @NonNull CellSignalStrengthLte lte,
+ @NonNull CellSignalStrengthNr nr) {
mCdma = cdma;
mGsm = gsm;
mWcdma = wcdma;
mTdscdma = tdscdma;
mLte = lte;
+ mNr = nr;
}
/**
@@ -147,7 +150,8 @@ public class SignalStrength implements Parcelable {
new CellSignalStrengthGsm(signalStrength.gw),
new CellSignalStrengthWcdma(),
new CellSignalStrengthTdscdma(signalStrength.tdScdma),
- new CellSignalStrengthLte(signalStrength.lte));
+ new CellSignalStrengthLte(signalStrength.lte),
+ new CellSignalStrengthNr());
}
/**
@@ -160,7 +164,23 @@ public class SignalStrength implements Parcelable {
new CellSignalStrengthGsm(signalStrength.gsm),
new CellSignalStrengthWcdma(signalStrength.wcdma),
new CellSignalStrengthTdscdma(signalStrength.tdScdma),
- new CellSignalStrengthLte(signalStrength.lte));
+ new CellSignalStrengthLte(signalStrength.lte),
+ new CellSignalStrengthNr());
+ }
+
+ /**
+ * Constructor for Radio HAL V1.4.
+ *
+ * @param signalStrength signal strength reported from modem.
+ * @hide
+ */
+ public SignalStrength(android.hardware.radio.V1_4.SignalStrength signalStrength) {
+ this(new CellSignalStrengthCdma(signalStrength.cdma, signalStrength.evdo),
+ new CellSignalStrengthGsm(signalStrength.gsm),
+ new CellSignalStrengthWcdma(signalStrength.wcdma),
+ new CellSignalStrengthTdscdma(signalStrength.tdscdma),
+ new CellSignalStrengthLte(signalStrength.lte),
+ new CellSignalStrengthNr(signalStrength.nr));
}
private CellSignalStrength getPrimary() {
@@ -171,6 +191,7 @@ public class SignalStrength implements Parcelable {
if (mTdscdma.isValid()) return mTdscdma;
if (mWcdma.isValid()) return mWcdma;
if (mGsm.isValid()) return mGsm;
+ if (mNr.isValid()) return mNr;
return mLte;
}
@@ -200,6 +221,7 @@ public class SignalStrength implements Parcelable {
if (mTdscdma.isValid()) cssList.add(mTdscdma);
if (mWcdma.isValid()) cssList.add(mWcdma);
if (mGsm.isValid()) cssList.add(mGsm);
+ if (mNr.isValid()) cssList.add(mNr);
return cssList;
}
@@ -210,6 +232,7 @@ public class SignalStrength implements Parcelable {
mWcdma.updateLevel(cc, ss);
mTdscdma.updateLevel(cc, ss);
mLte.updateLevel(cc, ss);
+ mNr.updateLevel(cc, ss);
}
/**
@@ -234,6 +257,7 @@ public class SignalStrength implements Parcelable {
mWcdma = new CellSignalStrengthWcdma(s.mWcdma);
mTdscdma = new CellSignalStrengthTdscdma(s.mTdscdma);
mLte = new CellSignalStrengthLte(s.mLte);
+ mNr = new CellSignalStrengthNr(s.mNr);
}
/**
@@ -250,6 +274,7 @@ public class SignalStrength implements Parcelable {
mWcdma = in.readParcelable(CellSignalStrengthWcdma.class.getClassLoader());
mTdscdma = in.readParcelable(CellSignalStrengthTdscdma.class.getClassLoader());
mLte = in.readParcelable(CellSignalStrengthLte.class.getClassLoader());
+ mNr = in.readParcelable(CellSignalStrengthLte.class.getClassLoader());
}
/**
@@ -261,6 +286,7 @@ public class SignalStrength implements Parcelable {
out.writeParcelable(mWcdma, flags);
out.writeParcelable(mTdscdma, flags);
out.writeParcelable(mLte, flags);
+ out.writeParcelable(mNr, flags);
}
/**
@@ -814,7 +840,7 @@ public class SignalStrength implements Parcelable {
*/
@Override
public int hashCode() {
- return Objects.hash(mCdma, mGsm, mWcdma, mTdscdma, mLte);
+ return Objects.hash(mCdma, mGsm, mWcdma, mTdscdma, mLte, mNr);
}
/**
@@ -830,7 +856,8 @@ public class SignalStrength implements Parcelable {
&& mGsm.equals(s.mGsm)
&& mWcdma.equals(s.mWcdma)
&& mTdscdma.equals(s.mTdscdma)
- && mLte.equals(s.mLte);
+ && mLte.equals(s.mLte)
+ && mNr.equals(s.mNr);
}
/**
@@ -844,6 +871,7 @@ public class SignalStrength implements Parcelable {
.append(",mWcdma=").append(mWcdma)
.append(",mTdscdma=").append(mTdscdma)
.append(",mLte=").append(mLte)
+ .append(",mNr=").append(mNr)
.append(",primary=").append(getPrimary().getClass().getSimpleName())
.append("}")
.toString();
@@ -866,6 +894,7 @@ public class SignalStrength implements Parcelable {
mWcdma = m.getParcelable("Wcdma");
mTdscdma = m.getParcelable("Tdscdma");
mLte = m.getParcelable("Lte");
+ mNr = m.getParcelable("Nr");
}
/**
@@ -885,6 +914,7 @@ public class SignalStrength implements Parcelable {
m.putParcelable("Wcdma", mWcdma);
m.putParcelable("Tdscdma", mTdscdma);
m.putParcelable("Lte", mLte);
+ m.putParcelable("Nr", mNr);
}
/**
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index 91e24a955013..2a73a5cdef54 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -187,15 +187,7 @@ public class SmsMessage {
int activePhone = TelephonyManager.getDefault().getCurrentPhoneType();
String format = (PHONE_TYPE_CDMA == activePhone) ?
SmsConstants.FORMAT_3GPP2 : SmsConstants.FORMAT_3GPP;
- message = createFromPdu(pdu, format);
-
- if (null == message || null == message.mWrappedSmsMessage) {
- // decoding pdu failed based on activePhone type, must be other format
- format = (PHONE_TYPE_CDMA == activePhone) ?
- SmsConstants.FORMAT_3GPP : SmsConstants.FORMAT_3GPP2;
- message = createFromPdu(pdu, format);
- }
- return message;
+ return createFromPdu(pdu, format);
}
/**
@@ -211,11 +203,18 @@ public class SmsMessage {
* {@link android.provider.Telephony.Sms.Intents#SMS_RECEIVED_ACTION} intent
*/
public static SmsMessage createFromPdu(byte[] pdu, String format) {
- SmsMessageBase wrappedMessage;
+ return createFromPdu(pdu, format, true);
+ }
+
+ private static SmsMessage createFromPdu(byte[] pdu, String format,
+ boolean fallbackToOtherFormat) {
if (pdu == null) {
Rlog.i(LOG_TAG, "createFromPdu(): pdu is null");
return null;
}
+ SmsMessageBase wrappedMessage;
+ String otherFormat = SmsConstants.FORMAT_3GPP2.equals(format) ? SmsConstants.FORMAT_3GPP :
+ SmsConstants.FORMAT_3GPP2;
if (SmsConstants.FORMAT_3GPP2.equals(format)) {
wrappedMessage = com.android.internal.telephony.cdma.SmsMessage.createFromPdu(pdu);
} else if (SmsConstants.FORMAT_3GPP.equals(format)) {
@@ -228,8 +227,12 @@ public class SmsMessage {
if (wrappedMessage != null) {
return new SmsMessage(wrappedMessage);
} else {
- Rlog.e(LOG_TAG, "createFromPdu(): wrappedMessage is null");
- return null;
+ if (fallbackToOtherFormat) {
+ return createFromPdu(pdu, otherFormat, false);
+ } else {
+ Rlog.e(LOG_TAG, "createFromPdu(): wrappedMessage is null");
+ return null;
+ }
}
}
@@ -978,4 +981,13 @@ public class SmsMessage {
return false;
}
+
+ /**
+ * {@hide}
+ * Returns the recipient address(receiver) of this SMS message in String form or null if
+ * unavailable.
+ */
+ public String getRecipientAddress() {
+ return mWrappedSmsMessage.getRecipientAddress();
+ }
}
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 845d23ee4cc2..5b9e23228dcb 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -865,7 +865,8 @@ public class SubscriptionManager {
}
/**
- * Callback invoked when there is any change to any SubscriptionInfo. Typically
+ * Callback invoked when there is any change to any SubscriptionInfo, as well as once on
+ * registering for changes with {@link #addOnSubscriptionsChangedListener}. Typically
* this method would invoke {@link #getActiveSubscriptionInfoList}
*/
public void onSubscriptionsChanged() {
@@ -917,7 +918,9 @@ public class SubscriptionManager {
/**
* Register for changes to the list of active {@link SubscriptionInfo} records or to the
* individual records themselves. When a change occurs the onSubscriptionsChanged method of
- * the listener will be invoked immediately if there has been a notification.
+ * the listener will be invoked immediately if there has been a notification. The
+ * onSubscriptionChanged method will also be triggered once initially when calling this
+ * function.
*
* @param listener an instance of {@link OnSubscriptionsChangedListener} with
* onSubscriptionsChanged overridden.
@@ -1860,7 +1863,7 @@ public class SubscriptionManager {
iSub.setDefaultSmsSubId(subscriptionId);
}
} catch (RemoteException ex) {
- // ignore it
+ ex.rethrowFromSystemServer();
}
}
@@ -2048,6 +2051,8 @@ public class SubscriptionManager {
putPhoneIdAndSubIdExtra(intent, phoneId, subIds[0]);
} else {
logd("putPhoneIdAndSubIdExtra: no valid subs");
+ intent.putExtra(PhoneConstants.PHONE_KEY, phoneId);
+ intent.putExtra(PhoneConstants.SLOT_KEY, phoneId);
}
}
@@ -2860,6 +2865,95 @@ public class SubscriptionManager {
}
}
+ /**
+ * Enabled or disable a subscription. This is currently used in the settings page.
+ *
+ * <p>
+ * Permissions android.Manifest.permission.MODIFY_PHONE_STATE is required
+ *
+ * @param enable whether user is turning it on or off.
+ * @param subscriptionId Subscription to be enabled or disabled.
+ * It could be a eSIM or pSIM subscription.
+ *
+ * @return whether the operation is successful.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public boolean setSubscriptionEnabled(int subscriptionId, boolean enable) {
+ if (VDBG) {
+ logd("setSubscriptionActivated subId= " + subscriptionId + " enable " + enable);
+ }
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ return iSub.setSubscriptionEnabled(enable, subscriptionId);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return false;
+ }
+
+ /**
+ * Returns whether the subscription is enabled or not. This is different from activated
+ * or deactivated for two aspects. 1) For when user disables a physical subscription, we
+ * actually disable the modem because we can't switch off the subscription. 2) For eSIM,
+ * user may enable one subscription but the system may activate another temporarily. In this
+ * case, user enabled one is different from current active one.
+
+ * @param subscriptionId The subscription it asks about.
+ * @return whether it's enabled or not. {@code true} if user set this subscription enabled
+ * earlier, or user never set subscription enable / disable on this slot explicitly, and
+ * this subscription is currently active. Otherwise, it returns {@code false}.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public boolean isSubscriptionEnabled(int subscriptionId) {
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ return iSub.isSubscriptionEnabled(subscriptionId);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return false;
+ }
+
+ /**
+ * Get which subscription is enabled on this slot. See {@link #isSubscriptionEnabled(int)}
+ * for more details.
+ *
+ * @param slotIndex which slot it asks about.
+ * @return which subscription is enabled on this slot. If there's no enabled subscription
+ * in this slot, it will return {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public int getEnabledSubscriptionId(int slotIndex) {
+ int subId = INVALID_SUBSCRIPTION_ID;
+
+ try {
+ ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
+ if (iSub != null) {
+ subId = iSub.getEnabledSubscriptionId(slotIndex);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ if (VDBG) logd("getEnabledSubscriptionId, subId = " + subId);
+ return subId;
+ }
+
private interface CallISubMethodHelper {
int callMethod(ISub iSub) throws RemoteException;
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index d8add3c2752e..1433b2ac9280 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -23,6 +23,7 @@ import static com.android.internal.util.Preconditions.checkNotNull;
import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
+import android.annotation.LongDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -71,6 +72,7 @@ import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.text.TextUtils;
import android.util.Log;
+import android.util.Pair;
import com.android.ims.internal.IImsServiceFeatureCallback;
import com.android.internal.annotations.VisibleForTesting;
@@ -227,10 +229,19 @@ public class TelephonyManager {
public static final int SRVCC_STATE_HANDOVER_CANCELED = 3;
/**
- * An invalid UICC card identifier. See {@link #getCardIdForDefaultEuicc()} and
- * {@link UiccCardInfo#getCardId()}.
+ * A UICC card identifier used if the device does not support the operation.
+ * For example, {@link #getCardIdForDefaultEuicc()} returns this value if the device has no
+ * eUICC, or the eUICC cannot be read.
*/
- public static final int INVALID_CARD_ID = -1;
+ public static final int UNSUPPORTED_CARD_ID = -1;
+
+ /**
+ * A UICC card identifier used before the UICC card is loaded. See
+ * {@link #getCardIdForDefaultEuicc()} and {@link UiccCardInfo#getCardId()}.
+ * <p>
+ * Note that once the UICC card is loaded, the card ID may become {@link #UNSUPPORTED_CARD_ID}.
+ */
+ public static final int UNINITIALIZED_CARD_ID = -2;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@@ -349,41 +360,30 @@ public class TelephonyManager {
* Returns 0 if none of voice, sms, data is not supported
* Returns 1 for Single standby mode (Single SIM functionality)
* Returns 2 for Dual standby mode.(Dual SIM functionality)
+ * Returns 3 for Tri standby mode.(Tri SIM functionality)
*/
public int getPhoneCount() {
- int phoneCount = 1;
- switch (getMultiSimConfiguration()) {
- case UNKNOWN:
- // if voice or sms or data is supported, return 1 otherwise 0
- if (isVoiceCapable() || isSmsCapable()) {
- phoneCount = 1;
- } else {
- // todo: try to clean this up further by getting rid of the nested conditions
- if (mContext == null) {
- phoneCount = 1;
- } else {
- // check for data support
- ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(
- Context.CONNECTIVITY_SERVICE);
- if (cm == null) {
- phoneCount = 1;
- } else {
- if (cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)) {
- phoneCount = 1;
- } else {
- phoneCount = 0;
- }
- }
- }
+ int phoneCount = 0;
+
+ // check for voice and data support, 0 if not supported
+ if (!isVoiceCapable() && !isSmsCapable()) {
+ ConnectivityManager cm = (ConnectivityManager) mContext.getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ if (cm != null) {
+ if (!cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)) {
+ return phoneCount;
}
- break;
- case DSDS:
- case DSDA:
- phoneCount = PhoneConstants.MAX_PHONE_COUNT_DUAL_SIM;
- break;
- case TSTS:
- phoneCount = PhoneConstants.MAX_PHONE_COUNT_TRI_SIM;
- break;
+ }
+ }
+
+ phoneCount = 1;
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ phoneCount = telephony.getNumOfActiveSims();
+ }
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "getNumOfActiveSims RemoteException", ex);
}
return phoneCount;
}
@@ -1352,6 +1352,38 @@ public class TelephonyManager {
@SystemApi
public static final long MAX_NUMBER_VERIFICATION_TIMEOUT_MILLIS = 60000;
+ /**
+ * Intent sent when an error occurs that debug tools should log and possibly take further
+ * action such as capturing vendor-specific logs.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public static final String ACTION_DEBUG_EVENT = "android.telephony.action.DEBUG_EVENT";
+
+ /**
+ * An arbitrary ParcelUuid which should be consistent for each occurrence of the same event.
+ *
+ * This field must be included in all events.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_DEBUG_EVENT_ID = "android.telephony.extra.DEBUG_EVENT_ID";
+
+ /**
+ * A freeform string description of the event.
+ *
+ * This field is optional for all events and as a guideline should not exceed 80 characters
+ * and should be as short as possible to convey the essence of the event.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String EXTRA_DEBUG_EVENT_DESCRIPTION =
+ "android.telephony.extra.DEBUG_EVENT_DESCRIPTION";
+
//
//
// Device Info
@@ -1632,10 +1664,7 @@ public class TelephonyManager {
* @deprecated use {@link #getAllCellInfo} instead, which returns a superset of this API.
*/
@Deprecated
- @RequiresPermission(anyOf = {
- android.Manifest.permission.ACCESS_COARSE_LOCATION,
- android.Manifest.permission.ACCESS_FINE_LOCATION
- })
+ @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
public CellLocation getCellLocation() {
try {
ITelephony telephony = getITelephony();
@@ -3110,24 +3139,25 @@ public class TelephonyManager {
}
/**
- * Get the card ID of the default eUICC card. If there is no eUICC, returns
- * {@link #INVALID_CARD_ID}.
+ * Get the card ID of the default eUICC card. If the eUICCs have not yet been loaded, returns
+ * {@link #UNINITIALIZED_CARD_ID}. If there is no eUICC or the device does not support card IDs
+ * for eUICCs, returns {@link #UNSUPPORTED_CARD_ID}.
*
* <p>The card ID is a unique identifier associated with a UICC or eUICC card. Card IDs are
* unique to a device, and always refer to the same UICC or eUICC card unless the device goes
* through a factory reset.
*
- * @return card ID of the default eUICC card.
+ * @return card ID of the default eUICC card, if loaded.
*/
public int getCardIdForDefaultEuicc() {
try {
ITelephony telephony = getITelephony();
if (telephony == null) {
- return INVALID_CARD_ID;
+ return UNINITIALIZED_CARD_ID;
}
return telephony.getCardIdForDefaultEuicc(mSubId, mContext.getOpPackageName());
} catch (RemoteException e) {
- return INVALID_CARD_ID;
+ return UNINITIALIZED_CARD_ID;
}
}
@@ -3230,6 +3260,35 @@ public class TelephonyManager {
}
}
+ /**
+ * Get the mapping from logical slots to physical slots. The mapping represent by a pair list.
+ * The key of the piar is the logical slot id and the value of the pair is the physical
+ * slots id mapped to this logical slot id.
+ *
+ * @return an pair list indicates the mapping from logical slots to physical slots. The size of
+ * the list should be {@link #getPhoneCount()} if success, otherwise return an empty list.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @NonNull
+ public List<Pair<Integer, Integer>> getLogicalToPhysicalSlotMapping() {
+ List<Pair<Integer, Integer>> slotMapping = new ArrayList<>();
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ int[] slotMappingArray = telephony.getSlotsMapping();
+ for (int i = 0; i < slotMappingArray.length; i++) {
+ slotMapping.add(new Pair(i, slotMappingArray[i]));
+ }
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "getSlotsMapping RemoteException", e);
+ }
+ return slotMapping;
+ }
+
//
//
// Subscriber Info
@@ -4845,12 +4904,13 @@ public class TelephonyManager {
*
* <p>Apps targeting {@link android.os.Build.VERSION_CODES#Q Android Q} or higher will no
* longer trigger a refresh of the cached CellInfo by invoking this API. Instead, those apps
- * will receive the latest cached results. Apps targeting
+ * will receive the latest cached results, which may not be current. Apps targeting
* {@link android.os.Build.VERSION_CODES#Q Android Q} or higher that wish to request updated
* CellInfo should call
- * {android.telephony.TelephonyManager#requestCellInfoUpdate requestCellInfoUpdate()} and
- * listen for responses via {@link android.telephony.PhoneStateListener#onCellInfoChanged
- * onCellInfoChanged()}.
+ * {@link android.telephony.TelephonyManager#requestCellInfoUpdate requestCellInfoUpdate()};
+ * however, in all cases, updates will be rate-limited and are not guaranteed. To determine the
+ * recency of CellInfo data, callers should check
+ * {@link android.telephony.CellInfo#getTimeStamp CellInfo#getTimeStamp()}.
*
* <p>This method returns valid data for devices with
* {@link android.content.pm.PackageManager#FEATURE_TELEPHONY FEATURE_TELEPHONY}. In cases
@@ -4865,7 +4925,7 @@ public class TelephonyManager {
* @return List of {@link android.telephony.CellInfo}; null if cell
* information is unavailable.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
+ @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
public List<CellInfo> getAllCellInfo() {
try {
ITelephony telephony = getITelephony();
@@ -4942,7 +5002,7 @@ public class TelephonyManager {
* @param executor the executor on which callback will be invoked.
* @param callback a callback to receive CellInfo.
*/
- @RequiresPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION)
+ @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
public void requestCellInfoUpdate(
@NonNull @CallbackExecutor Executor executor, @NonNull CellInfoCallback callback) {
try {
@@ -4981,7 +5041,7 @@ public class TelephonyManager {
* @hide
*/
@SystemApi
- @RequiresPermission(allOf = {android.Manifest.permission.ACCESS_COARSE_LOCATION,
+ @RequiresPermission(allOf = {android.Manifest.permission.ACCESS_FINE_LOCATION,
android.Manifest.permission.MODIFY_PHONE_STATE})
public void requestCellInfoUpdate(@NonNull WorkSource workSource,
@NonNull @CallbackExecutor Executor executor, @NonNull CellInfoCallback callback) {
@@ -6247,197 +6307,258 @@ public class TelephonyManager {
NETWORK_MODE_LTE_TDSCDMA_WCDMA,
NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA,
NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA,
- NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA
+ NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA,
+ NETWORK_MODE_NR_ONLY,
+ NETWORK_MODE_NR_LTE,
+ NETWORK_MODE_NR_LTE_CDMA_EVDO,
+ NETWORK_MODE_NR_LTE_GSM_WCDMA,
+ NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA,
+ NETWORK_MODE_NR_LTE_WCDMA,
+ NETWORK_MODE_NR_LTE_TDSCDMA,
+ NETWORK_MODE_NR_LTE_TDSCDMA_GSM,
+ NETWORK_MODE_NR_LTE_TDSCDMA_WCDMA,
+ NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA,
+ NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA
})
@Retention(RetentionPolicy.SOURCE)
public @interface PrefNetworkMode{}
/**
- * network mode is GSM/WCDMA (WCDMA preferred).
+ * Preferred network mode is GSM/WCDMA (WCDMA preferred).
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_WCDMA_PREF = RILConstants.NETWORK_MODE_WCDMA_PREF;
/**
- * network mode is GSM only.
+ * Preferred network mode is GSM only.
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_GSM_ONLY = RILConstants.NETWORK_MODE_GSM_ONLY;
/**
- * network mode is WCDMA only.
+ * Preferred network mode is WCDMA only.
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_WCDMA_ONLY = RILConstants.NETWORK_MODE_WCDMA_ONLY;
/**
- * network mode is GSM/WCDMA (auto mode, according to PRL).
+ * Preferred network mode is GSM/WCDMA (auto mode, according to PRL).
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_GSM_UMTS = RILConstants.NETWORK_MODE_GSM_UMTS;
/**
- * network mode is CDMA and EvDo (auto mode, according to PRL).
+ * Preferred network mode is CDMA and EvDo (auto mode, according to PRL).
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_CDMA_EVDO = RILConstants.NETWORK_MODE_CDMA;
/**
- * network mode is CDMA only.
+ * Preferred network mode is CDMA only.
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_CDMA_NO_EVDO = RILConstants.NETWORK_MODE_CDMA_NO_EVDO;
/**
- * network mode is EvDo only.
+ * Preferred network mode is EvDo only.
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_EVDO_NO_CDMA = RILConstants.NETWORK_MODE_EVDO_NO_CDMA;
/**
- * network mode is GSM/WCDMA, CDMA, and EvDo (auto mode, according to PRL).
+ * Preferred network mode is GSM/WCDMA, CDMA, and EvDo (auto mode, according to PRL).
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_GLOBAL = RILConstants.NETWORK_MODE_GLOBAL;
/**
- * network mode is LTE, CDMA and EvDo.
+ * Preferred network mode is LTE, CDMA and EvDo.
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_LTE_CDMA_EVDO = RILConstants.NETWORK_MODE_LTE_CDMA_EVDO;
/**
- * preferred network mode is LTE, GSM/WCDMA.
+ * Preferred network mode is LTE, GSM/WCDMA.
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_LTE_GSM_WCDMA = RILConstants.NETWORK_MODE_LTE_GSM_WCDMA;
/**
- * network mode is LTE, CDMA, EvDo, GSM/WCDMA.
+ * Preferred network mode is LTE, CDMA, EvDo, GSM/WCDMA.
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA =
RILConstants.NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA;
/**
- * network mode is LTE Only.
+ * Preferred network mode is LTE Only.
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_LTE_ONLY = RILConstants.NETWORK_MODE_LTE_ONLY;
/**
- * network mode is LTE/WCDMA.
+ * Preferred network mode is LTE/WCDMA.
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_LTE_WCDMA = RILConstants.NETWORK_MODE_LTE_WCDMA;
/**
- * network mode is TD-SCDMA only.
+ * Preferred network mode is TD-SCDMA only.
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_TDSCDMA_ONLY = RILConstants.NETWORK_MODE_TDSCDMA_ONLY;
/**
- * network mode is TD-SCDMA and WCDMA.
+ * Preferred network mode is TD-SCDMA and WCDMA.
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_TDSCDMA_WCDMA = RILConstants.NETWORK_MODE_TDSCDMA_WCDMA;
/**
- * network mode is TD-SCDMA and LTE.
+ * Preferred network mode is TD-SCDMA and LTE.
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_LTE_TDSCDMA = RILConstants.NETWORK_MODE_LTE_TDSCDMA;
/**
- * network mode is TD-SCDMA and GSM.
+ * Preferred network mode is TD-SCDMA and GSM.
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_TDSCDMA_GSM = RILConstants.NETWORK_MODE_TDSCDMA_GSM;
/**
- * network mode is TD-SCDMA,GSM and LTE.
+ * Preferred network mode is TD-SCDMA,GSM and LTE.
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_LTE_TDSCDMA_GSM =
RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM;
/**
- * network mode is TD-SCDMA, GSM/WCDMA.
+ * Preferred network mode is TD-SCDMA, GSM/WCDMA.
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_TDSCDMA_GSM_WCDMA =
RILConstants.NETWORK_MODE_TDSCDMA_GSM_WCDMA;
/**
- * network mode is TD-SCDMA, WCDMA and LTE.
+ * Preferred network mode is TD-SCDMA, WCDMA and LTE.
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_LTE_TDSCDMA_WCDMA =
RILConstants.NETWORK_MODE_LTE_TDSCDMA_WCDMA;
/**
- * network mode is TD-SCDMA, GSM/WCDMA and LTE.
+ * Preferred network mode is TD-SCDMA, GSM/WCDMA and LTE.
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA =
RILConstants.NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA;
/**
- * network mode is TD-SCDMA,EvDo,CDMA,GSM/WCDMA.
+ * Preferred network mode is TD-SCDMA,EvDo,CDMA,GSM/WCDMA.
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA =
RILConstants.NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
-
/**
- * network mode is TD-SCDMA/LTE/GSM/WCDMA, CDMA, and EvDo.
+ * Preferred network mode is TD-SCDMA/LTE/GSM/WCDMA, CDMA, and EvDo.
* @hide
*/
- @SystemApi
public static final int NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA =
RILConstants.NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
/**
+ * Preferred network mode is NR 5G only.
+ * @hide
+ */
+ public static final int NETWORK_MODE_NR_ONLY = RILConstants.NETWORK_MODE_NR_ONLY;
+
+ /**
+ * Preferred network mode is NR 5G, LTE.
+ * @hide
+ */
+ public static final int NETWORK_MODE_NR_LTE = RILConstants.NETWORK_MODE_NR_LTE;
+
+ /**
+ * Preferred network mode is NR 5G, LTE, CDMA and EvDo.
+ * @hide
+ */
+ public static final int NETWORK_MODE_NR_LTE_CDMA_EVDO =
+ RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO;
+
+ /**
+ * Preferred network mode is NR 5G, LTE, GSM and WCDMA.
+ * @hide
+ */
+ public static final int NETWORK_MODE_NR_LTE_GSM_WCDMA =
+ RILConstants.NETWORK_MODE_NR_LTE_GSM_WCDMA;
+
+ /**
+ * Preferred network mode is NR 5G, LTE, CDMA, EvDo, GSM and WCDMA.
+ * @hide
+ */
+ public static final int NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA =
+ RILConstants.NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA;
+
+ /**
+ * Preferred network mode is NR 5G, LTE and WCDMA.
+ * @hide
+ */
+ public static final int NETWORK_MODE_NR_LTE_WCDMA = RILConstants.NETWORK_MODE_NR_LTE_WCDMA;
+
+ /**
+ * Preferred network mode is NR 5G, LTE and TDSCDMA.
+ * @hide
+ */
+ public static final int NETWORK_MODE_NR_LTE_TDSCDMA = RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA;
+
+ /**
+ * Preferred network mode is NR 5G, LTE, TD-SCDMA and GSM.
+ * @hide
+ */
+ public static final int NETWORK_MODE_NR_LTE_TDSCDMA_GSM =
+ RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_GSM;
+
+ /**
+ * Preferred network mode is NR 5G, LTE, TD-SCDMA, WCDMA.
+ * @hide
+ */
+ public static final int NETWORK_MODE_NR_LTE_TDSCDMA_WCDMA =
+ RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_WCDMA;
+
+ /**
+ * Preferred network mode is NR 5G, LTE, TD-SCDMA, GSM and WCDMA.
+ * @hide
+ */
+ public static final int NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA =
+ RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA;
+
+ /**
+ * Preferred network mode is NR 5G, LTE, TD-SCDMA, CDMA, EVDO, GSM and WCDMA.
+ * @hide
+ */
+ public static final int NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA =
+ RILConstants.NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA;
+
+ /**
* Get the preferred network type.
* Used for device configuration by some CDMA operators.
*
* <p>Requires Permission:
- * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+ * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
* app has carrier privileges (see {@link #hasCarrierPrivileges}).
*
* @return the preferred network type.
* @hide
*/
- @RequiresPermission((android.Manifest.permission.MODIFY_PHONE_STATE))
- @SystemApi
+ @RequiresPermission((android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE))
+ @UnsupportedAppUsage
public @PrefNetworkMode int getPreferredNetworkType(int subId) {
try {
ITelephony telephony = getITelephony();
- if (telephony != null)
+ if (telephony != null) {
return telephony.getPreferredNetworkType(subId);
+ }
} catch (RemoteException ex) {
Rlog.e(TAG, "getPreferredNetworkType RemoteException", ex);
} catch (NullPointerException ex) {
@@ -6447,6 +6568,37 @@ public class TelephonyManager {
}
/**
+ * Get the preferred network type bitmap.
+ *
+ * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+ * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
+ * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ *
+ * @return The bitmap of preferred network types.
+ *
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @SystemApi
+ public @NetworkTypeBitMask long getPreferredNetworkTypeBitmap() {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return (long) RadioAccessFamily.getRafFromNetworkType(
+ telephony.getPreferredNetworkType(getSubId()));
+ }
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "getPreferredNetworkTypeBitmap RemoteException", ex);
+ } catch (NullPointerException ex) {
+ Rlog.e(TAG, "getPreferredNetworkTypeBitmap NPE", ex);
+ }
+ return 0;
+ }
+
+ /**
* Sets the network selection mode to automatic.
*
* <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
@@ -6479,9 +6631,10 @@ public class TelephonyManager {
*
* <p> Note that this scan can take a long time (sometimes minutes) to happen.
*
- * <p>Requires Permission:
+ * <p>Requires Permissions:
* {@link android.Manifest.permission#MODIFY_PHONE_STATE} or that the calling app has carrier
* privileges (see {@link #hasCarrierPrivileges})
+ * and {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
*
* @return {@link CellNetworkScanResult} with the status
* {@link CellNetworkScanResult#STATUS_SUCCESS} and a list of
@@ -6490,12 +6643,15 @@ public class TelephonyManager {
*
* @hide
*/
- @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.MODIFY_PHONE_STATE,
+ Manifest.permission.ACCESS_COARSE_LOCATION
+ })
public CellNetworkScanResult getAvailableNetworks() {
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- return telephony.getCellNetworkScanResults(getSubId());
+ return telephony.getCellNetworkScanResults(getSubId(), getOpPackageName());
}
} catch (RemoteException ex) {
Rlog.e(TAG, "getAvailableNetworks RemoteException", ex);
@@ -6514,7 +6670,8 @@ public class TelephonyManager {
*
* <p>Requires Permission:
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
- * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ * app has carrier privileges (see {@link #hasCarrierPrivileges})
+ * and {@link android.Manifest.permission#ACCESS_FINE_LOCATION}.
*
* @param request Contains all the RAT with bands/channels that need to be scanned.
* @param executor The executor through which the callback should be invoked. Since the scan
@@ -6525,7 +6682,10 @@ public class TelephonyManager {
* @return A NetworkScan obj which contains a callback which can be used to stop the scan.
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
- @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.MODIFY_PHONE_STATE,
+ Manifest.permission.ACCESS_FINE_LOCATION
+ })
public NetworkScan requestNetworkScan(
NetworkScanRequest request, Executor executor,
TelephonyScanManager.NetworkScanCallback callback) {
@@ -6534,7 +6694,8 @@ public class TelephonyManager {
mTelephonyScanManager = new TelephonyScanManager();
}
}
- return mTelephonyScanManager.requestNetworkScan(getSubId(), request, executor, callback);
+ return mTelephonyScanManager.requestNetworkScan(getSubId(), request, executor, callback,
+ getOpPackageName());
}
/**
@@ -6544,7 +6705,10 @@ public class TelephonyManager {
* @removed
*/
@Deprecated
- @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresPermission(allOf = {
+ android.Manifest.permission.MODIFY_PHONE_STATE,
+ Manifest.permission.ACCESS_FINE_LOCATION
+ })
public NetworkScan requestNetworkScan(
NetworkScanRequest request, TelephonyScanManager.NetworkScanCallback callback) {
return requestNetworkScan(request, AsyncTask.SERIAL_EXECUTOR, callback);
@@ -6661,6 +6825,38 @@ public class TelephonyManager {
}
/**
+ * Set the preferred network type bitmap.
+ *
+ * <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
+ * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+ *
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
+ * app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ *
+ * @param networkTypeBitmap The bitmap of preferred network types.
+ * @return true on success; false on any failure.
+ * @hide
+ */
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @SystemApi
+ public boolean setPreferredNetworkTypeBitmap(@NetworkTypeBitMask long networkTypeBitmap) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ return telephony.setPreferredNetworkType(
+ getSubId(), RadioAccessFamily.getNetworkTypeFromRaf(
+ (int) networkTypeBitmap));
+ }
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "setPreferredNetworkType RemoteException", ex);
+ } catch (NullPointerException ex) {
+ Rlog.e(TAG, "setPreferredNetworkType NPE", ex);
+ }
+ return false;
+ }
+
+ /**
* Set the preferred network type to global mode which includes LTE, CDMA, EvDo and GSM/WCDMA.
*
* <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
@@ -7808,9 +8004,7 @@ public class TelephonyManager {
* support for the feature and device firmware support.
*
* @return {@code true} if the device and carrier both support RTT, {@code false} otherwise.
- * @hide
*/
- @TestApi
public boolean isRttSupported() {
try {
ITelephony telephony = getITelephony();
@@ -8514,10 +8708,14 @@ public class TelephonyManager {
* given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
*
* <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
- * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ * or that the calling app has carrier privileges (see {@link #hasCarrierPrivileges})
+ * and {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
*/
@SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
- @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ @RequiresPermission(allOf = {
+ Manifest.permission.READ_PHONE_STATE,
+ Manifest.permission.ACCESS_COARSE_LOCATION
+ })
public ServiceState getServiceState() {
return getServiceStateForSubscriber(getSubId());
}
@@ -9519,10 +9717,10 @@ public class TelephonyManager {
*
* <p>
* Requires Permission:
- * {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
+ * {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE READ_PRIVILEGED_PHONE_STATE}
* @hide
*/
- @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public boolean isOpportunisticNetworkEnabled() {
String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
boolean isEnabled = false;
@@ -9541,7 +9739,7 @@ public class TelephonyManager {
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true, prefix = {"NETWORK_TYPE_BITMASK_"},
+ @LongDef(flag = true, prefix = {"NETWORK_TYPE_BITMASK_"},
value = {NETWORK_TYPE_BITMASK_UNKNOWN,
NETWORK_TYPE_BITMASK_GSM,
NETWORK_TYPE_BITMASK_GPRS,
@@ -9570,118 +9768,125 @@ public class TelephonyManager {
* @hide
*/
@SystemApi
- public static final int NETWORK_TYPE_BITMASK_UNKNOWN = (1 << NETWORK_TYPE_UNKNOWN);
+ public static final long NETWORK_TYPE_BITMASK_UNKNOWN = 0L;
/**
* network type bitmask indicating the support of radio tech GSM.
* @hide
*/
@SystemApi
- public static final int NETWORK_TYPE_BITMASK_GSM = (1 << NETWORK_TYPE_GSM);
+ public static final long NETWORK_TYPE_BITMASK_GSM = (1 << (NETWORK_TYPE_GSM -1));
/**
* network type bitmask indicating the support of radio tech GPRS.
* @hide
*/
@SystemApi
- public static final int NETWORK_TYPE_BITMASK_GPRS = (1 << NETWORK_TYPE_GPRS);
+ public static final long NETWORK_TYPE_BITMASK_GPRS = (1 << (NETWORK_TYPE_GPRS -1));
/**
* network type bitmask indicating the support of radio tech EDGE.
* @hide
*/
@SystemApi
- public static final int NETWORK_TYPE_BITMASK_EDGE = (1 << NETWORK_TYPE_EDGE);
+ public static final long NETWORK_TYPE_BITMASK_EDGE = (1 << (NETWORK_TYPE_EDGE -1));
/**
* network type bitmask indicating the support of radio tech CDMA(IS95A/IS95B).
* @hide
*/
@SystemApi
- public static final int NETWORK_TYPE_BITMASK_CDMA = (1 << NETWORK_TYPE_CDMA);
+ public static final long NETWORK_TYPE_BITMASK_CDMA = (1 << (NETWORK_TYPE_CDMA -1));
/**
* network type bitmask indicating the support of radio tech 1xRTT.
* @hide
*/
@SystemApi
- public static final int NETWORK_TYPE_BITMASK_1xRTT = (1 << NETWORK_TYPE_1xRTT);
+ public static final long NETWORK_TYPE_BITMASK_1xRTT = (1 << (NETWORK_TYPE_1xRTT - 1));
// 3G
/**
* network type bitmask indicating the support of radio tech EVDO 0.
* @hide
*/
@SystemApi
- public static final int NETWORK_TYPE_BITMASK_EVDO_0 = (1 << NETWORK_TYPE_EVDO_0);
+ public static final long NETWORK_TYPE_BITMASK_EVDO_0 = (1 << (NETWORK_TYPE_EVDO_0 -1));
/**
* network type bitmask indicating the support of radio tech EVDO A.
* @hide
*/
@SystemApi
- public static final int NETWORK_TYPE_BITMASK_EVDO_A = (1 << NETWORK_TYPE_EVDO_A);
+ public static final long NETWORK_TYPE_BITMASK_EVDO_A = (1 << (NETWORK_TYPE_EVDO_A - 1));
/**
* network type bitmask indicating the support of radio tech EVDO B.
* @hide
*/
@SystemApi
- public static final int NETWORK_TYPE_BITMASK_EVDO_B = (1 << NETWORK_TYPE_EVDO_B);
+ public static final long NETWORK_TYPE_BITMASK_EVDO_B = (1 << (NETWORK_TYPE_EVDO_B -1));
/**
* network type bitmask indicating the support of radio tech EHRPD.
* @hide
*/
@SystemApi
- public static final int NETWORK_TYPE_BITMASK_EHRPD = (1 << NETWORK_TYPE_EHRPD);
+ public static final long NETWORK_TYPE_BITMASK_EHRPD = (1 << (NETWORK_TYPE_EHRPD -1));
/**
* network type bitmask indicating the support of radio tech HSUPA.
* @hide
*/
@SystemApi
- public static final int NETWORK_TYPE_BITMASK_HSUPA = (1 << NETWORK_TYPE_HSUPA);
+ public static final long NETWORK_TYPE_BITMASK_HSUPA = (1 << (NETWORK_TYPE_HSUPA -1));
/**
* network type bitmask indicating the support of radio tech HSDPA.
* @hide
*/
@SystemApi
- public static final int NETWORK_TYPE_BITMASK_HSDPA = (1 << NETWORK_TYPE_HSDPA);
+ public static final long NETWORK_TYPE_BITMASK_HSDPA = (1 << (NETWORK_TYPE_HSDPA -1));
/**
* network type bitmask indicating the support of radio tech HSPA.
* @hide
*/
@SystemApi
- public static final int NETWORK_TYPE_BITMASK_HSPA = (1 << NETWORK_TYPE_HSPA);
+ public static final long NETWORK_TYPE_BITMASK_HSPA = (1 << (NETWORK_TYPE_HSPA -1));
/**
* network type bitmask indicating the support of radio tech HSPAP.
* @hide
*/
@SystemApi
- public static final int NETWORK_TYPE_BITMASK_HSPAP = (1 << NETWORK_TYPE_HSPAP);
+ public static final long NETWORK_TYPE_BITMASK_HSPAP = (1 << (NETWORK_TYPE_HSPAP -1));
/**
* network type bitmask indicating the support of radio tech UMTS.
* @hide
*/
@SystemApi
- public static final int NETWORK_TYPE_BITMASK_UMTS = (1 << NETWORK_TYPE_UMTS);
+ public static final long NETWORK_TYPE_BITMASK_UMTS = (1 << (NETWORK_TYPE_UMTS -1));
/**
* network type bitmask indicating the support of radio tech TD_SCDMA.
* @hide
*/
@SystemApi
- public static final int NETWORK_TYPE_BITMASK_TD_SCDMA = (1 << NETWORK_TYPE_TD_SCDMA);
+ public static final long NETWORK_TYPE_BITMASK_TD_SCDMA = (1 << (NETWORK_TYPE_TD_SCDMA -1));
// 4G
/**
* network type bitmask indicating the support of radio tech LTE.
* @hide
*/
@SystemApi
- public static final int NETWORK_TYPE_BITMASK_LTE = (1 << NETWORK_TYPE_LTE);
+ public static final long NETWORK_TYPE_BITMASK_LTE = (1 << (NETWORK_TYPE_LTE -1));
/**
* network type bitmask indicating the support of radio tech LTE CA (carrier aggregation).
* @hide
*/
@SystemApi
- public static final int NETWORK_TYPE_BITMASK_LTE_CA = (1 << NETWORK_TYPE_LTE_CA);
+ public static final long NETWORK_TYPE_BITMASK_LTE_CA = (1 << (NETWORK_TYPE_LTE_CA -1));
/**
* network type bitmask indicating the support of radio tech NR(New Radio) 5G.
* @hide
*/
@SystemApi
- public static final int NETWORK_TYPE_BITMASK_NR = (1 << NETWORK_TYPE_NR);
+ public static final long NETWORK_TYPE_BITMASK_NR = (1 << (NETWORK_TYPE_NR -1));
+
+ /**
+ * network type bitmask indicating the support of radio tech IWLAN.
+ * @hide
+ */
+ @SystemApi
+ public static final long NETWORK_TYPE_BITMASK_IWLAN = (1 << (NETWORK_TYPE_IWLAN -1));
/**
* @return Modem supported radio access family bitmask
@@ -9692,11 +9897,11 @@ public class TelephonyManager {
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- public @NetworkTypeBitMask int getSupportedRadioAccessFamily() {
+ public @NetworkTypeBitMask long getSupportedRadioAccessFamily() {
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- return telephony.getRadioAccessFamily(getSlotIndex(), getOpPackageName());
+ return (long) telephony.getRadioAccessFamily(getSlotIndex(), getOpPackageName());
} else {
// This can happen when the ITelephony interface is not up yet.
return NETWORK_TYPE_BITMASK_UNKNOWN;
@@ -9903,12 +10108,17 @@ public class TelephonyManager {
* Get preferred opportunistic data subscription Id
*
* <p>Requires that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}),
- * or has permission {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}.
+ * or has either READ_PRIVILEGED_PHONE_STATE
+ * or {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE} permission.
* @return subId preferred opportunistic subscription id or
* {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID} if there are no preferred
* subscription id
*
*/
+ @RequiresPermission(anyOf = {
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
+ android.Manifest.permission.READ_PHONE_STATE
+ })
public int getPreferredOpportunisticDataSubscription() {
String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -10051,4 +10261,53 @@ public class TelephonyManager {
*/
public static final String EXTRA_NETWORK_COUNTRY =
"android.telephony.extra.NETWORK_COUNTRY";
+ /**
+ * Switch configs to enable multi-sim or switch back to single-sim
+ * <p>Requires Permission:
+ * {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the
+ * calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ * @param numOfSims number of live SIMs we want to switch to
+ * @throws android.os.RemoteException
+ */
+ @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void switchMultiSimConfig(int numOfSims) {
+ //only proceed if multi-sim is not restricted
+ if (isMultisimCarrierRestricted()) {
+ Rlog.e(TAG, "switchMultiSimConfig not possible. It is restricted.");
+ return;
+ }
+
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null) {
+ telephony.switchMultiSimConfig(numOfSims);
+ }
+ } catch (RemoteException ex) {
+ Rlog.e(TAG, "switchMultiSimConfig RemoteException", ex);
+ }
+ }
+
+ /**
+ * Get whether reboot is required or not after making changes to modem configurations.
+ * The modem configuration change refers to switching from single SIM configuration to DSDS
+ * or the other way around.
+ * @Return {@code true} if reboot is required after making changes to modem configurations,
+ * otherwise return {@code false}.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public boolean isRebootRequiredForModemConfigChange() {
+ try {
+ ITelephony service = getITelephony();
+ if (service != null) {
+ return service.isRebootRequiredForModemConfigChange();
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "isRebootRequiredForModemConfigChange RemoteException", e);
+ }
+ return false;
+ }
}
diff --git a/telephony/java/android/telephony/TelephonyScanManager.java b/telephony/java/android/telephony/TelephonyScanManager.java
index 96ff33255b53..91f74b867fa0 100644
--- a/telephony/java/android/telephony/TelephonyScanManager.java
+++ b/telephony/java/android/telephony/TelephonyScanManager.java
@@ -29,14 +29,14 @@ import android.os.Messenger;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.util.Log;
import android.util.SparseArray;
+
+import com.android.internal.telephony.ITelephony;
+
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Executor;
-import com.android.internal.telephony.ITelephony;
-
/**
* Manages the radio access network scan requests and callbacks.
*/
@@ -183,6 +183,7 @@ public final class TelephonyScanManager {
*
* <p>
* Requires Permission:
+ * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} and
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
* Or the calling app has carrier privileges. @see #hasCarrierPrivileges
*
@@ -192,11 +193,13 @@ public final class TelephonyScanManager {
* @hide
*/
public NetworkScan requestNetworkScan(int subId,
- NetworkScanRequest request, Executor executor, NetworkScanCallback callback) {
+ NetworkScanRequest request, Executor executor, NetworkScanCallback callback,
+ String callingPackage) {
try {
ITelephony telephony = getITelephony();
if (telephony != null) {
- int scanId = telephony.requestNetworkScan(subId, request, mMessenger, new Binder());
+ int scanId = telephony.requestNetworkScan(
+ subId, request, mMessenger, new Binder(), callingPackage);
saveScanInfo(scanId, request, executor, callback);
return new NetworkScan(scanId, subId);
}
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index a5c0442948ac..828e3e93b7a4 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -253,7 +253,7 @@ public class EuiccManager {
public static final int EUICC_OTA_STATUS_UNAVAILABLE = 5;
private final Context mContext;
- private final int mCardId;
+ private int mCardId;
/** @hide */
public EuiccManager(Context context) {
@@ -291,7 +291,7 @@ public class EuiccManager {
public boolean isEnabled() {
// In the future, this may reach out to IEuiccController (if non-null) to check any dynamic
// restrictions.
- return getIEuiccController() != null && mCardId != TelephonyManager.INVALID_CARD_ID;
+ return getIEuiccController() != null;
}
/**
@@ -301,15 +301,15 @@ public class EuiccManager {
* current eUICC. A calling app with carrier privileges for one eUICC may not necessarily have
* access to the EID of another eUICC.
*
- * @return the EID. May be null if {@link #isEnabled()} is false or the eUICC is not ready.
+ * @return the EID. May be null if the eUICC is not ready.
*/
@Nullable
public String getEid() {
- if (!isEnabled()) {
+ if (!refreshCardIdIfUninitialized()) {
return null;
}
try {
- return getIEuiccController().getEid(mCardId);
+ return getIEuiccController().getEid(mCardId, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -320,15 +320,15 @@ public class EuiccManager {
*
* <p>Requires the {@link android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission.
*
- * @return the status of eUICC OTA. If {@link #isEnabled()} is false or the eUICC is not ready,
- * {@link OtaStatus#EUICC_OTA_STATUS_UNAVAILABLE} will be returned.
+ * @return the status of eUICC OTA. If the eUICC is not ready,
+ * {@link OtaStatus#EUICC_OTA_STATUS_UNAVAILABLE} will be returned.
*
* @hide
*/
@SystemApi
@RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
public int getOtaStatus() {
- if (!isEnabled()) {
+ if (!refreshCardIdIfUninitialized()) {
return EUICC_OTA_STATUS_UNAVAILABLE;
}
try {
@@ -347,6 +347,15 @@ public class EuiccManager {
* Without the former, an {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR} will be
* returned in the callback intent to prompt the user to accept the download.
*
+ * <p>On a multi-active SIM device, requires the
+ * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission, or a calling app
+ * only if the targeted eUICC does not currently have an active subscription or the calling app
+ * is authorized to manage the active subscription on the target eUICC, and the calling app is
+ * authorized to manage any active subscription on any SIM. Without it, an
+ * {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR} will be returned in the callback
+ * intent to prompt the user to accept the download. The caller should also be authorized to
+ * manage the subscription to be downloaded.
+ *
* @param subscription the subscription to download.
* @param switchAfterDownload if true, the profile will be activated upon successful download.
* @param callbackIntent a PendingIntent to launch when the operation completes.
@@ -354,7 +363,7 @@ public class EuiccManager {
@RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
public void downloadSubscription(DownloadableSubscription subscription,
boolean switchAfterDownload, PendingIntent callbackIntent) {
- if (!isEnabled()) {
+ if (!refreshCardIdIfUninitialized()) {
sendUnavailableError(callbackIntent);
return;
}
@@ -416,7 +425,7 @@ public class EuiccManager {
@SystemApi
@RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
public void continueOperation(Intent resolutionIntent, Bundle resolutionExtras) {
- if (!isEnabled()) {
+ if (!refreshCardIdIfUninitialized()) {
PendingIntent callbackIntent =
resolutionIntent.getParcelableExtra(
EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_RESOLUTION_CALLBACK_INTENT);
@@ -453,7 +462,7 @@ public class EuiccManager {
@RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
public void getDownloadableSubscriptionMetadata(
DownloadableSubscription subscription, PendingIntent callbackIntent) {
- if (!isEnabled()) {
+ if (!refreshCardIdIfUninitialized()) {
sendUnavailableError(callbackIntent);
return;
}
@@ -483,7 +492,7 @@ public class EuiccManager {
@SystemApi
@RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
public void getDefaultDownloadableSubscriptionList(PendingIntent callbackIntent) {
- if (!isEnabled()) {
+ if (!refreshCardIdIfUninitialized()) {
sendUnavailableError(callbackIntent);
return;
}
@@ -498,12 +507,11 @@ public class EuiccManager {
/**
* Returns information about the eUICC chip/device.
*
- * @return the {@link EuiccInfo}. May be null if {@link #isEnabled()} is false or the eUICC is
- * not ready.
+ * @return the {@link EuiccInfo}. May be null if the eUICC is not ready.
*/
@Nullable
public EuiccInfo getEuiccInfo() {
- if (!isEnabled()) {
+ if (!refreshCardIdIfUninitialized()) {
return null;
}
try {
@@ -528,7 +536,7 @@ public class EuiccManager {
*/
@RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
public void deleteSubscription(int subscriptionId, PendingIntent callbackIntent) {
- if (!isEnabled()) {
+ if (!refreshCardIdIfUninitialized()) {
sendUnavailableError(callbackIntent);
return;
}
@@ -549,14 +557,26 @@ public class EuiccManager {
* an {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR} will be returned in the callback
* intent to prompt the user to accept the download.
*
+ * <p>On a multi-active SIM device, requires the
+ * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission, or a calling app
+ * only if the targeted eUICC does not currently have an active subscription or the calling app
+ * is authorized to manage the active subscription on the target eUICC, and the calling app is
+ * authorized to manage any active subscription on any SIM. Without it, an
+ * {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR} will be returned in the callback
+ * intent to prompt the user to accept the download. The caller should also be authorized to
+ * manage the subscription to be enabled.
+ *
* @param subscriptionId the ID of the subscription to enable. May be
* {@link android.telephony.SubscriptionManager#INVALID_SUBSCRIPTION_ID} to deactivate the
- * current profile without activating another profile to replace it.
+ * current profile without activating another profile to replace it. If it's a disable
+ * operation, requires the {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS}
+ * permission, or the calling app must be authorized to manage the active subscription on
+ * the target eUICC.
* @param callbackIntent a PendingIntent to launch when the operation completes.
*/
@RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
public void switchToSubscription(int subscriptionId, PendingIntent callbackIntent) {
- if (!isEnabled()) {
+ if (!refreshCardIdIfUninitialized()) {
sendUnavailableError(callbackIntent);
return;
}
@@ -582,7 +602,7 @@ public class EuiccManager {
@RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
public void updateSubscriptionNickname(
int subscriptionId, String nickname, PendingIntent callbackIntent) {
- if (!isEnabled()) {
+ if (!refreshCardIdIfUninitialized()) {
sendUnavailableError(callbackIntent);
return;
}
@@ -606,7 +626,7 @@ public class EuiccManager {
@SystemApi
@RequiresPermission(Manifest.permission.WRITE_EMBEDDED_SUBSCRIPTIONS)
public void eraseSubscriptions(PendingIntent callbackIntent) {
- if (!isEnabled()) {
+ if (!refreshCardIdIfUninitialized()) {
sendUnavailableError(callbackIntent);
return;
}
@@ -636,7 +656,7 @@ public class EuiccManager {
* @hide
*/
public void retainSubscriptionsForFactoryReset(PendingIntent callbackIntent) {
- if (!isEnabled()) {
+ if (!refreshCardIdIfUninitialized()) {
sendUnavailableError(callbackIntent);
return;
}
@@ -647,6 +667,27 @@ public class EuiccManager {
}
}
+ /**
+ * Refreshes the cardId if its uninitialized, and returns whether we should continue the
+ * operation.
+ * <p>
+ * Note that after a successful refresh, the mCardId may be TelephonyManager.UNSUPPORTED_CARD_ID
+ * on older HALs. For backwards compatability, we continue to the LPA and let it decide which
+ * card to use.
+ */
+ private boolean refreshCardIdIfUninitialized() {
+ // Refresh mCardId if its UNINITIALIZED_CARD_ID
+ if (mCardId == TelephonyManager.UNINITIALIZED_CARD_ID) {
+ TelephonyManager tm = (TelephonyManager)
+ mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ mCardId = tm.getCardIdForDefaultEuicc();
+ }
+ if (mCardId == TelephonyManager.UNINITIALIZED_CARD_ID) {
+ return false;
+ }
+ return true;
+ }
+
private static void sendUnavailableError(PendingIntent callbackIntent) {
try {
callbackIntent.send(EMBEDDED_SUBSCRIPTION_RESULT_ERROR);
diff --git a/telephony/java/android/telephony/ims/ImsCallProfile.java b/telephony/java/android/telephony/ims/ImsCallProfile.java
index 59167b7d5bba..73f055649b3f 100644
--- a/telephony/java/android/telephony/ims/ImsCallProfile.java
+++ b/telephony/java/android/telephony/ims/ImsCallProfile.java
@@ -350,6 +350,9 @@ public final class ImsCallProfile implements Parcelable {
/** Indicates if the call is for testing purpose */
private boolean mEmergencyCallTesting = false;
+ /** Indicates if we have known the intent of the user for the call is emergency */
+ private boolean mHasKnownUserIntentEmergency = false;
+
/**
* Extras associated with this {@link ImsCallProfile}.
* <p>
@@ -789,12 +792,13 @@ public final class ImsCallProfile implements Parcelable {
*
* @hide
*/
- public void setEmergencyCallInfo(EmergencyNumber num) {
+ public void setEmergencyCallInfo(EmergencyNumber num, boolean hasKnownUserIntentEmergency) {
setEmergencyServiceCategories(num.getEmergencyServiceCategoryBitmaskInternalDial());
setEmergencyUrns(num.getEmergencyUrns());
setEmergencyCallRouting(num.getEmergencyCallRouting());
setEmergencyCallTesting(num.getEmergencyNumberSourceBitmask()
== EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST);
+ setHasKnownUserIntentEmergency(hasKnownUserIntentEmergency);
}
/**
@@ -860,6 +864,19 @@ public final class ImsCallProfile implements Parcelable {
}
/**
+ * Set if we have known the user intent of the call is emergency.
+ *
+ * This is only used to specify when the dialed number is ambiguous when it can be identified
+ * as both emergency number and any other non-emergency number; e.g. in some situation, 611
+ * could be both an emergency number in a country and a non-emergency number of a carrier's
+ * customer service hotline.
+ */
+ @VisibleForTesting
+ public void setHasKnownUserIntentEmergency(boolean hasKnownUserIntentEmergency) {
+ mHasKnownUserIntentEmergency = hasKnownUserIntentEmergency;
+ }
+
+ /**
* Get the emergency service categories, only valid if {@link #getServiceType} returns
* {@link #SERVICE_TYPE_EMERGENCY}
*
@@ -916,4 +933,16 @@ public final class ImsCallProfile implements Parcelable {
public boolean isEmergencyCallTesting() {
return mEmergencyCallTesting;
}
+
+ /**
+ * Checks if we have known the user intent of the call is emergency.
+ *
+ * This is only used to specify when the dialed number is ambiguous when it can be identified
+ * as both emergency number and any other non-emergency number; e.g. in some situation, 611
+ * could be both an emergency number in a country and a non-emergency number of a carrier's
+ * customer service hotline.
+ */
+ public boolean hasKnownUserIntentEmergency() {
+ return mHasKnownUserIntentEmergency;
+ }
}
diff --git a/telephony/java/android/telephony/ims/ImsException.java b/telephony/java/android/telephony/ims/ImsException.java
new file mode 100644
index 000000000000..ac4d17a0ce65
--- /dev/null
+++ b/telephony/java/android/telephony/ims/ImsException.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.text.TextUtils;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This class defines an IMS-related exception that has been thrown while interacting with a
+ * device or carrier provided ImsService implementation.
+ * @hide
+ */
+@SystemApi
+public class ImsException extends Exception {
+
+ /**
+ * The operation has failed due to an unknown or unspecified error.
+ */
+ public static final int CODE_ERROR_UNSPECIFIED = 0;
+ /**
+ * The operation has failed because there is no {@link ImsService} available to service it. This
+ * may be due to an {@link ImsService} crash or other illegal state.
+ * <p>
+ * This is a temporary error and the operation may be retried until the connection to the
+ * {@link ImsService} is restored.
+ */
+ public static final int CODE_ERROR_SERVICE_UNAVAILABLE = 1;
+
+ /**
+ * This device or carrier configuration does not support IMS for this subscription.
+ * <p>
+ * This is a permanent configuration error and there should be no retry.
+ */
+ public static final int CODE_ERROR_UNSUPPORTED_OPERATION = 2;
+
+ /**@hide*/
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = "CODE_ERROR_", value = {
+ CODE_ERROR_UNSPECIFIED,
+ CODE_ERROR_SERVICE_UNAVAILABLE,
+ CODE_ERROR_UNSUPPORTED_OPERATION
+ })
+ public @interface ImsErrorCode {}
+
+ private int mCode = CODE_ERROR_UNSPECIFIED;
+
+ /**
+ * A new {@link ImsException} with an unspecified {@link ImsErrorCode} code.
+ * @param message an optional message to detail the error condition more specifically.
+ */
+ public ImsException(@Nullable String message) {
+ super(getMessage(message, CODE_ERROR_UNSPECIFIED));
+ }
+
+ /**
+ * A new {@link ImsException} that includes an {@link ImsErrorCode} error code.
+ * @param message an optional message to detail the error condition more specifically.
+ */
+ public ImsException(@Nullable String message, @ImsErrorCode int code) {
+ super(getMessage(message, code));
+ mCode = code;
+ }
+
+ /**
+ * A new {@link ImsException} that includes an {@link ImsErrorCode} error code and a
+ * {@link Throwable} that contains the original error that was thrown to lead to this Exception.
+ * @param message an optional message to detail the error condition more specifically.
+ * @param cause the {@link Throwable} that caused this {@link ImsException} to be created.
+ */
+ public ImsException(@Nullable String message, @ImsErrorCode int code, Throwable cause) {
+ super(getMessage(message, code), cause);
+ mCode = code;
+ }
+
+ /**
+ * @return the IMS Error code that is associated with this {@link ImsException}.
+ */
+ public @ImsErrorCode int getCode() {
+ return mCode;
+ }
+
+ private static String getMessage(String message, int code) {
+ StringBuilder builder;
+ if (!TextUtils.isEmpty(message)) {
+ builder = new StringBuilder(message);
+ builder.append(" (code: ");
+ builder.append(code);
+ builder.append(")");
+ return builder.toString();
+ } else {
+ return "code: " + code;
+ }
+ }
+}
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index 5b2e635b179f..eb99d5dcaaeb 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -54,7 +54,7 @@ import java.util.concurrent.Executor;
* registration and MmTel capability status callbacks, as well as query/modify user settings for the
* associated subscription.
*
- * @see #createForSubscriptionId(Context, int)
+ * @see #createForSubscriptionId(int)
* @hide
*/
@SystemApi
@@ -315,15 +315,12 @@ public class ImsMmTelManager {
/**
* Create an instance of ImsManager for the subscription id specified.
*
- * @param context The context to create this ImsMmTelManager instance within.
* @param subId The ID of the subscription that this ImsMmTelManager will use.
* @see android.telephony.SubscriptionManager#getActiveSubscriptionInfoList()
- * @throws IllegalArgumentException if the subscription is invalid or
- * the subscription ID is not an active subscription.
+ * @throws IllegalArgumentException if the subscription is invalid.
*/
- public static ImsMmTelManager createForSubscriptionId(Context context, int subId) {
- if (!SubscriptionManager.isValidSubscriptionId(subId)
- || !getSubscriptionManager(context).isActiveSubscriptionId(subId)) {
+ public static ImsMmTelManager createForSubscriptionId(int subId) {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
throw new IllegalArgumentException("Invalid subscription ID");
}
@@ -331,7 +328,7 @@ public class ImsMmTelManager {
}
/**
- * Only visible for testing, use {@link #createForSubscriptionId(Context, int)} instead.
+ * Only visible for testing, use {@link #createForSubscriptionId(int)} instead.
* @hide
*/
@VisibleForTesting
@@ -341,7 +338,7 @@ public class ImsMmTelManager {
/**
* Registers a {@link RegistrationCallback} with the system, which will provide registration
- * updates for the subscription specified in {@link #createForSubscriptionId(Context, int)}. Use
+ * updates for the subscription specified in {@link #createForSubscriptionId(int)}. Use
* {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to Subscription changed
* events and call {@link #unregisterImsRegistrationCallback(RegistrationCallback)} to clean up.
*
@@ -354,13 +351,14 @@ public class ImsMmTelManager {
* @throws IllegalArgumentException if the subscription associated with this callback is not
* active (SIM is not inserted, ESIM inactive) or invalid, or a null {@link Executor} or
* {@link CapabilityCallback} callback.
- * @throws IllegalStateException if the subscription associated with this callback is valid, but
+ * @throws ImsException if the subscription associated with this callback is valid, but
* the {@link ImsService} associated with the subscription is not available. This can happen if
- * the service crashed, for example.
+ * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
+ * reason.
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public void registerImsRegistrationCallback(@CallbackExecutor Executor executor,
- @NonNull RegistrationCallback c) {
+ @NonNull RegistrationCallback c) throws ImsException {
if (c == null) {
throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
}
@@ -372,6 +370,8 @@ public class ImsMmTelManager {
getITelephony().registerImsRegistrationCallback(mSubId, c.getBinder());
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
+ } catch (IllegalStateException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
}
@@ -403,7 +403,7 @@ public class ImsMmTelManager {
/**
* Registers a {@link CapabilityCallback} with the system, which will provide MmTel service
* availability updates for the subscription specified in
- * {@link #createForSubscriptionId(Context, int)}. The method {@link #isAvailable(int, int)}
+ * {@link #createForSubscriptionId(int)}. The method {@link #isAvailable(int, int)}
* can also be used to query this information at any time.
*
* Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to
@@ -419,13 +419,14 @@ public class ImsMmTelManager {
* @throws IllegalArgumentException if the subscription associated with this callback is not
* active (SIM is not inserted, ESIM inactive) or invalid, or a null {@link Executor} or
* {@link CapabilityCallback} callback.
- * @throws IllegalStateException if the subscription associated with this callback is valid, but
+ * @throws ImsException if the subscription associated with this callback is valid, but
* the {@link ImsService} associated with the subscription is not available. This can happen if
- * the service crashed, for example.
+ * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
+ * reason.
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public void registerMmTelCapabilityCallback(@NonNull @CallbackExecutor Executor executor,
- @NonNull CapabilityCallback c) {
+ @NonNull CapabilityCallback c) throws ImsException {
if (c == null) {
throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
}
@@ -437,6 +438,8 @@ public class ImsMmTelManager {
getITelephony().registerMmTelCapabilityCallback(mSubId, c.getBinder());
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
+ } catch (IllegalStateException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
}
@@ -796,14 +799,6 @@ public class ImsMmTelManager {
}
}
- private static SubscriptionManager getSubscriptionManager(Context context) {
- SubscriptionManager manager = context.getSystemService(SubscriptionManager.class);
- if (manager == null) {
- throw new RuntimeException("Could not find SubscriptionManager.");
- }
- return manager;
- }
-
private static ITelephony getITelephony() {
ITelephony binder = ITelephony.Stub.asInterface(
ServiceManager.getService(Context.TELEPHONY_SERVICE));
diff --git a/telephony/java/android/telephony/ims/ImsReasonInfo.java b/telephony/java/android/telephony/ims/ImsReasonInfo.java
index 4d95e552c1da..d8d2d9e5951a 100644
--- a/telephony/java/android/telephony/ims/ImsReasonInfo.java
+++ b/telephony/java/android/telephony/ims/ImsReasonInfo.java
@@ -465,7 +465,7 @@ public final class ImsReasonInfo implements Parcelable {
public static final int CODE_USER_REJECTED_SESSION_MODIFICATION = 511;
/**
- * Upgrade Downgrade request cacncelled by the user who initiated it
+ * Upgrade Downgrade request cancelled by the user who initiated it
*/
public static final int CODE_USER_CANCELLED_SESSION_MODIFICATION = 512;
@@ -887,6 +887,185 @@ public final class ImsReasonInfo implements Parcelable {
public static final int CODE_OEM_CAUSE_15 = 0xf00f;
/**
+ * @hide
+ */
+ @IntDef(value = {
+ CODE_UNSPECIFIED,
+ CODE_LOCAL_ILLEGAL_ARGUMENT,
+ CODE_LOCAL_ILLEGAL_STATE,
+ CODE_LOCAL_INTERNAL_ERROR,
+ CODE_LOCAL_IMS_SERVICE_DOWN,
+ CODE_LOCAL_NO_PENDING_CALL,
+ CODE_LOCAL_ENDED_BY_CONFERENCE_MERGE,
+ CODE_LOCAL_POWER_OFF,
+ CODE_LOCAL_LOW_BATTERY,
+ CODE_LOCAL_NETWORK_NO_SERVICE,
+ CODE_LOCAL_NETWORK_NO_LTE_COVERAGE,
+ CODE_LOCAL_NETWORK_ROAMING,
+ CODE_LOCAL_NETWORK_IP_CHANGED,
+ CODE_LOCAL_SERVICE_UNAVAILABLE,
+ CODE_LOCAL_NOT_REGISTERED,
+ CODE_LOCAL_CALL_EXCEEDED,
+ CODE_LOCAL_CALL_BUSY,
+ CODE_LOCAL_CALL_DECLINE,
+ CODE_LOCAL_CALL_VCC_ON_PROGRESSING,
+ CODE_LOCAL_CALL_RESOURCE_RESERVATION_FAILED,
+ CODE_LOCAL_CALL_CS_RETRY_REQUIRED,
+ CODE_LOCAL_CALL_VOLTE_RETRY_REQUIRED,
+ CODE_LOCAL_CALL_TERMINATED,
+ CODE_LOCAL_HO_NOT_FEASIBLE,
+ CODE_TIMEOUT_1XX_WAITING,
+ CODE_TIMEOUT_NO_ANSWER,
+ CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE,
+ CODE_CALL_BARRED,
+ CODE_FDN_BLOCKED,
+ CODE_IMEI_NOT_ACCEPTED,
+ CODE_DIAL_MODIFIED_TO_USSD,
+ CODE_DIAL_MODIFIED_TO_SS,
+ CODE_DIAL_MODIFIED_TO_DIAL,
+ CODE_DIAL_MODIFIED_TO_DIAL_VIDEO,
+ CODE_DIAL_VIDEO_MODIFIED_TO_DIAL,
+ CODE_DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO,
+ CODE_DIAL_VIDEO_MODIFIED_TO_SS,
+ CODE_DIAL_VIDEO_MODIFIED_TO_USSD,
+ CODE_SIP_REDIRECTED,
+ CODE_SIP_BAD_REQUEST,
+ CODE_SIP_FORBIDDEN,
+ CODE_SIP_NOT_FOUND,
+ CODE_SIP_NOT_SUPPORTED,
+ CODE_SIP_REQUEST_TIMEOUT,
+ CODE_SIP_TEMPRARILY_UNAVAILABLE,
+ CODE_SIP_BAD_ADDRESS,
+ CODE_SIP_BUSY,
+ CODE_SIP_REQUEST_CANCELLED,
+ CODE_SIP_NOT_ACCEPTABLE,
+ CODE_SIP_NOT_REACHABLE,
+ CODE_SIP_CLIENT_ERROR,
+ CODE_SIP_TRANSACTION_DOES_NOT_EXIST,
+ CODE_SIP_SERVER_INTERNAL_ERROR,
+ CODE_SIP_SERVICE_UNAVAILABLE,
+ CODE_SIP_SERVER_TIMEOUT,
+ CODE_SIP_SERVER_ERROR,
+ CODE_SIP_USER_REJECTED,
+ CODE_SIP_GLOBAL_ERROR,
+ CODE_EMERGENCY_TEMP_FAILURE,
+ CODE_EMERGENCY_PERM_FAILURE,
+ CODE_SIP_USER_MARKED_UNWANTED,
+ CODE_SIP_METHOD_NOT_ALLOWED,
+ CODE_SIP_PROXY_AUTHENTICATION_REQUIRED,
+ CODE_SIP_REQUEST_ENTITY_TOO_LARGE,
+ CODE_SIP_REQUEST_URI_TOO_LARGE,
+ CODE_SIP_EXTENSION_REQUIRED,
+ CODE_SIP_INTERVAL_TOO_BRIEF,
+ CODE_SIP_CALL_OR_TRANS_DOES_NOT_EXIST,
+ CODE_SIP_LOOP_DETECTED,
+ CODE_SIP_TOO_MANY_HOPS,
+ CODE_SIP_AMBIGUOUS,
+ CODE_SIP_REQUEST_PENDING,
+ CODE_SIP_UNDECIPHERABLE,
+ CODE_MEDIA_INIT_FAILED,
+ CODE_MEDIA_NO_DATA,
+ CODE_MEDIA_NOT_ACCEPTABLE,
+ CODE_MEDIA_UNSPECIFIED,
+ CODE_USER_TERMINATED,
+ CODE_USER_NOANSWER,
+ CODE_USER_IGNORE,
+ CODE_USER_DECLINE,
+ CODE_LOW_BATTERY,
+ CODE_BLACKLISTED_CALL_ID,
+ CODE_USER_TERMINATED_BY_REMOTE,
+ CODE_USER_REJECTED_SESSION_MODIFICATION,
+ CODE_USER_CANCELLED_SESSION_MODIFICATION,
+ CODE_SESSION_MODIFICATION_FAILED,
+ CODE_UT_NOT_SUPPORTED,
+ CODE_UT_SERVICE_UNAVAILABLE,
+ CODE_UT_OPERATION_NOT_ALLOWED,
+ CODE_UT_NETWORK_ERROR,
+ CODE_UT_CB_PASSWORD_MISMATCH,
+ CODE_UT_SS_MODIFIED_TO_DIAL,
+ CODE_UT_SS_MODIFIED_TO_USSD,
+ CODE_UT_SS_MODIFIED_TO_SS,
+ CODE_UT_SS_MODIFIED_TO_DIAL_VIDEO,
+ CODE_ECBM_NOT_SUPPORTED,
+ CODE_MULTIENDPOINT_NOT_SUPPORTED,
+ CODE_REGISTRATION_ERROR,
+ CODE_ANSWERED_ELSEWHERE,
+ CODE_CALL_PULL_OUT_OF_SYNC,
+ CODE_CALL_END_CAUSE_CALL_PULL,
+ CODE_CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE,
+ CODE_REJECTED_ELSEWHERE,
+ CODE_SUPP_SVC_FAILED,
+ CODE_SUPP_SVC_CANCELLED,
+ CODE_SUPP_SVC_REINVITE_COLLISION,
+ CODE_IWLAN_DPD_FAILURE,
+ CODE_EPDG_TUNNEL_ESTABLISH_FAILURE,
+ CODE_EPDG_TUNNEL_REKEY_FAILURE,
+ CODE_EPDG_TUNNEL_LOST_CONNECTION,
+ CODE_MAXIMUM_NUMBER_OF_CALLS_REACHED,
+ CODE_REMOTE_CALL_DECLINE,
+ CODE_DATA_LIMIT_REACHED,
+ CODE_DATA_DISABLED,
+ CODE_WIFI_LOST,
+ CODE_IKEV2_AUTH_FAILURE,
+ CODE_RADIO_OFF,
+ CODE_NO_VALID_SIM,
+ CODE_RADIO_INTERNAL_ERROR,
+ CODE_NETWORK_RESP_TIMEOUT,
+ CODE_NETWORK_REJECT,
+ CODE_RADIO_ACCESS_FAILURE,
+ CODE_RADIO_LINK_FAILURE,
+ CODE_RADIO_LINK_LOST,
+ CODE_RADIO_UPLINK_FAILURE,
+ CODE_RADIO_SETUP_FAILURE,
+ CODE_RADIO_RELEASE_NORMAL,
+ CODE_RADIO_RELEASE_ABNORMAL,
+ CODE_ACCESS_CLASS_BLOCKED,
+ CODE_NETWORK_DETACH,
+ CODE_SIP_ALTERNATE_EMERGENCY_CALL,
+ CODE_UNOBTAINABLE_NUMBER,
+ CODE_NO_CSFB_IN_CS_ROAM,
+ CODE_REJECT_UNKNOWN,
+ CODE_REJECT_ONGOING_CALL_WAITING_DISABLED,
+ CODE_REJECT_CALL_ON_OTHER_SUB,
+ CODE_REJECT_1X_COLLISION,
+ CODE_REJECT_SERVICE_NOT_REGISTERED,
+ CODE_REJECT_CALL_TYPE_NOT_ALLOWED,
+ CODE_REJECT_ONGOING_E911_CALL,
+ CODE_REJECT_ONGOING_CALL_SETUP,
+ CODE_REJECT_MAX_CALL_LIMIT_REACHED,
+ CODE_REJECT_UNSUPPORTED_SIP_HEADERS,
+ CODE_REJECT_UNSUPPORTED_SDP_HEADERS,
+ CODE_REJECT_ONGOING_CALL_TRANSFER,
+ CODE_REJECT_INTERNAL_ERROR,
+ CODE_REJECT_QOS_FAILURE,
+ CODE_REJECT_ONGOING_HANDOVER,
+ CODE_REJECT_VT_TTY_NOT_ALLOWED,
+ CODE_REJECT_ONGOING_CALL_UPGRADE,
+ CODE_REJECT_CONFERENCE_TTY_NOT_ALLOWED,
+ CODE_REJECT_ONGOING_CONFERENCE_CALL,
+ CODE_REJECT_VT_AVPF_NOT_ALLOWED,
+ CODE_REJECT_ONGOING_ENCRYPTED_CALL,
+ CODE_REJECT_ONGOING_CS_CALL,
+ CODE_OEM_CAUSE_1,
+ CODE_OEM_CAUSE_2,
+ CODE_OEM_CAUSE_3,
+ CODE_OEM_CAUSE_4,
+ CODE_OEM_CAUSE_5,
+ CODE_OEM_CAUSE_6,
+ CODE_OEM_CAUSE_7,
+ CODE_OEM_CAUSE_8,
+ CODE_OEM_CAUSE_9,
+ CODE_OEM_CAUSE_10,
+ CODE_OEM_CAUSE_11,
+ CODE_OEM_CAUSE_12,
+ CODE_OEM_CAUSE_13,
+ CODE_OEM_CAUSE_14,
+ CODE_OEM_CAUSE_15
+ }, prefix = "CODE_")
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ImsCode {}
+
+ /**
* Network string error messages.
* mExtraMessage may have these values.
*/
@@ -964,7 +1143,7 @@ public final class ImsReasonInfo implements Parcelable {
/**
* @return an integer representing more information about the completion of an operation.
*/
- public int getCode() {
+ public @ImsCode int getCode() {
return mCode;
}
diff --git a/telephony/java/android/telephony/ims/ProvisioningManager.java b/telephony/java/android/telephony/ims/ProvisioningManager.java
index 086a76546b2d..204891b7b86e 100644
--- a/telephony/java/android/telephony/ims/ProvisioningManager.java
+++ b/telephony/java/android/telephony/ims/ProvisioningManager.java
@@ -172,15 +172,13 @@ public class ProvisioningManager {
/**
* Create a new {@link ProvisioningManager} for the subscription specified.
- * @param context The context that this manager will use.
+ *
* @param subId The ID of the subscription that this ProvisioningManager will use.
* @see android.telephony.SubscriptionManager#getActiveSubscriptionInfoList()
- * @throws IllegalArgumentException if the subscription is invalid or
- * the subscription ID is not an active subscription.
+ * @throws IllegalArgumentException if the subscription is invalid.
*/
- public static ProvisioningManager createForSubscriptionId(Context context, int subId) {
- if (!SubscriptionManager.isValidSubscriptionId(subId)
- || !getSubscriptionManager(context).isActiveSubscriptionId(subId)) {
+ public static ProvisioningManager createForSubscriptionId(int subId) {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
throw new IllegalArgumentException("Invalid subscription ID");
}
@@ -202,18 +200,21 @@ public class ProvisioningManager {
* @see SubscriptionManager.OnSubscriptionsChangedListener
* @throws IllegalArgumentException if the subscription associated with this callback is not
* active (SIM is not inserted, ESIM inactive) or the subscription is invalid.
- * @throws IllegalStateException if the subscription associated with this callback is valid, but
+ * @throws ImsException if the subscription associated with this callback is valid, but
* the {@link ImsService} associated with the subscription is not available. This can happen if
- * the service crashed, for example.
+ * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
+ * reason.
*/
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
public void registerProvisioningChangedCallback(@CallbackExecutor Executor executor,
- @NonNull Callback callback) {
+ @NonNull Callback callback) throws ImsException {
callback.setExecutor(executor);
try {
getITelephony().registerImsProvisioningChangedCallback(mSubId, callback.getBinder());
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
+ } catch (IllegalStateException e) {
+ throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
}
@@ -369,14 +370,6 @@ public class ProvisioningManager {
}
}
- private static SubscriptionManager getSubscriptionManager(Context context) {
- SubscriptionManager manager = context.getSystemService(SubscriptionManager.class);
- if (manager == null) {
- throw new RuntimeException("Could not find SubscriptionManager.");
- }
- return manager;
- }
-
private static ITelephony getITelephony() {
ITelephony binder = ITelephony.Stub.asInterface(
ServiceManager.getService(Context.TELEPHONY_SERVICE));
diff --git a/telephony/java/android/telephony/ims/Rcs1To1Thread.java b/telephony/java/android/telephony/ims/Rcs1To1Thread.java
index 709b3aa0f804..d4a78ffb77db 100644
--- a/telephony/java/android/telephony/ims/Rcs1To1Thread.java
+++ b/telephony/java/android/telephony/ims/Rcs1To1Thread.java
@@ -15,42 +15,70 @@
*/
package android.telephony.ims;
-import android.os.Parcel;
+import android.annotation.NonNull;
+import android.annotation.WorkerThread;
/**
* Rcs1To1Thread represents a single RCS conversation thread with a total of two
- * {@link RcsParticipant}s.
- * @hide - TODO(sahinc) make this public
+ * {@link RcsParticipant}s. Please see Section 5 (1-to-1 Messaging) - GSMA RCC.71 (RCS Universal
+ * Profile Service Definition Document)
*/
public class Rcs1To1Thread extends RcsThread {
+ private int mThreadId;
+
+ /**
+ * Public constructor only for RcsMessageStoreController to initialize new threads.
+ *
+ * @hide
+ */
public Rcs1To1Thread(int threadId) {
super(threadId);
+ mThreadId = threadId;
}
- public static final Creator<Rcs1To1Thread> CREATOR = new Creator<Rcs1To1Thread>() {
- @Override
- public Rcs1To1Thread createFromParcel(Parcel in) {
- return new Rcs1To1Thread(in);
- }
-
- @Override
- public Rcs1To1Thread[] newArray(int size) {
- return new Rcs1To1Thread[size];
- }
- };
+ /**
+ * @return Returns {@code false} as this is always a 1 to 1 thread.
+ */
+ @Override
+ public boolean isGroup() {
+ return false;
+ }
- protected Rcs1To1Thread(Parcel in) {
- super(in);
+ /**
+ * {@link Rcs1To1Thread}s can fall back to SMS as a back-up protocol. This function returns the
+ * thread id to be used to query {@code content://mms-sms/conversation/#} to get the fallback
+ * thread.
+ *
+ * @return The thread id to be used to query the mms-sms authority
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public long getFallbackThreadId() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.get1To1ThreadFallbackThreadId(mThreadId));
}
- @Override
- public int describeContents() {
- return 0;
+ /**
+ * If the RCS client allows falling back to SMS, it needs to create an MMS-SMS thread in the
+ * SMS/MMS Provider( see {@link android.provider.Telephony.MmsSms#CONTENT_CONVERSATIONS_URI}.
+ * Use this function to link the {@link Rcs1To1Thread} to the MMS-SMS thread. This function
+ * also updates the storage.
+ *
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setFallbackThreadId(long fallbackThreadId) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(
+ iRcs -> iRcs.set1To1ThreadFallbackThreadId(mThreadId, fallbackThreadId));
}
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(RCS_1_TO_1_TYPE);
- super.writeToParcel(dest, flags);
+ /**
+ * @return Returns the {@link RcsParticipant} that receives the messages sent in this thread.
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @NonNull
+ @WorkerThread
+ public RcsParticipant getRecipient() throws RcsMessageStoreException {
+ return new RcsParticipant(
+ RcsControllerCall.call(iRcs -> iRcs.get1To1ThreadOtherParticipantId(mThreadId)));
}
}
diff --git a/telephony/java/android/telephony/ims/RcsControllerCall.java b/telephony/java/android/telephony/ims/RcsControllerCall.java
new file mode 100644
index 000000000000..5512c4c7b19d
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsControllerCall.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.telephony.ims.aidl.IRcs;
+
+/**
+ * A wrapper class around RPC calls that {@link RcsMessageStore} APIs to minimize boilerplate code.
+ *
+ * @hide - not meant for public use
+ */
+class RcsControllerCall {
+ static <R> R call(RcsServiceCall<R> serviceCall) throws RcsMessageStoreException {
+ IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_RCS_SERVICE));
+ if (iRcs == null) {
+ throw new RcsMessageStoreException("Could not connect to RCS storage service");
+ }
+
+ try {
+ return serviceCall.methodOnIRcs(iRcs);
+ } catch (RemoteException exception) {
+ throw new RcsMessageStoreException(exception.getMessage());
+ }
+ }
+
+ static void callWithNoReturn(RcsServiceCallWithNoReturn serviceCall)
+ throws RcsMessageStoreException {
+ IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService(Context.TELEPHONY_RCS_SERVICE));
+ if (iRcs == null) {
+ throw new RcsMessageStoreException("Could not connect to RCS storage service");
+ }
+
+ try {
+ serviceCall.methodOnIRcs(iRcs);
+ } catch (RemoteException exception) {
+ throw new RcsMessageStoreException(exception.getMessage());
+ }
+ }
+
+ interface RcsServiceCall<R> {
+ R methodOnIRcs(IRcs iRcs) throws RemoteException;
+ }
+
+ interface RcsServiceCallWithNoReturn {
+ void methodOnIRcs(IRcs iRcs) throws RemoteException;
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsPart.aidl b/telephony/java/android/telephony/ims/RcsEvent.aidl
index 8b8077d57676..08974e0a771c 100644
--- a/telephony/java/android/telephony/ims/RcsPart.aidl
+++ b/telephony/java/android/telephony/ims/RcsEvent.aidl
@@ -17,4 +17,4 @@
package android.telephony.ims;
-parcelable RcsPart;
+parcelable RcsEvent;
diff --git a/telephony/java/android/telephony/ims/RcsEvent.java b/telephony/java/android/telephony/ims/RcsEvent.java
new file mode 100644
index 000000000000..df62277f9ac1
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsEvent.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.ims;
+
+import android.os.Parcel;
+
+/**
+ * The base class for events that can happen on {@link RcsParticipant}s and {@link RcsThread}s.
+ */
+public abstract class RcsEvent {
+ /**
+ * @hide
+ */
+ protected final long mTimestamp;
+
+ protected RcsEvent(long timestamp) {
+ mTimestamp = timestamp;
+ }
+
+ /**
+ * @return Returns the time of when this event happened. The timestamp is defined as
+ * milliseconds passed after midnight, January 1, 1970 UTC
+ */
+ public long getTimestamp() {
+ return mTimestamp;
+ }
+
+ /**
+ * Persists the event to the data store
+ *
+ * @hide
+ */
+ abstract void persist() throws RcsMessageStoreException;
+
+ /**
+ * @hide
+ */
+ RcsEvent(Parcel in) {
+ mTimestamp = in.readLong();
+ }
+
+ /**
+ * @hide
+ */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(mTimestamp);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsIncomingMessage.aidl b/telephony/java/android/telephony/ims/RcsEventQueryParams.aidl
index 6552a82c9072..f18c4dfd2dcd 100644
--- a/telephony/java/android/telephony/ims/RcsIncomingMessage.aidl
+++ b/telephony/java/android/telephony/ims/RcsEventQueryParams.aidl
@@ -17,4 +17,4 @@
package android.telephony.ims;
-parcelable RcsIncomingMessage;
+parcelable RcsEventQueryParams;
diff --git a/telephony/java/android/telephony/ims/RcsEventQueryParams.java b/telephony/java/android/telephony/ims/RcsEventQueryParams.java
new file mode 100644
index 000000000000..9dbfe4393213
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsEventQueryParams.java
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import static android.provider.Telephony.RcsColumns.RcsEventTypes.ICON_CHANGED_EVENT_TYPE;
+import static android.provider.Telephony.RcsColumns.RcsEventTypes.NAME_CHANGED_EVENT_TYPE;
+import static android.provider.Telephony.RcsColumns.RcsEventTypes.PARTICIPANT_ALIAS_CHANGED_EVENT_TYPE;
+import static android.provider.Telephony.RcsColumns.RcsEventTypes.PARTICIPANT_JOINED_EVENT_TYPE;
+import static android.provider.Telephony.RcsColumns.RcsEventTypes.PARTICIPANT_LEFT_EVENT_TYPE;
+
+import android.annotation.CheckResult;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.security.InvalidParameterException;
+
+/**
+ * The parameters to pass into
+ * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} in order to select a
+ * subset of {@link RcsEvent}s present in the message store.
+ */
+public final class RcsEventQueryParams implements Parcelable {
+ /**
+ * Flag to be used with {@link Builder#setEventType(int)} to make
+ * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} return all types of
+ * {@link RcsEvent}s
+ */
+ public static final int ALL_EVENTS = -1;
+
+ /**
+ * Flag to be used with {@link Builder#setEventType(int)} to make
+ * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} return sub-types of
+ * {@link RcsGroupThreadEvent}s
+ */
+ public static final int ALL_GROUP_THREAD_EVENTS = 0;
+
+ /**
+ * Flag to be used with {@link Builder#setEventType(int)} to make
+ * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} return only
+ * {@link RcsParticipantAliasChangedEvent}s
+ */
+ public static final int PARTICIPANT_ALIAS_CHANGED_EVENT =
+ PARTICIPANT_ALIAS_CHANGED_EVENT_TYPE;
+
+ /**
+ * Flag to be used with {@link Builder#setEventType(int)} to make
+ * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} return only
+ * {@link RcsGroupThreadParticipantJoinedEvent}s
+ */
+ public static final int GROUP_THREAD_PARTICIPANT_JOINED_EVENT =
+ PARTICIPANT_JOINED_EVENT_TYPE;
+
+ /**
+ * Flag to be used with {@link Builder#setEventType(int)} to make
+ * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} return only
+ * {@link RcsGroupThreadParticipantLeftEvent}s
+ */
+ public static final int GROUP_THREAD_PARTICIPANT_LEFT_EVENT =
+ PARTICIPANT_LEFT_EVENT_TYPE;
+
+ /**
+ * Flag to be used with {@link Builder#setEventType(int)} to make
+ * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} return only
+ * {@link RcsGroupThreadNameChangedEvent}s
+ */
+ public static final int GROUP_THREAD_NAME_CHANGED_EVENT = NAME_CHANGED_EVENT_TYPE;
+
+ /**
+ * Flag to be used with {@link Builder#setEventType(int)} to make
+ * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} return only
+ * {@link RcsGroupThreadIconChangedEvent}s
+ */
+ public static final int GROUP_THREAD_ICON_CHANGED_EVENT = ICON_CHANGED_EVENT_TYPE;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({ALL_EVENTS, ALL_GROUP_THREAD_EVENTS, PARTICIPANT_ALIAS_CHANGED_EVENT,
+ GROUP_THREAD_PARTICIPANT_JOINED_EVENT, GROUP_THREAD_PARTICIPANT_LEFT_EVENT,
+ GROUP_THREAD_NAME_CHANGED_EVENT, GROUP_THREAD_ICON_CHANGED_EVENT})
+ public @interface EventType {
+ }
+
+ /**
+ * Flag to be used with {@link Builder#setSortProperty(int)} that makes the result set sorted
+ * in the order of creation for faster query results.
+ */
+ public static final int SORT_BY_CREATION_ORDER = 0;
+
+ /**
+ * Flag to be used with {@link Builder#setSortProperty(int)} that makes the result set sorted
+ * with respect to {@link RcsEvent#getTimestamp()}
+ */
+ public static final int SORT_BY_TIMESTAMP = 1;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({SORT_BY_CREATION_ORDER, SORT_BY_TIMESTAMP})
+ public @interface SortingProperty {
+ }
+
+ /**
+ * The key to pass into a Bundle, for usage in RcsProvider.query(Bundle)
+ * @hide - not meant for public use
+ */
+ public static final String EVENT_QUERY_PARAMETERS_KEY = "event_query_parameters";
+
+ // Which types of events the results should be limited to
+ private @EventType int mEventType;
+ // The property which the results should be sorted against
+ private int mSortingProperty;
+ // Whether the results should be sorted in ascending order
+ private boolean mIsAscending;
+ // The number of results that should be returned with this query
+ private int mLimit;
+ // The thread that the results are limited to
+ private int mThreadId;
+
+ RcsEventQueryParams(@EventType int eventType, int threadId,
+ @SortingProperty int sortingProperty, boolean isAscending, int limit) {
+ mEventType = eventType;
+ mSortingProperty = sortingProperty;
+ mIsAscending = isAscending;
+ mLimit = limit;
+ mThreadId = threadId;
+ }
+
+ /**
+ * @return Returns the type of {@link RcsEvent}s that this {@link RcsEventQueryParams} is
+ * set to query for.
+ */
+ public @EventType int getEventType() {
+ return mEventType;
+ }
+
+ /**
+ * @return Returns the number of {@link RcsEvent}s to be returned from the query. A value of
+ * 0 means there is no set limit.
+ */
+ public int getLimit() {
+ return mLimit;
+ }
+
+ /**
+ * @return Returns the property where the results should be sorted against.
+ * @see SortingProperty
+ */
+ public int getSortingProperty() {
+ return mSortingProperty;
+ }
+
+ /**
+ * @return Returns {@code true} if the result set will be sorted in ascending order,
+ * {@code false} if it will be sorted in descending order.
+ */
+ public boolean getSortDirection() {
+ return mIsAscending;
+ }
+
+ /**
+ * @return Returns the ID of the {@link RcsGroupThread} that the results are limited to. As this
+ * API exposes an ID, it should stay hidden.
+ *
+ * @hide
+ */
+ public int getThreadId() {
+ return mThreadId;
+ }
+
+ /**
+ * A helper class to build the {@link RcsEventQueryParams}.
+ */
+ public static class Builder {
+ private @EventType int mEventType;
+ private @SortingProperty int mSortingProperty;
+ private boolean mIsAscending;
+ private int mLimit = 100;
+ private int mThreadId;
+
+ /**
+ * Creates a new builder for {@link RcsEventQueryParams} to be used in
+ * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)}
+ */
+ public Builder() {
+ // empty implementation
+ }
+
+ /**
+ * Desired number of events to be returned from the query. Passing in 0 will return all
+ * existing events at once. The limit defaults to 100.
+ *
+ * @param limit The number to limit the query result to.
+ * @return The same instance of the builder to chain parameters.
+ * @throws InvalidParameterException If the given limit is negative.
+ */
+ @CheckResult
+ public Builder setResultLimit(@IntRange(from = 0) int limit)
+ throws InvalidParameterException {
+ if (limit < 0) {
+ throw new InvalidParameterException("The query limit must be non-negative");
+ }
+
+ mLimit = limit;
+ return this;
+ }
+
+ /**
+ * Sets the type of events to be returned from the query.
+ *
+ * @param eventType The type of event to be returned.
+ * @return The same instance of the builder to chain parameters.
+ */
+ @CheckResult
+ public Builder setEventType(@EventType int eventType) {
+ mEventType = eventType;
+ return this;
+ }
+
+ /**
+ * Sets the property where the results should be sorted against. Defaults to
+ * {@link RcsEventQueryParams.SortingProperty#SORT_BY_CREATION_ORDER}
+ *
+ * @param sortingProperty against which property the results should be sorted
+ * @return The same instance of the builder to chain parameters.
+ */
+ @CheckResult
+ public Builder setSortProperty(@SortingProperty int sortingProperty) {
+ mSortingProperty = sortingProperty;
+ return this;
+ }
+
+ /**
+ * Sets whether the results should be sorted ascending or descending
+ *
+ * @param isAscending whether the results should be sorted ascending
+ * @return The same instance of the builder to chain parameters.
+ */
+ @CheckResult
+ public Builder setSortDirection(boolean isAscending) {
+ mIsAscending = isAscending;
+ return this;
+ }
+
+ /**
+ * Limits the results to the given {@link RcsGroupThread}. Setting this value prevents
+ * returning any instances of {@link RcsParticipantAliasChangedEvent}.
+ *
+ * @param groupThread The thread to limit the results to.
+ * @return The same instance of the builder to chain parameters.
+ */
+ @CheckResult
+ public Builder setGroupThread(@NonNull RcsGroupThread groupThread) {
+ mThreadId = groupThread.getThreadId();
+ return this;
+ }
+
+ /**
+ * Builds the {@link RcsEventQueryParams} to use in
+ * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)}
+ *
+ * @return An instance of {@link RcsEventQueryParams} to use with the event query.
+ */
+ public RcsEventQueryParams build() {
+ return new RcsEventQueryParams(mEventType, mThreadId, mSortingProperty,
+ mIsAscending, mLimit);
+ }
+ }
+
+ private RcsEventQueryParams(Parcel in) {
+ mEventType = in.readInt();
+ mThreadId = in.readInt();
+ mSortingProperty = in.readInt();
+ mIsAscending = in.readBoolean();
+ mLimit = in.readInt();
+ }
+
+ public static final Creator<RcsEventQueryParams> CREATOR =
+ new Creator<RcsEventQueryParams>() {
+ @Override
+ public RcsEventQueryParams createFromParcel(Parcel in) {
+ return new RcsEventQueryParams(in);
+ }
+
+ @Override
+ public RcsEventQueryParams[] newArray(int size) {
+ return new RcsEventQueryParams[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mEventType);
+ dest.writeInt(mThreadId);
+ dest.writeInt(mSortingProperty);
+ dest.writeBoolean(mIsAscending);
+ dest.writeInt(mLimit);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/Rcs1To1Thread.aidl b/telephony/java/android/telephony/ims/RcsEventQueryResult.aidl
index 9fdc41d2bd5f..7d133350973c 100644
--- a/telephony/java/android/telephony/ims/Rcs1To1Thread.aidl
+++ b/telephony/java/android/telephony/ims/RcsEventQueryResult.aidl
@@ -17,4 +17,4 @@
package android.telephony.ims;
-parcelable Rcs1To1Thread;
+parcelable RcsEventQueryResult;
diff --git a/telephony/java/android/telephony/ims/RcsEventQueryResult.java b/telephony/java/android/telephony/ims/RcsEventQueryResult.java
new file mode 100644
index 000000000000..c30e4ccd7aa2
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsEventQueryResult.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.List;
+
+/**
+ * The result of a {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)}
+ * call. This class allows getting the token for querying the next batch of events in order to
+ * prevent handling large amounts of data at once.
+ */
+public final class RcsEventQueryResult implements Parcelable {
+ private RcsQueryContinuationToken mContinuationToken;
+ private List<RcsEvent> mEvents;
+
+ /**
+ * Internal constructor for {@link com.android.internal.telephony.ims.RcsMessageStoreController}
+ * to create query results
+ *
+ * @hide
+ */
+ public RcsEventQueryResult(
+ RcsQueryContinuationToken continuationToken,
+ List<RcsEvent> events) {
+ mContinuationToken = continuationToken;
+ mEvents = events;
+ }
+
+ /**
+ * Returns a token to call
+ * {@link RcsMessageStore#getRcsEvents(RcsQueryContinuationToken)}
+ * to get the next batch of {@link RcsEvent}s.
+ */
+ public RcsQueryContinuationToken getContinuationToken() {
+ return mContinuationToken;
+ }
+
+ /**
+ * Returns all the {@link RcsEvent}s in the current query result. Call {@link
+ * RcsMessageStore#getRcsEvents(RcsQueryContinuationToken)} to get the next batch
+ * of {@link RcsEvent}s.
+ */
+ public List<RcsEvent> getEvents() {
+ return mEvents;
+ }
+
+ private RcsEventQueryResult(Parcel in) {
+ mContinuationToken = in.readParcelable(RcsQueryContinuationToken.class.getClassLoader());
+ }
+
+ public static final Creator<RcsEventQueryResult> CREATOR = new Creator<RcsEventQueryResult>() {
+ @Override
+ public RcsEventQueryResult createFromParcel(Parcel in) {
+ return new RcsEventQueryResult(in);
+ }
+
+ @Override
+ public RcsEventQueryResult[] newArray(int size) {
+ return new RcsEventQueryResult[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeParcelable(mContinuationToken, flags);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.aidl b/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.aidl
new file mode 100644
index 000000000000..155219038d7b
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.aidl
@@ -0,0 +1,20 @@
+/*
+ *
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+parcelable RcsFileTransferCreationParams;
diff --git a/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.java b/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.java
new file mode 100644
index 000000000000..14af8ea63a67
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.ims;
+
+import android.annotation.CheckResult;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Pass an instance of this class to
+ * {@link RcsMessage#insertFileTransfer(RcsFileTransferCreationParams)} create an
+ * {@link RcsFileTransferPart} and save it into storage.
+ */
+public final class RcsFileTransferCreationParams implements Parcelable {
+ private String mRcsFileTransferSessionId;
+ private Uri mContentUri;
+ private String mContentMimeType;
+ private long mFileSize;
+ private long mTransferOffset;
+ private int mWidth;
+ private int mHeight;
+ private long mMediaDuration;
+ private Uri mPreviewUri;
+ private String mPreviewMimeType;
+ private @RcsFileTransferPart.RcsFileTransferStatus int mFileTransferStatus;
+
+ /**
+ * @return Returns the globally unique RCS file transfer session ID for the
+ * {@link RcsFileTransferPart} to be created
+ */
+ public String getRcsFileTransferSessionId() {
+ return mRcsFileTransferSessionId;
+ }
+
+ /**
+ * @return Returns the URI for the content of the {@link RcsFileTransferPart} to be created
+ */
+ public Uri getContentUri() {
+ return mContentUri;
+ }
+
+ /**
+ * @return Returns the MIME type for the content of the {@link RcsFileTransferPart} to be
+ * created
+ */
+ public String getContentMimeType() {
+ return mContentMimeType;
+ }
+
+ /**
+ * @return Returns the file size in bytes for the {@link RcsFileTransferPart} to be created
+ */
+ public long getFileSize() {
+ return mFileSize;
+ }
+
+ /**
+ * @return Returns the transfer offset for the {@link RcsFileTransferPart} to be created. The
+ * file transfer offset is defined as how many bytes have been successfully transferred to the
+ * receiver of this file transfer.
+ */
+ public long getTransferOffset() {
+ return mTransferOffset;
+ }
+
+ /**
+ * @return Returns the width of the {@link RcsFileTransferPart} to be created. The value is in
+ * pixels.
+ */
+ public int getWidth() {
+ return mWidth;
+ }
+
+ /**
+ * @return Returns the height of the {@link RcsFileTransferPart} to be created. The value is in
+ * pixels.
+ */
+ public int getHeight() {
+ return mHeight;
+ }
+
+ /**
+ * @return Returns the duration of the {@link RcsFileTransferPart} to be created.
+ */
+ public long getMediaDuration() {
+ return mMediaDuration;
+ }
+
+ /**
+ * @return Returns the URI of the preview of the content of the {@link RcsFileTransferPart} to
+ * be created. This should only be used for multi-media files.
+ */
+ public Uri getPreviewUri() {
+ return mPreviewUri;
+ }
+
+ /**
+ * @return Returns the MIME type of the preview of the content of the
+ * {@link RcsFileTransferPart} to be created. This should only be used for multi-media files.
+ */
+ public String getPreviewMimeType() {
+ return mPreviewMimeType;
+ }
+
+ /**
+ * @return Returns the status of the {@link RcsFileTransferPart} to be created.
+ */
+ public @RcsFileTransferPart.RcsFileTransferStatus int getFileTransferStatus() {
+ return mFileTransferStatus;
+ }
+
+ /**
+ * @hide
+ */
+ RcsFileTransferCreationParams(Builder builder) {
+ mRcsFileTransferSessionId = builder.mRcsFileTransferSessionId;
+ mContentUri = builder.mContentUri;
+ mContentMimeType = builder.mContentMimeType;
+ mFileSize = builder.mFileSize;
+ mTransferOffset = builder.mTransferOffset;
+ mWidth = builder.mWidth;
+ mHeight = builder.mHeight;
+ mMediaDuration = builder.mLength;
+ mPreviewUri = builder.mPreviewUri;
+ mPreviewMimeType = builder.mPreviewMimeType;
+ mFileTransferStatus = builder.mFileTransferStatus;
+ }
+
+ /**
+ * A builder to create instances of {@link RcsFileTransferCreationParams}
+ */
+ public class Builder {
+ private String mRcsFileTransferSessionId;
+ private Uri mContentUri;
+ private String mContentMimeType;
+ private long mFileSize;
+ private long mTransferOffset;
+ private int mWidth;
+ private int mHeight;
+ private long mLength;
+ private Uri mPreviewUri;
+ private String mPreviewMimeType;
+ private @RcsFileTransferPart.RcsFileTransferStatus int mFileTransferStatus;
+
+ /**
+ * Sets the globally unique RCS file transfer session ID for the {@link RcsFileTransferPart}
+ * to be created
+ *
+ * @param sessionId The RCS file transfer session ID
+ * @return The same instance of {@link Builder} to chain methods
+ */
+ @CheckResult
+ public Builder setFileTransferSessionId(String sessionId) {
+ mRcsFileTransferSessionId = sessionId;
+ return this;
+ }
+
+ /**
+ * Sets the URI for the content of the {@link RcsFileTransferPart} to be created
+ *
+ * @param contentUri The URI for the file
+ * @return The same instance of {@link Builder} to chain methods
+ */
+ @CheckResult
+ public Builder setContentUri(Uri contentUri) {
+ mContentUri = contentUri;
+ return this;
+ }
+
+ /**
+ * Sets the MIME type for the content of the {@link RcsFileTransferPart} to be created
+ *
+ * @param contentType The MIME type of the file
+ * @return The same instance of {@link Builder} to chain methods
+ */
+ @CheckResult
+ public Builder setContentMimeType(String contentType) {
+ mContentMimeType = contentType;
+ return this;
+ }
+
+ /**
+ * Sets the file size for the {@link RcsFileTransferPart} to be created
+ *
+ * @param size The size of the file in bytes
+ * @return The same instance of {@link Builder} to chain methods
+ */
+ @CheckResult
+ public Builder setFileSize(long size) {
+ mFileSize = size;
+ return this;
+ }
+
+ /**
+ * Sets the transfer offset for the {@link RcsFileTransferPart} to be created. The file
+ * transfer offset is defined as how many bytes have been successfully transferred to the
+ * receiver of this file transfer.
+ *
+ * @param offset The transfer offset in bytes
+ * @return The same instance of {@link Builder} to chain methods
+ */
+ @CheckResult
+ public Builder setTransferOffset(long offset) {
+ mTransferOffset = offset;
+ return this;
+ }
+
+ /**
+ * Sets the width of the {@link RcsFileTransferPart} to be created. This should only be used
+ * for multi-media files.
+ *
+ * @param width The width of the multi-media file in pixels.
+ * @return The same instance of {@link Builder} to chain methods
+ */
+ @CheckResult
+ public Builder setWidth(int width) {
+ mWidth = width;
+ return this;
+ }
+
+ /**
+ * Sets the height of the {@link RcsFileTransferPart} to be created. This should only be
+ * used for multi-media files.
+ *
+ * @param height The height of the multi-media file in pixels.
+ * @return The same instance of {@link Builder} to chain methods
+ */
+ @CheckResult
+ public Builder setHeight(int height) {
+ mHeight = height;
+ return this;
+ }
+
+ /**
+ * Sets the length of the {@link RcsFileTransferPart} to be created. This should only be
+ * used for multi-media files such as audio or video.
+ *
+ * @param length The length of the multi-media file in milliseconds
+ * @return The same instance of {@link Builder} to chain methods
+ */
+ @CheckResult
+ public Builder setMediaDuration(long length) {
+ mLength = length;
+ return this;
+ }
+
+ /**
+ * Sets the URI of the preview of the content of the {@link RcsFileTransferPart} to be
+ * created. This should only be used for multi-media files.
+ *
+ * @param previewUri The URI of the preview of the file transfer
+ * @return The same instance of {@link Builder} to chain methods
+ */
+ @CheckResult
+ public Builder setPreviewUri(Uri previewUri) {
+ mPreviewUri = previewUri;
+ return this;
+ }
+
+ /**
+ * Sets the MIME type of the preview of the content of the {@link RcsFileTransferPart} to
+ * be created. This should only be used for multi-media files.
+ *
+ * @param previewType The MIME type of the preview of the file transfer
+ * @return The same instance of {@link Builder} to chain methods
+ */
+ @CheckResult
+ public Builder setPreviewMimeType(String previewType) {
+ mPreviewMimeType = previewType;
+ return this;
+ }
+
+ /**
+ * Sets the status of the {@link RcsFileTransferPart} to be created.
+ *
+ * @param status The status of the file transfer
+ * @return The same instance of {@link Builder} to chain methods
+ */
+ @CheckResult
+ public Builder setFileTransferStatus(
+ @RcsFileTransferPart.RcsFileTransferStatus int status) {
+ mFileTransferStatus = status;
+ return this;
+ }
+
+ /**
+ * Creates an instance of {@link RcsFileTransferCreationParams} with the given
+ * parameters.
+ *
+ * @return The same instance of {@link Builder} to chain methods
+ * @see RcsMessage#insertFileTransfer(RcsFileTransferCreationParams)
+ */
+ public RcsFileTransferCreationParams build() {
+ return new RcsFileTransferCreationParams(this);
+ }
+ }
+
+ private RcsFileTransferCreationParams(Parcel in) {
+ mRcsFileTransferSessionId = in.readString();
+ mContentUri = in.readParcelable(Uri.class.getClassLoader());
+ mContentMimeType = in.readString();
+ mFileSize = in.readLong();
+ mTransferOffset = in.readLong();
+ mWidth = in.readInt();
+ mHeight = in.readInt();
+ mMediaDuration = in.readLong();
+ mPreviewUri = in.readParcelable(Uri.class.getClassLoader());
+ mPreviewMimeType = in.readString();
+ mFileTransferStatus = in.readInt();
+ }
+
+ public static final Creator<RcsFileTransferCreationParams> CREATOR =
+ new Creator<RcsFileTransferCreationParams>() {
+ @Override
+ public RcsFileTransferCreationParams createFromParcel(Parcel in) {
+ return new RcsFileTransferCreationParams(in);
+ }
+
+ @Override
+ public RcsFileTransferCreationParams[] newArray(int size) {
+ return new RcsFileTransferCreationParams[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mRcsFileTransferSessionId);
+ dest.writeParcelable(mContentUri, flags);
+ dest.writeString(mContentMimeType);
+ dest.writeLong(mFileSize);
+ dest.writeLong(mTransferOffset);
+ dest.writeInt(mWidth);
+ dest.writeInt(mHeight);
+ dest.writeLong(mMediaDuration);
+ dest.writeParcelable(mPreviewUri, flags);
+ dest.writeString(mPreviewMimeType);
+ dest.writeInt(mFileTransferStatus);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsFileTransferPart.java b/telephony/java/android/telephony/ims/RcsFileTransferPart.java
index 39c58dd9c15b..9531c2e2f981 100644
--- a/telephony/java/android/telephony/ims/RcsFileTransferPart.java
+++ b/telephony/java/android/telephony/ims/RcsFileTransferPart.java
@@ -15,34 +15,344 @@
*/
package android.telephony.ims;
-import android.os.Parcel;
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.annotation.WorkerThread;
+import android.net.Uri;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
/**
- * A part of a composite {@link RcsMessage} that holds a file transfer.
- * @hide - TODO(sahinc) make this public
+ * A part of a composite {@link RcsMessage} that holds a file transfer. Please see Section 7
+ * (File Transfer) - GSMA RCC.71 (RCS Universal Profile Service Definition Document)
*/
-public class RcsFileTransferPart extends RcsPart {
- public static final Creator<RcsFileTransferPart> CREATOR = new Creator<RcsFileTransferPart>() {
- @Override
- public RcsFileTransferPart createFromParcel(Parcel in) {
- return new RcsFileTransferPart(in);
- }
+public class RcsFileTransferPart {
+ /**
+ * The status to indicate that this {@link RcsFileTransferPart} is not set yet.
+ */
+ public static final int NOT_SET = 0;
+
+ /**
+ * The status to indicate that this {@link RcsFileTransferPart} is a draft and is not in the
+ * process of sending yet.
+ */
+ public static final int DRAFT = 1;
+
+ /**
+ * The status to indicate that this {@link RcsFileTransferPart} is actively being sent right
+ * now.
+ */
+ public static final int SENDING = 2;
+
+ /**
+ * The status to indicate that this {@link RcsFileTransferPart} was being sent, but the user has
+ * paused the sending process.
+ */
+ public static final int SENDING_PAUSED = 3;
+
+ /**
+ * The status to indicate that this {@link RcsFileTransferPart} was attempted, but failed to
+ * send.
+ */
+ public static final int SENDING_FAILED = 4;
+
+ /**
+ * The status to indicate that this {@link RcsFileTransferPart} is permanently cancelled to
+ * send.
+ */
+ public static final int SENDING_CANCELLED = 5;
+
+ /**
+ * The status to indicate that this {@link RcsFileTransferPart} is actively being downloaded
+ * right now.
+ */
+ public static final int DOWNLOADING = 6;
+
+ /**
+ * The status to indicate that this {@link RcsFileTransferPart} was being downloaded, but the
+ * user paused the downloading process.
+ */
+ public static final int DOWNLOADING_PAUSED = 7;
- @Override
- public RcsFileTransferPart[] newArray(int size) {
- return new RcsFileTransferPart[size];
- }
- };
+ /**
+ * The status to indicate that this {@link RcsFileTransferPart} was attempted, but failed to
+ * download.
+ */
+ public static final int DOWNLOADING_FAILED = 8;
+
+ /**
+ * The status to indicate that this {@link RcsFileTransferPart} is permanently cancelled to
+ * download.
+ */
+ public static final int DOWNLOADING_CANCELLED = 9;
+
+ /**
+ * The status to indicate that this {@link RcsFileTransferPart} was successfully sent or
+ * received.
+ */
+ public static final int SUCCEEDED = 10;
+
+ @IntDef({
+ DRAFT, SENDING, SENDING_PAUSED, SENDING_FAILED, SENDING_CANCELLED, DOWNLOADING,
+ DOWNLOADING_PAUSED, DOWNLOADING_FAILED, DOWNLOADING_CANCELLED, SUCCEEDED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RcsFileTransferStatus {
+ }
+
+ private int mId;
+
+ /**
+ * @hide
+ */
+ RcsFileTransferPart(int id) {
+ mId = id;
+ }
+
+ /**
+ * @hide
+ */
+ public void setId(int id) {
+ mId = id;
+ }
+
+ /**
+ * @hide
+ */
+ public int getId() {
+ return mId;
+ }
+
+ /**
+ * Sets the RCS file transfer session ID for this file transfer and persists into storage.
+ *
+ * @param sessionId The session ID to be used for this file transfer.
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setFileTransferSessionId(String sessionId) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setFileTransferSessionId(mId, sessionId));
+ }
+
+ /**
+ * @return Returns the file transfer session ID.
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public String getFileTransferSessionId() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getFileTransferSessionId(mId));
+ }
+
+ /**
+ * Sets the content URI for this file transfer and persists into storage. The file transfer
+ * should be reachable using this URI.
+ *
+ * @param contentUri The URI for this file transfer.
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setContentUri(Uri contentUri) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setFileTransferContentUri(mId, contentUri));
+ }
+
+ /**
+ * @return Returns the URI for this file transfer
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @Nullable
+ @WorkerThread
+ public Uri getContentUri() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getFileTransferContentUri(mId));
+ }
+
+ /**
+ * Sets the MIME type of this file transfer and persists into storage. Whether this type
+ * actually matches any known or supported types is not checked.
+ *
+ * @param contentMimeType The type of this file transfer.
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setContentMimeType(String contentMimeType) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(
+ iRcs -> iRcs.setFileTransferContentType(mId, contentMimeType));
+ }
+
+ /**
+ * @return Returns the content type of this file transfer
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ @Nullable
+ public String getContentMimeType() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getFileTransferContentType(mId));
+ }
+
+ /**
+ * Sets the content length (i.e. file size) for this file transfer and persists into storage.
+ *
+ * @param contentLength The content length of this file transfer
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setFileSize(long contentLength) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(
+ iRcs -> iRcs.setFileTransferFileSize(mId, contentLength));
+ }
+
+ /**
+ * @return Returns the content length (i.e. file size) for this file transfer.
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public long getFileSize() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getFileTransferFileSize(mId));
+ }
+
+ /**
+ * Sets the transfer offset for this file transfer and persists into storage. The file transfer
+ * offset is defined as how many bytes have been successfully transferred to the receiver of
+ * this file transfer.
+ *
+ * @param transferOffset The transfer offset for this file transfer.
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setTransferOffset(long transferOffset) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(
+ iRcs -> iRcs.setFileTransferTransferOffset(mId, transferOffset));
+ }
+
+ /**
+ * @return Returns the number of bytes that have successfully transferred.
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public long getTransferOffset() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getFileTransferTransferOffset(mId));
+ }
+
+ /**
+ * Sets the status for this file transfer and persists into storage.
+ *
+ * @param status The status of this file transfer.
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setFileTransferStatus(@RcsFileTransferStatus int status)
+ throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setFileTransferStatus(mId, status));
+ }
+
+ /**
+ * @return Returns the status of this file transfer.
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public @RcsFileTransferStatus int getFileTransferStatus() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getFileTransferStatus(mId));
+ }
+
+ /**
+ * @return Returns the width of this multi-media message part in pixels.
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public int getWidth() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getFileTransferWidth(mId));
+ }
+
+ /**
+ * Sets the width of this RCS multi-media message part and persists into storage.
+ *
+ * @param width The width value in pixels
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setWidth(int width) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setFileTransferWidth(mId, width));
+ }
+
+ /**
+ * @return Returns the height of this multi-media message part in pixels.
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public int getHeight() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getFileTransferHeight(mId));
+ }
+
+ /**
+ * Sets the height of this RCS multi-media message part and persists into storage.
+ *
+ * @param height The height value in pixels
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setHeight(int height) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setFileTransferHeight(mId, height));
+ }
+
+ /**
+ * @return Returns the length of this multi-media file (e.g. video or audio) in milliseconds.
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public long getLength() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getFileTransferLength(mId));
+ }
+
+ /**
+ * Sets the length of this multi-media file (e.g. video or audio) and persists into storage.
+ *
+ * @param length The length of the file in milliseconds.
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setLength(long length) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setFileTransferLength(mId, length));
+ }
+
+ /**
+ * @return Returns the URI for the preview of this multi-media file (e.g. an image thumbnail for
+ * a video)
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public Uri getPreviewUri() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getFileTransferPreviewUri(mId));
+ }
- protected RcsFileTransferPart(Parcel in) {
+ /**
+ * Sets the URI for the preview of this multi-media file and persists into storage.
+ *
+ * @param previewUri The URI to access to the preview file.
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setPreviewUri(Uri previewUri) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setFileTransferPreviewUri(mId, previewUri));
}
- @Override
- public int describeContents() {
- return 0;
+ /**
+ * @return Returns the MIME type of this multi-media file's preview.
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public String getPreviewMimeType() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getFileTransferPreviewType(mId));
}
- @Override
- public void writeToParcel(Parcel dest, int flags) {
+ /**
+ * Sets the MIME type for this multi-media file's preview and persists into storage.
+ *
+ * @param previewMimeType The MIME type for the preview
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setPreviewMimeType(String previewMimeType) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(
+ iRcs -> iRcs.setFileTransferPreviewType(mId, previewMimeType));
}
}
diff --git a/telephony/java/android/telephony/ims/RcsGroupThread.java b/telephony/java/android/telephony/ims/RcsGroupThread.java
index d954b2d70ac3..6e17bc2a685f 100644
--- a/telephony/java/android/telephony/ims/RcsGroupThread.java
+++ b/telephony/java/android/telephony/ims/RcsGroupThread.java
@@ -15,38 +15,189 @@
*/
package android.telephony.ims;
-import android.os.Parcel;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.WorkerThread;
+import android.net.Uri;
+
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
/**
* RcsGroupThread represents a single RCS conversation thread where {@link RcsParticipant}s can join
- * or leave.
- * @hide - TODO(sahinc) make this public
+ * or leave. Please see Section 6 (Group Chat) - GSMA RCC.71 (RCS Universal Profile Service
+ * Definition Document)
*/
public class RcsGroupThread extends RcsThread {
- public static final Creator<RcsGroupThread> CREATOR = new Creator<RcsGroupThread>() {
- @Override
- public RcsGroupThread createFromParcel(Parcel in) {
- return new RcsGroupThread(in);
+ /**
+ * Public constructor only for RcsMessageStoreController to initialize new threads.
+ *
+ * @hide
+ */
+ public RcsGroupThread(int threadId) {
+ super(threadId);
+ }
+
+ /**
+ * @return Returns {@code true} as this is always a group thread
+ */
+ @Override
+ public boolean isGroup() {
+ return true;
+ }
+
+ /**
+ * @return Returns the given name of this {@link RcsGroupThread}. Please see US6-2 - GSMA RCC.71
+ * (RCS Universal Profile Service Definition Document)
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @Nullable
+ @WorkerThread
+ public String getGroupName() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getGroupThreadName(mThreadId));
+ }
+
+ /**
+ * Sets the name of this {@link RcsGroupThread} and saves it into storage. Please see US6-2 -
+ * GSMA RCC.71 (RCS Universal Profile Service Definition Document)
+ *
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setGroupName(String groupName) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setGroupThreadName(mThreadId, groupName));
+ }
+
+ /**
+ * @return Returns a URI that points to the group's icon {@link RcsGroupThread}. Please see
+ * US6-2 - GSMA RCC.71 (RCS Universal Profile Service Definition Document)
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @Nullable
+ public Uri getGroupIcon() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getGroupThreadIcon(mThreadId));
+ }
+
+ /**
+ * Sets the icon for this {@link RcsGroupThread} and saves it into storage. Please see US6-2 -
+ * GSMA RCC.71 (RCS Universal Profile Service Definition Document)
+ *
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setGroupIcon(@Nullable Uri groupIcon) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setGroupThreadIcon(mThreadId, groupIcon));
+ }
+
+ /**
+ * @return Returns the owner of this thread or {@code null} if there doesn't exist an owner
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @Nullable
+ @WorkerThread
+ public RcsParticipant getOwner() throws RcsMessageStoreException {
+ return new RcsParticipant(RcsControllerCall.call(
+ iRcs -> iRcs.getGroupThreadOwner(mThreadId)));
+ }
+
+ /**
+ * Sets the owner of this {@link RcsGroupThread} and saves it into storage. This is intended to
+ * be used for selecting a new owner for a group thread if the owner leaves the thread. The
+ * owner needs to be in the list of existing participants.
+ *
+ * @param participant The new owner of the thread. {@code null} values are allowed.
+ * @throws RcsMessageStoreException if the operation could not be persisted into storage
+ */
+ @WorkerThread
+ public void setOwner(@Nullable RcsParticipant participant) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(
+ iRcs -> iRcs.setGroupThreadOwner(mThreadId, participant.getId()));
+ }
+
+ /**
+ * Adds a new {@link RcsParticipant} to this group thread and persists into storage. If the user
+ * is actively participating in this {@link RcsGroupThread}, an {@link RcsParticipant} on behalf
+ * of them should be added.
+ *
+ * @param participant The new participant to be added to the thread.
+ * @throws RcsMessageStoreException if the operation could not be persisted into storage
+ */
+ @WorkerThread
+ public void addParticipant(@NonNull RcsParticipant participant)
+ throws RcsMessageStoreException {
+ if (participant == null) {
+ return;
}
- @Override
- public RcsGroupThread[] newArray(int size) {
- return new RcsGroupThread[size];
+ RcsControllerCall.callWithNoReturn(
+ iRcs -> iRcs.addParticipantToGroupThread(mThreadId, participant.getId()));
+ }
+
+ /**
+ * Removes an {@link RcsParticipant} from this group thread and persists into storage. If the
+ * removed participant was the owner of this group, the owner will become null.
+ *
+ * @throws RcsMessageStoreException if the operation could not be persisted into storage
+ */
+ @WorkerThread
+ public void removeParticipant(@NonNull RcsParticipant participant)
+ throws RcsMessageStoreException {
+ if (participant == null) {
+ return;
}
- };
- protected RcsGroupThread(Parcel in) {
- super(in);
+ RcsControllerCall.callWithNoReturn(
+ iRcs -> iRcs.removeParticipantFromGroupThread(mThreadId, participant.getId()));
}
- @Override
- public int describeContents() {
- return 0;
+ /**
+ * Returns the set of {@link RcsParticipant}s that contribute to this group thread. The
+ * returned set does not support modifications, please use
+ * {@link RcsGroupThread#addParticipant(RcsParticipant)}
+ * and {@link RcsGroupThread#removeParticipant(RcsParticipant)} instead.
+ *
+ * @return the immutable set of {@link RcsParticipant} in this group thread.
+ * @throws RcsMessageStoreException if the values could not be read from the storage
+ */
+ @WorkerThread
+ @NonNull
+ public Set<RcsParticipant> getParticipants() throws RcsMessageStoreException {
+ RcsParticipantQueryParams queryParameters =
+ new RcsParticipantQueryParams.Builder().setThread(this).build();
+
+ RcsParticipantQueryResult queryResult = RcsControllerCall.call(
+ iRcs -> iRcs.getParticipants(queryParameters));
+
+ List<RcsParticipant> participantList = queryResult.getParticipants();
+ Set<RcsParticipant> participantSet = new LinkedHashSet<>(participantList);
+ return Collections.unmodifiableSet(participantSet);
}
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(RCS_GROUP_TYPE);
- super.writeToParcel(dest, flags);
+ /**
+ * Returns the conference URI for this {@link RcsGroupThread}. Please see 4.4.5.2 - GSMA RCC.53
+ * (RCS Device API 1.6 Specification
+ *
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @Nullable
+ @WorkerThread
+ public Uri getConferenceUri() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getGroupThreadConferenceUri(mThreadId));
+ }
+
+ /**
+ * Sets the conference URI for this {@link RcsGroupThread} and persists into storage. Please see
+ * 4.4.5.2 - GSMA RCC.53 (RCS Device API 1.6 Specification
+ *
+ * @param conferenceUri The URI as String to be used as the conference URI.
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @Nullable
+ @WorkerThread
+ public void setConferenceUri(Uri conferenceUri) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(
+ iRcs -> iRcs.setGroupThreadConferenceUri(mThreadId, conferenceUri));
}
}
diff --git a/telephony/java/android/telephony/ims/RcsLocationPart.aidl b/telephony/java/android/telephony/ims/RcsGroupThreadEvent.aidl
index 4fe5ca97a30d..77a23722f080 100644
--- a/telephony/java/android/telephony/ims/RcsLocationPart.aidl
+++ b/telephony/java/android/telephony/ims/RcsGroupThreadEvent.aidl
@@ -1,5 +1,4 @@
/*
- *
* Copyright 2019, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -17,4 +16,4 @@
package android.telephony.ims;
-parcelable RcsLocationPart;
+parcelable RcsGroupThreadEvent;
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadEvent.java
new file mode 100644
index 000000000000..609b1740a536
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsGroupThreadEvent.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.ims;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+
+/**
+ * An event that happened on an {@link RcsGroupThread}.
+ */
+public abstract class RcsGroupThreadEvent extends RcsEvent {
+ private final int mRcsGroupThreadId;
+ private final int mOriginatingParticipantId;
+
+ RcsGroupThreadEvent(long timestamp, int rcsGroupThreadId,
+ int originatingParticipantId) {
+ super(timestamp);
+ mRcsGroupThreadId = rcsGroupThreadId;
+ mOriginatingParticipantId = originatingParticipantId;
+ }
+
+ /**
+ * @return Returns the {@link RcsGroupThread} that this event happened on.
+ */
+ @NonNull
+ public RcsGroupThread getRcsGroupThread() {
+ return new RcsGroupThread(mRcsGroupThreadId);
+ }
+
+ /**
+ * @return Returns the {@link RcsParticipant} that performed the event.
+ */
+ @NonNull
+ public RcsParticipant getOriginatingParticipant() {
+ return new RcsParticipant(mOriginatingParticipantId);
+ }
+
+ /**
+ * @hide
+ */
+ RcsGroupThreadEvent(Parcel in) {
+ super(in);
+ mRcsGroupThreadId = in.readInt();
+ mOriginatingParticipantId = in.readInt();
+ }
+
+ /**
+ * @hide
+ */
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeInt(mRcsGroupThreadId);
+ dest.writeInt(mOriginatingParticipantId);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.aidl b/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.aidl
new file mode 100644
index 000000000000..daea7922f3df
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+parcelable RcsGroupThreadIconChangedEvent;
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.java
new file mode 100644
index 000000000000..e768439d6cfa
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.ims;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * An event that indicates an {@link RcsGroupThread}'s icon was changed. Please see R6-2-5 - GSMA
+ * RCC.71 (RCS Universal Profile Service Definition Document)
+ */
+public final class RcsGroupThreadIconChangedEvent extends RcsGroupThreadEvent implements
+ Parcelable {
+ private final Uri mNewIcon;
+
+ /**
+ * Creates a new {@link RcsGroupThreadIconChangedEvent}. This event is not persisted into
+ * storage until {@link RcsMessageStore#persistRcsEvent(RcsEvent)} is called.
+ *
+ * @param timestamp The timestamp of when this event happened, in milliseconds passed after
+ * midnight, January 1st, 1970 UTC
+ * @param rcsGroupThread The {@link RcsGroupThread} that this event happened on
+ * @param originatingParticipant The {@link RcsParticipant} that changed the
+ * {@link RcsGroupThread}'s icon.
+ * @param newIcon {@link Uri} to the new icon of this {@link RcsGroupThread}
+ * @see RcsMessageStore#persistRcsEvent(RcsEvent)
+ */
+ public RcsGroupThreadIconChangedEvent(long timestamp, @NonNull RcsGroupThread rcsGroupThread,
+ @NonNull RcsParticipant originatingParticipant, @Nullable Uri newIcon) {
+ super(timestamp, rcsGroupThread.getThreadId(), originatingParticipant.getId());
+ mNewIcon = newIcon;
+ }
+
+ /**
+ * @hide - internal constructor for queries
+ */
+ public RcsGroupThreadIconChangedEvent(long timestamp, int rcsGroupThreadId,
+ int originatingParticipantId, @Nullable Uri newIcon) {
+ super(timestamp, rcsGroupThreadId, originatingParticipantId);
+ mNewIcon = newIcon;
+ }
+
+ /**
+ * @return Returns the {@link Uri} to the icon of the {@link RcsGroupThread} after this
+ * {@link RcsGroupThreadIconChangedEvent} occured.
+ */
+ @Nullable
+ public Uri getNewIcon() {
+ return mNewIcon;
+ }
+
+ /**
+ * Persists the event to the data store.
+ *
+ * @hide - not meant for public use.
+ */
+ @Override
+ public void persist() throws RcsMessageStoreException {
+ // TODO ensure failure throws
+ RcsControllerCall.call(iRcs -> iRcs.createGroupThreadIconChangedEvent(
+ getTimestamp(), getRcsGroupThread().getThreadId(),
+ getOriginatingParticipant().getId(), mNewIcon));
+ }
+
+ public static final Creator<RcsGroupThreadIconChangedEvent> CREATOR =
+ new Creator<RcsGroupThreadIconChangedEvent>() {
+ @Override
+ public RcsGroupThreadIconChangedEvent createFromParcel(Parcel in) {
+ return new RcsGroupThreadIconChangedEvent(in);
+ }
+
+ @Override
+ public RcsGroupThreadIconChangedEvent[] newArray(int size) {
+ return new RcsGroupThreadIconChangedEvent[size];
+ }
+ };
+
+ private RcsGroupThreadIconChangedEvent(Parcel in) {
+ super(in);
+ mNewIcon = in.readParcelable(Uri.class.getClassLoader());
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeParcelable(mNewIcon, flags);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.aidl b/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.aidl
new file mode 100644
index 000000000000..3ed9bd11dc70
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.aidl
@@ -0,0 +1,20 @@
+/*
+ *
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+parcelable RcsGroupThreadNameChangedEvent;
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.java
new file mode 100644
index 000000000000..02030bc6a2ec
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.ims;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * An event that indicates an {@link RcsGroupThread}'s name was changed. Please see R6-2-5 - GSMA
+ * RCC.71 (RCS Universal Profile Service Definition Document)
+ */
+public final class RcsGroupThreadNameChangedEvent extends RcsGroupThreadEvent implements
+ Parcelable {
+ private final String mNewName;
+
+ /**
+ * Creates a new {@link RcsGroupThreadNameChangedEvent}. This event is not persisted into
+ * storage until {@link RcsMessageStore#persistRcsEvent(RcsEvent)} is called.
+ *
+ * @param timestamp The timestamp of when this event happened, in milliseconds passed after
+ * midnight, January 1st, 1970 UTC
+ * @param rcsGroupThread The {@link RcsGroupThread} that this event happened on
+ * @param originatingParticipant The {@link RcsParticipant} that changed the
+ * {@link RcsGroupThread}'s icon.
+ * @param newName The new name of the {@link RcsGroupThread}
+ * @see RcsMessageStore#persistRcsEvent(RcsEvent)
+ */
+ public RcsGroupThreadNameChangedEvent(long timestamp, @NonNull RcsGroupThread rcsGroupThread,
+ @NonNull RcsParticipant originatingParticipant, @Nullable String newName) {
+ super(timestamp, rcsGroupThread.getThreadId(), originatingParticipant.getId());
+ mNewName = newName;
+ }
+
+ /**
+ * @hide - internal constructor for queries
+ */
+ public RcsGroupThreadNameChangedEvent(long timestamp, int rcsGroupThreadId,
+ int originatingParticipantId, @Nullable String newName) {
+ super(timestamp, rcsGroupThreadId, originatingParticipantId);
+ mNewName = newName;
+ }
+
+ /**
+ * @return Returns the name of this {@link RcsGroupThread} after this
+ * {@link RcsGroupThreadNameChangedEvent} happened.
+ */
+ @Nullable
+ public String getNewName() {
+ return mNewName;
+ }
+
+ /**
+ * Persists the event to the data store.
+ *
+ * @hide - not meant for public use.
+ */
+ @Override
+ public void persist() throws RcsMessageStoreException {
+ RcsControllerCall.call(iRcs -> iRcs.createGroupThreadNameChangedEvent(
+ getTimestamp(), getRcsGroupThread().getThreadId(),
+ getOriginatingParticipant().getId(), mNewName));
+ }
+
+ public static final Creator<RcsGroupThreadNameChangedEvent> CREATOR =
+ new Creator<RcsGroupThreadNameChangedEvent>() {
+ @Override
+ public RcsGroupThreadNameChangedEvent createFromParcel(Parcel in) {
+ return new RcsGroupThreadNameChangedEvent(in);
+ }
+
+ @Override
+ public RcsGroupThreadNameChangedEvent[] newArray(int size) {
+ return new RcsGroupThreadNameChangedEvent[size];
+ }
+ };
+
+ private RcsGroupThreadNameChangedEvent(Parcel in) {
+ super(in);
+ mNewName = in.readString();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeString(mNewName);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.aidl b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.aidl
new file mode 100644
index 000000000000..420abffa067a
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.aidl
@@ -0,0 +1,20 @@
+/*
+ *
+ * Copyright 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+parcelable RcsGroupThreadParticipantJoinedEvent;
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.java
new file mode 100644
index 000000000000..0d1a5730f0a0
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.ims;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * An event that indicates an RCS participant has joined an {@link RcsThread}. Please see US6-3 -
+ * GSMA RCC.71 (RCS Universal Profile Service Definition Document)
+ */
+public final class RcsGroupThreadParticipantJoinedEvent extends RcsGroupThreadEvent implements
+ Parcelable {
+ private final int mJoinedParticipantId;
+
+ /**
+ * Creates a new {@link RcsGroupThreadParticipantJoinedEvent}. This event is not persisted into
+ * storage until {@link RcsMessageStore#persistRcsEvent(RcsEvent)} is called.
+ *
+ * @param timestamp The timestamp of when this event happened, in milliseconds passed after
+ * midnight, January 1st, 1970 UTC
+ * @param rcsGroupThread The {@link RcsGroupThread} that this event happened on
+ * @param originatingParticipant The {@link RcsParticipant} that added or invited the new
+ * {@link RcsParticipant} into the {@link RcsGroupThread}
+ * @param joinedParticipant The new {@link RcsParticipant} that joined the
+ * {@link RcsGroupThread}
+ * @see RcsMessageStore#persistRcsEvent(RcsEvent)
+ */
+ public RcsGroupThreadParticipantJoinedEvent(long timestamp,
+ @NonNull RcsGroupThread rcsGroupThread, @NonNull RcsParticipant originatingParticipant,
+ @NonNull RcsParticipant joinedParticipant) {
+ super(timestamp, rcsGroupThread.getThreadId(), originatingParticipant.getId());
+ mJoinedParticipantId = joinedParticipant.getId();
+ }
+
+ /**
+ * @hide - internal constructor for queries
+ */
+ public RcsGroupThreadParticipantJoinedEvent(long timestamp, int rcsGroupThreadId,
+ int originatingParticipantId, int joinedParticipantId) {
+ super(timestamp, rcsGroupThreadId, originatingParticipantId);
+ mJoinedParticipantId = joinedParticipantId;
+ }
+
+ /**
+ * @return Returns the {@link RcsParticipant} that joined the associated {@link RcsGroupThread}
+ */
+ public RcsParticipant getJoinedParticipant() {
+ return new RcsParticipant(mJoinedParticipantId);
+ }
+
+ /**
+ * Persists the event to the data store.
+ *
+ * @hide - not meant for public use.
+ */
+ @Override
+ public void persist() throws RcsMessageStoreException {
+ RcsControllerCall.call(
+ iRcs -> iRcs.createGroupThreadParticipantJoinedEvent(getTimestamp(),
+ getRcsGroupThread().getThreadId(), getOriginatingParticipant().getId(),
+ getJoinedParticipant().getId()));
+ }
+
+ public static final Creator<RcsGroupThreadParticipantJoinedEvent> CREATOR =
+ new Creator<RcsGroupThreadParticipantJoinedEvent>() {
+ @Override
+ public RcsGroupThreadParticipantJoinedEvent createFromParcel(Parcel in) {
+ return new RcsGroupThreadParticipantJoinedEvent(in);
+ }
+
+ @Override
+ public RcsGroupThreadParticipantJoinedEvent[] newArray(int size) {
+ return new RcsGroupThreadParticipantJoinedEvent[size];
+ }
+ };
+
+ private RcsGroupThreadParticipantJoinedEvent(Parcel in) {
+ super(in);
+ mJoinedParticipantId = in.readInt();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeInt(mJoinedParticipantId);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsMessage.aidl b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.aidl
index b32cd1208c40..ff139ac0ab1e 100644
--- a/telephony/java/android/telephony/ims/RcsMessage.aidl
+++ b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.aidl
@@ -17,4 +17,4 @@
package android.telephony.ims;
-parcelable RcsMessage;
+parcelable RcsGroupThreadParticipantLeftEvent;
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.java
new file mode 100644
index 000000000000..cd525086749a
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.ims;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * An event that indicates an RCS participant has left an {@link RcsThread}. Please see US6-23 -
+ * GSMA RCC.71 (RCS Universal Profile Service Definition Document)
+ */
+public final class RcsGroupThreadParticipantLeftEvent extends RcsGroupThreadEvent implements
+ Parcelable {
+ private final int mLeavingParticipantId;
+
+ /**
+ * Creates a new {@link RcsGroupThreadParticipantLeftEvent}. his event is not persisted into
+ * storage until {@link RcsMessageStore#persistRcsEvent(RcsEvent)} is called.
+ *
+ * @param timestamp The timestamp of when this event happened, in milliseconds passed after
+ * midnight, January 1st, 1970 UTC
+ * @param rcsGroupThread The {@link RcsGroupThread} that this event happened on
+ * @param originatingParticipant The {@link RcsParticipant} that removed the
+ * {@link RcsParticipant} from the {@link RcsGroupThread}. It is
+ * possible that originatingParticipant and leavingParticipant are
+ * the same (i.e. {@link RcsParticipant} left the group
+ * themselves)
+ * @param leavingParticipant The {@link RcsParticipant} that left the {@link RcsGroupThread}
+ * @see RcsMessageStore#persistRcsEvent(RcsEvent)
+ */
+ public RcsGroupThreadParticipantLeftEvent(long timestamp,
+ @NonNull RcsGroupThread rcsGroupThread, @NonNull RcsParticipant originatingParticipant,
+ @NonNull RcsParticipant leavingParticipant) {
+ super(timestamp, rcsGroupThread.getThreadId(), originatingParticipant.getId());
+ mLeavingParticipantId = leavingParticipant.getId();
+ }
+
+ /**
+ * @hide - internal constructor for queries
+ */
+ public RcsGroupThreadParticipantLeftEvent(long timestamp, int rcsGroupThreadId,
+ int originatingParticipantId, int leavingParticipantId) {
+ super(timestamp, rcsGroupThreadId, originatingParticipantId);
+ mLeavingParticipantId = leavingParticipantId;
+ }
+
+ /**
+ * @return Returns the {@link RcsParticipant} that left the associated {@link RcsGroupThread}
+ * after this {@link RcsGroupThreadParticipantLeftEvent} happened.
+ */
+ @NonNull
+ public RcsParticipant getLeavingParticipantId() {
+ return new RcsParticipant(mLeavingParticipantId);
+ }
+
+ @Override
+ public void persist() throws RcsMessageStoreException {
+ RcsControllerCall.call(
+ iRcs -> iRcs.createGroupThreadParticipantJoinedEvent(getTimestamp(),
+ getRcsGroupThread().getThreadId(), getOriginatingParticipant().getId(),
+ getLeavingParticipantId().getId()));
+ }
+
+ public static final Creator<RcsGroupThreadParticipantLeftEvent> CREATOR =
+ new Creator<RcsGroupThreadParticipantLeftEvent>() {
+ @Override
+ public RcsGroupThreadParticipantLeftEvent createFromParcel(Parcel in) {
+ return new RcsGroupThreadParticipantLeftEvent(in);
+ }
+
+ @Override
+ public RcsGroupThreadParticipantLeftEvent[] newArray(int size) {
+ return new RcsGroupThreadParticipantLeftEvent[size];
+ }
+ };
+
+ private RcsGroupThreadParticipantLeftEvent(Parcel in) {
+ super(in);
+ mLeavingParticipantId = in.readInt();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeInt(mLeavingParticipantId);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsIncomingMessage.java b/telephony/java/android/telephony/ims/RcsIncomingMessage.java
index f39e06db068a..61911abd00c5 100644
--- a/telephony/java/android/telephony/ims/RcsIncomingMessage.java
+++ b/telephony/java/android/telephony/ims/RcsIncomingMessage.java
@@ -15,34 +15,80 @@
*/
package android.telephony.ims;
-import android.os.Parcel;
+import android.annotation.WorkerThread;
/**
* This is a single instance of a message received over RCS.
- * @hide - TODO(sahinc) make this public
*/
public class RcsIncomingMessage extends RcsMessage {
- public static final Creator<RcsIncomingMessage> CREATOR = new Creator<RcsIncomingMessage>() {
- @Override
- public RcsIncomingMessage createFromParcel(Parcel in) {
- return new RcsIncomingMessage(in);
- }
-
- @Override
- public RcsIncomingMessage[] newArray(int size) {
- return new RcsIncomingMessage[size];
- }
- };
-
- protected RcsIncomingMessage(Parcel in) {
+ /**
+ * @hide
+ */
+ RcsIncomingMessage(int id) {
+ super(id);
}
- @Override
- public int describeContents() {
- return 0;
+ /**
+ * Sets the timestamp of arrival for this message and persists into storage. The timestamp is
+ * defined as milliseconds passed after midnight, January 1, 1970 UTC
+ *
+ * @param arrivalTimestamp The timestamp to set to.
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setArrivalTimestamp(long arrivalTimestamp) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(
+ iRcs -> iRcs.setMessageArrivalTimestamp(mId, true, arrivalTimestamp));
+ }
+
+ /**
+ * @return Returns the timestamp of arrival for this message. The timestamp is defined as
+ * milliseconds passed after midnight, January 1, 1970 UTC
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public long getArrivalTimestamp() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getMessageArrivalTimestamp(mId, true));
+ }
+
+ /**
+ * Sets the timestamp of when the user saw this message and persists into storage. The timestamp
+ * is defined as milliseconds passed after midnight, January 1, 1970 UTC
+ *
+ * @param notifiedTimestamp The timestamp to set to.
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setSeenTimestamp(long notifiedTimestamp) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(
+ iRcs -> iRcs.setMessageSeenTimestamp(mId, true, notifiedTimestamp));
+ }
+
+ /**
+ * @return Returns the timestamp of when the user saw this message. The timestamp is defined as
+ * milliseconds passed after midnight, January 1, 1970 UTC
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public long getSeenTimestamp() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getMessageSeenTimestamp(mId, true));
+ }
+
+ /**
+ * @return Returns the sender of this incoming message.
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public RcsParticipant getSenderParticipant() throws RcsMessageStoreException {
+ return new RcsParticipant(
+ RcsControllerCall.call(iRcs -> iRcs.getSenderParticipant(mId)));
}
+ /**
+ * @return Returns {@code true} as this is an incoming message
+ */
@Override
- public void writeToParcel(Parcel dest, int flags) {
+ public boolean isIncoming() {
+ return true;
}
}
diff --git a/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.aidl b/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.aidl
new file mode 100644
index 000000000000..1f1d4f68213a
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.aidl
@@ -0,0 +1,20 @@
+/*
+ *
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+parcelable RcsIncomingMessageCreationParams;
diff --git a/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.java b/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.java
new file mode 100644
index 000000000000..61dedbc1578a
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.annotation.CheckResult;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * {@link RcsIncomingMessageCreationParams} is a collection of parameters that should be passed
+ * into {@link RcsThread#addIncomingMessage(RcsIncomingMessageCreationParams)} to generate an
+ * {@link RcsIncomingMessage} on that {@link RcsThread}
+ */
+public final class RcsIncomingMessageCreationParams extends RcsMessageCreationParams implements
+ Parcelable {
+ // The arrival timestamp for the RcsIncomingMessage to be created
+ private final long mArrivalTimestamp;
+ // The seen timestamp for the RcsIncomingMessage to be created
+ private final long mSeenTimestamp;
+ // The participant that sent this incoming message
+ private final int mSenderParticipantId;
+
+ /**
+ * Builder to help create an {@link RcsIncomingMessageCreationParams}
+ *
+ * @see RcsThread#addIncomingMessage(RcsIncomingMessageCreationParams)
+ */
+ public static class Builder extends RcsMessageCreationParams.Builder {
+ private RcsParticipant mSenderParticipant;
+ private long mArrivalTimestamp;
+ private long mSeenTimestamp;
+
+ /**
+ * Creates a {@link Builder} to create an instance of
+ * {@link RcsIncomingMessageCreationParams}
+ *
+ * @param originationTimestamp The timestamp of {@link RcsMessage} creation. The origination
+ * timestamp value in milliseconds passed after midnight,
+ * January 1, 1970 UTC
+ * @param arrivalTimestamp The timestamp of arrival, defined as milliseconds passed after
+ * midnight, January 1, 1970 UTC
+ * @param subscriptionId The subscription ID that was used to send or receive this
+ * {@link RcsMessage}
+ */
+ public Builder(long originationTimestamp, long arrivalTimestamp, int subscriptionId) {
+ super(originationTimestamp, subscriptionId);
+ mArrivalTimestamp = arrivalTimestamp;
+ }
+
+ /**
+ * Sets the {@link RcsParticipant} that send this {@link RcsIncomingMessage}
+ *
+ * @param senderParticipant The {@link RcsParticipant} that sent this
+ * {@link RcsIncomingMessage}
+ * @return The same instance of {@link Builder} to chain methods.
+ */
+ @CheckResult
+ public Builder setSenderParticipant(RcsParticipant senderParticipant) {
+ mSenderParticipant = senderParticipant;
+ return this;
+ }
+
+ /**
+ * Sets the time of the arrival of this {@link RcsIncomingMessage}
+
+ * @return The same instance of {@link Builder} to chain methods.
+ * @see RcsIncomingMessage#setArrivalTimestamp(long)
+ */
+ @CheckResult
+ public Builder setArrivalTimestamp(long arrivalTimestamp) {
+ mArrivalTimestamp = arrivalTimestamp;
+ return this;
+ }
+
+ /**
+ * Sets the time of the when this user saw the {@link RcsIncomingMessage}
+ * @param seenTimestamp The seen timestamp , defined as milliseconds passed after midnight,
+ * January 1, 1970 UTC
+ * @return The same instance of {@link Builder} to chain methods.
+ * @see RcsIncomingMessage#setSeenTimestamp(long)
+ */
+ @CheckResult
+ public Builder setSeenTimestamp(long seenTimestamp) {
+ mSeenTimestamp = seenTimestamp;
+ return this;
+ }
+
+ /**
+ * Creates parameters for creating a new incoming message.
+ * @return A new instance of {@link RcsIncomingMessageCreationParams} to create a new
+ * {@link RcsIncomingMessage}
+ */
+ public RcsIncomingMessageCreationParams build() {
+ return new RcsIncomingMessageCreationParams(this);
+ }
+ }
+
+ private RcsIncomingMessageCreationParams(Builder builder) {
+ super(builder);
+ mArrivalTimestamp = builder.mArrivalTimestamp;
+ mSeenTimestamp = builder.mSeenTimestamp;
+ mSenderParticipantId = builder.mSenderParticipant.getId();
+ }
+
+ private RcsIncomingMessageCreationParams(Parcel in) {
+ super(in);
+ mArrivalTimestamp = in.readLong();
+ mSeenTimestamp = in.readLong();
+ mSenderParticipantId = in.readInt();
+ }
+
+ /**
+ * @return Returns the arrival timestamp for the {@link RcsIncomingMessage} to be created.
+ * Timestamp is defined as milliseconds passed after midnight, January 1, 1970 UTC
+ */
+ public long getArrivalTimestamp() {
+ return mArrivalTimestamp;
+ }
+
+ /**
+ * @return Returns the seen timestamp for the {@link RcsIncomingMessage} to be created.
+ * Timestamp is defined as milliseconds passed after midnight, January 1, 1970 UTC
+ */
+ public long getSeenTimestamp() {
+ return mSeenTimestamp;
+ }
+
+ /**
+ * Helper getter for {@link com.android.internal.telephony.ims.RcsMessageStoreController} to
+ * create {@link RcsIncomingMessage}s
+ *
+ * Since the API doesn't expose any ID's to API users, this should be hidden.
+ * @hide
+ */
+ public int getSenderParticipantId() {
+ return mSenderParticipantId;
+ }
+
+ public static final Creator<RcsIncomingMessageCreationParams> CREATOR =
+ new Creator<RcsIncomingMessageCreationParams>() {
+ @Override
+ public RcsIncomingMessageCreationParams createFromParcel(Parcel in) {
+ return new RcsIncomingMessageCreationParams(in);
+ }
+
+ @Override
+ public RcsIncomingMessageCreationParams[] newArray(int size) {
+ return new RcsIncomingMessageCreationParams[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest);
+ dest.writeLong(mArrivalTimestamp);
+ dest.writeLong(mSeenTimestamp);
+ dest.writeInt(mSenderParticipantId);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsLocationPart.java b/telephony/java/android/telephony/ims/RcsLocationPart.java
deleted file mode 100644
index 19be4ceaf688..000000000000
--- a/telephony/java/android/telephony/ims/RcsLocationPart.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import android.os.Parcel;
-
-/**
- * A part of a composite {@link RcsMessage} that holds a location
- * @hide - TODO(sahinc) make this public
- */
-public class RcsLocationPart extends RcsPart {
- public static final Creator<RcsLocationPart> CREATOR = new Creator<RcsLocationPart>() {
- @Override
- public RcsLocationPart createFromParcel(Parcel in) {
- return new RcsLocationPart(in);
- }
-
- @Override
- public RcsLocationPart[] newArray(int size) {
- return new RcsLocationPart[size];
- }
- };
-
- protected RcsLocationPart(Parcel in) {
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsManager.java b/telephony/java/android/telephony/ims/RcsManager.java
index df108c88e3b0..22e4b2249c36 100644
--- a/telephony/java/android/telephony/ims/RcsManager.java
+++ b/telephony/java/android/telephony/ims/RcsManager.java
@@ -20,15 +20,21 @@ import android.content.Context;
/**
* The manager class for RCS related utilities.
- * @hide
*/
@SystemService(Context.TELEPHONY_RCS_SERVICE)
public class RcsManager {
+ /**
+ * @hide
+ */
+ public RcsManager() {
+ // empty constructor
+ }
+
private static final RcsMessageStore sRcsMessageStoreInstance = new RcsMessageStore();
/**
- * Returns an instance of RcsMessageStore.
+ * Returns an instance of {@link RcsMessageStore}
*/
public RcsMessageStore getRcsMessageStore() {
return sRcsMessageStoreInstance;
diff --git a/telephony/java/android/telephony/ims/RcsMessage.java b/telephony/java/android/telephony/ims/RcsMessage.java
index d46685c4a572..32274131a5ad 100644
--- a/telephony/java/android/telephony/ims/RcsMessage.java
+++ b/telephony/java/android/telephony/ims/RcsMessage.java
@@ -15,11 +15,315 @@
*/
package android.telephony.ims;
-import android.os.Parcelable;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.WorkerThread;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
/**
* This is a single instance of a message sent or received over RCS.
- * @hide - TODO(sahinc) make this public
*/
-public abstract class RcsMessage implements Parcelable {
+public abstract class RcsMessage {
+ /**
+ * The value to indicate that this {@link RcsMessage} does not have any location information.
+ */
+ public static final double LOCATION_NOT_SET = Double.MIN_VALUE;
+
+ /**
+ * The status to indicate that this {@link RcsMessage}s status is not set yet.
+ */
+ public static final int NOT_SET = 0;
+
+ /**
+ * The status to indicate that this {@link RcsMessage} is a draft and is not in the process of
+ * sending yet.
+ */
+ public static final int DRAFT = 1;
+
+ /**
+ * The status to indicate that this {@link RcsMessage} was successfully sent.
+ */
+ public static final int QUEUED = 2;
+
+ /**
+ * The status to indicate that this {@link RcsMessage} is actively being sent.
+ */
+ public static final int SENDING = 3;
+
+ /**
+ * The status to indicate that this {@link RcsMessage} was successfully sent.
+ */
+ public static final int SENT = 4;
+
+ /**
+ * The status to indicate that this {@link RcsMessage} failed to send in an attempt before, and
+ * now being retried.
+ */
+ public static final int RETRYING = 5;
+
+ /**
+ * The status to indicate that this {@link RcsMessage} has permanently failed to send.
+ */
+ public static final int FAILED = 6;
+
+ /**
+ * The status to indicate that this {@link RcsMessage} was successfully received.
+ */
+ public static final int RECEIVED = 7;
+
+ /**
+ * The status to indicate that this {@link RcsMessage} was seen.
+ */
+ public static final int SEEN = 9;
+
+ /**
+ * @hide
+ */
+ protected final int mId;
+
+ @IntDef({
+ DRAFT, QUEUED, SENDING, SENT, RETRYING, FAILED, RECEIVED, SEEN
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RcsMessageStatus {
+ }
+
+ RcsMessage(int id) {
+ mId = id;
+ }
+
+ /**
+ * Returns the row Id from the common message.
+ *
+ * @hide
+ */
+ public int getId() {
+ return mId;
+ }
+
+ /**
+ * @return Returns the subscription ID that this {@link RcsMessage} was sent from, or delivered
+ * to.
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ * @see android.telephony.SubscriptionInfo#getSubscriptionId
+ */
+ public int getSubscriptionId() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getMessageSubId(mId, isIncoming()));
+ }
+
+ /**
+ * Sets the subscription ID that this {@link RcsMessage} was sent from, or delivered to and
+ * persists it into storage.
+ *
+ * @param subId The subscription ID to persists into storage.
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ * @see android.telephony.SubscriptionInfo#getSubscriptionId
+ */
+ @WorkerThread
+ public void setSubscriptionId(int subId) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setMessageSubId(mId, isIncoming(), subId));
+ }
+
+ /**
+ * Sets the status of this message and persists it into storage. Please see
+ * {@link RcsFileTransferPart#setFileTransferStatus(int)} to set statuses around file transfers.
+ *
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setStatus(@RcsMessageStatus int rcsMessageStatus) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(
+ iRcs -> iRcs.setMessageStatus(mId, isIncoming(), rcsMessageStatus));
+ }
+
+ /**
+ * @return Returns the status of this message. Please see
+ * {@link RcsFileTransferPart#setFileTransferStatus(int)} to set statuses around file transfers.
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public @RcsMessageStatus int getStatus() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getMessageStatus(mId, isIncoming()));
+ }
+
+ /**
+ * Sets the origination timestamp of this message and persists it into storage. Origination is
+ * defined as when the sender tapped the send button.
+ *
+ * @param timestamp The origination timestamp value in milliseconds passed after midnight,
+ * January 1, 1970 UTC
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setOriginationTimestamp(long timestamp) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(
+ iRcs -> iRcs.setMessageOriginationTimestamp(mId, isIncoming(), timestamp));
+ }
+
+ /**
+ * @return Returns the origination timestamp of this message in milliseconds passed after
+ * midnight, January 1, 1970 UTC. Origination is defined as when the sender tapped the send
+ * button.
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public long getOriginationTimestamp() throws RcsMessageStoreException {
+ return RcsControllerCall.call(
+ iRcs -> iRcs.getMessageOriginationTimestamp(mId, isIncoming()));
+ }
+
+ /**
+ * Sets the globally unique RCS message identifier for this message and persists it into
+ * storage. This function does not confirm that this message id is unique. Please see 4.4.5.2
+ * - GSMA RCC.53 (RCS Device API 1.6 Specification
+ *
+ * @param rcsMessageGlobalId The globally RCS message identifier
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setRcsMessageId(String rcsMessageGlobalId) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(
+ iRcs -> iRcs.setGlobalMessageIdForMessage(mId, isIncoming(), rcsMessageGlobalId));
+ }
+
+ /**
+ * @return Returns the globally unique RCS message identifier for this message. Please see
+ * 4.4.5.2 - GSMA RCC.53 (RCS Device API 1.6 Specification
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public String getRcsMessageId() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getGlobalMessageIdForMessage(mId, isIncoming()));
+ }
+
+ /**
+ * @return Returns the user visible text included in this message.
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public String getText() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getTextForMessage(mId, isIncoming()));
+ }
+
+ /**
+ * Sets the user visible text for this message and persists in storage.
+ *
+ * @param text The text this message now has
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setText(String text) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setTextForMessage(mId, isIncoming(), text));
+ }
+
+ /**
+ * @return Returns the associated latitude for this message, or
+ * {@link RcsMessage#LOCATION_NOT_SET} if it does not contain a location.
+ *
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public double getLatitude() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getLatitudeForMessage(mId, isIncoming()));
+ }
+
+ /**
+ * Sets the latitude for this message and persists in storage.
+ *
+ * @param latitude The latitude for this location message.
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setLatitude(double latitude) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(
+ iRcs -> iRcs.setLatitudeForMessage(mId, isIncoming(), latitude));
+ }
+
+ /**
+ * @return Returns the associated longitude for this message, or
+ * {@link RcsMessage#LOCATION_NOT_SET} if it does not contain a location.
+ *
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public double getLongitude() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getLongitudeForMessage(mId, isIncoming()));
+ }
+
+ /**
+ * Sets the longitude for this message and persists in storage.
+ *
+ * @param longitude The longitude for this location message.
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setLongitude(double longitude) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(
+ iRcs -> iRcs.setLongitudeForMessage(mId, isIncoming(), longitude));
+ }
+
+ /**
+ * Attaches an {@link RcsFileTransferPart} to this message and persists into storage.
+ *
+ * @param fileTransferCreationParameters The parameters to be used to create the
+ * {@link RcsFileTransferPart}
+ * @return A new instance of {@link RcsFileTransferPart}
+ * @throws RcsMessageStoreException if the file transfer could not be persisted into storage.
+ */
+ @NonNull
+ @WorkerThread
+ public RcsFileTransferPart insertFileTransfer(
+ RcsFileTransferCreationParams fileTransferCreationParameters)
+ throws RcsMessageStoreException {
+ return new RcsFileTransferPart(RcsControllerCall.call(
+ iRcs -> iRcs.storeFileTransfer(mId, isIncoming(), fileTransferCreationParameters)));
+ }
+
+ /**
+ * @return Returns all the {@link RcsFileTransferPart}s associated with this message in an
+ * unmodifiable set.
+ * @throws RcsMessageStoreException if the file transfers could not be read from the storage
+ */
+ @NonNull
+ @WorkerThread
+ public Set<RcsFileTransferPart> getFileTransferParts() throws RcsMessageStoreException {
+ Set<RcsFileTransferPart> fileTransferParts = new HashSet<>();
+
+ int[] fileTransferIds = RcsControllerCall.call(
+ iRcs -> iRcs.getFileTransfersAttachedToMessage(mId, isIncoming()));
+
+ for (int fileTransfer : fileTransferIds) {
+ fileTransferParts.add(new RcsFileTransferPart(fileTransfer));
+ }
+
+ return Collections.unmodifiableSet(fileTransferParts);
+ }
+
+ /**
+ * Removes a {@link RcsFileTransferPart} from this message, and deletes it in storage.
+ *
+ * @param fileTransferPart The part to delete.
+ * @throws RcsMessageStoreException if the file transfer could not be removed from storage
+ */
+ @WorkerThread
+ public void removeFileTransferPart(@NonNull RcsFileTransferPart fileTransferPart)
+ throws RcsMessageStoreException {
+ if (fileTransferPart == null) {
+ return;
+ }
+
+ RcsControllerCall.callWithNoReturn(
+ iRcs -> iRcs.deleteFileTransfer(fileTransferPart.getId()));
+ }
+
+ /**
+ * @return Returns {@code true} if this message was received on this device, {@code false} if it
+ * was sent.
+ */
+ public abstract boolean isIncoming();
}
diff --git a/telephony/java/android/telephony/ims/RcsMessageCreationParams.java b/telephony/java/android/telephony/ims/RcsMessageCreationParams.java
new file mode 100644
index 000000000000..c46c605d861d
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsMessageCreationParams.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import static android.telephony.ims.RcsMessage.LOCATION_NOT_SET;
+
+import android.annotation.CheckResult;
+import android.annotation.Nullable;
+import android.os.Parcel;
+
+/**
+ * The collection of parameters to be passed into
+ * {@link RcsThread#addIncomingMessage(RcsIncomingMessageCreationParams)} and
+ * {@link RcsThread#addOutgoingMessage(RcsOutgoingMessageCreationParams)} to create and persist
+ * {@link RcsMessage}s on an {@link RcsThread}
+ */
+public class RcsMessageCreationParams {
+ // The globally unique id of the RcsMessage to be created.
+ private final String mRcsMessageGlobalId;
+
+ // The subscription that this message was/will be received/sent from.
+ private final int mSubId;
+ // The sending/receiving status of the message
+ private final @RcsMessage.RcsMessageStatus int mMessageStatus;
+ // The timestamp of message creation
+ private final long mOriginationTimestamp;
+ // The user visible content of the message
+ private final String mText;
+ // The latitude of the message if this is a location message
+ private final double mLatitude;
+ // The longitude of the message if this is a location message
+ private final double mLongitude;
+
+ /**
+ * @return Returns the globally unique RCS Message ID for the {@link RcsMessage} to be created.
+ * Please see 4.4.5.2 - GSMA RCC.53 (RCS Device API 1.6 Specification
+ */
+ @Nullable
+ public String getRcsMessageGlobalId() {
+ return mRcsMessageGlobalId;
+ }
+
+ /**
+ * @return Returns the subscription ID that was used to send or receive the {@link RcsMessage}
+ * to be created.
+ */
+ public int getSubId() {
+ return mSubId;
+ }
+
+ /**
+ * @return Returns the status for the {@link RcsMessage} to be created.
+ * @see RcsMessage.RcsMessageStatus
+ */
+ public int getMessageStatus() {
+ return mMessageStatus;
+ }
+
+ /**
+ * @return Returns the origination timestamp of the {@link RcsMessage} to be created in
+ * milliseconds passed after midnight, January 1, 1970 UTC. Origination is defined as when
+ * the sender tapped the send button.
+ */
+ public long getOriginationTimestamp() {
+ return mOriginationTimestamp;
+ }
+
+ /**
+ * @return Returns the user visible text contained in the {@link RcsMessage} to be created
+ */
+ @Nullable
+ public String getText() {
+ return mText;
+ }
+
+ /**
+ * @return Returns the latitude of the {@link RcsMessage} to be created, or
+ * {@link RcsMessage#LOCATION_NOT_SET} if the message does not contain a location.
+ */
+ public double getLatitude() {
+ return mLatitude;
+ }
+
+ /**
+ * @return Returns the longitude of the {@link RcsMessage} to be created, or
+ * {@link RcsMessage#LOCATION_NOT_SET} if the message does not contain a location.
+ */
+ public double getLongitude() {
+ return mLongitude;
+ }
+
+ /**
+ * The base builder for creating {@link RcsMessage}s on {@link RcsThread}s.
+ *
+ * @see RcsIncomingMessageCreationParams
+ */
+ public static class Builder {
+ private String mRcsMessageGlobalId;
+ private int mSubId;
+ private @RcsMessage.RcsMessageStatus int mMessageStatus;
+ private long mOriginationTimestamp;
+ private String mText;
+ private double mLatitude = LOCATION_NOT_SET;
+ private double mLongitude = LOCATION_NOT_SET;
+
+ /**
+ * @hide
+ */
+ public Builder(long originationTimestamp, int subscriptionId) {
+ mOriginationTimestamp = originationTimestamp;
+ mSubId = subscriptionId;
+ }
+
+ /**
+ * Sets the status of the {@link RcsMessage} to be built.
+ *
+ * @param rcsMessageStatus The status to be set
+ * @return The same instance of {@link Builder} to chain methods
+ * @see RcsMessage#setStatus(int)
+ */
+ @CheckResult
+ public Builder setStatus(@RcsMessage.RcsMessageStatus int rcsMessageStatus) {
+ mMessageStatus = rcsMessageStatus;
+ return this;
+ }
+
+ /**
+ * Sets the globally unique RCS message identifier for the {@link RcsMessage} to be built.
+ * This function does not confirm that this message id is unique. Please see 4.4.5.2 - GSMA
+ * RCC.53 (RCS Device API 1.6 Specification)
+ *
+ * @param rcsMessageId The ID to be set
+ * @return The same instance of {@link Builder} to chain methods
+ * @see RcsMessage#setRcsMessageId(String)
+ */
+ @CheckResult
+ public Builder setRcsMessageId(String rcsMessageId) {
+ mRcsMessageGlobalId = rcsMessageId;
+ return this;
+ }
+
+ /**
+ * Sets the text of the {@link RcsMessage} to be built.
+ *
+ * @param text The user visible text of the message
+ * @return The same instance of {@link Builder} to chain methods
+ * @see RcsMessage#setText(String)
+ */
+ @CheckResult
+ public Builder setText(String text) {
+ mText = text;
+ return this;
+ }
+
+ /**
+ * Sets the latitude of the {@link RcsMessage} to be built. Please see US5-24 - GSMA RCC.71
+ * (RCS Universal Profile Service Definition Document)
+ *
+ * @param latitude The latitude of the location information associated with this message.
+ * @return The same instance of {@link Builder} to chain methods
+ * @see RcsMessage#setLatitude(double)
+ */
+ @CheckResult
+ public Builder setLatitude(double latitude) {
+ mLatitude = latitude;
+ return this;
+ }
+
+ /**
+ * Sets the longitude of the {@link RcsMessage} to be built. Please see US5-24 - GSMA RCC.71
+ * (RCS Universal Profile Service Definition Document)
+ *
+ * @param longitude The longitude of the location information associated with this message.
+ * @return The same instance of {@link Builder} to chain methods
+ * @see RcsMessage#setLongitude(double)
+ */
+ @CheckResult
+ public Builder setLongitude(double longitude) {
+ mLongitude = longitude;
+ return this;
+ }
+
+ /**
+ * @return Builds and returns a newly created {@link RcsMessageCreationParams}
+ */
+ public RcsMessageCreationParams build() {
+ return new RcsMessageCreationParams(this);
+ }
+ }
+
+ protected RcsMessageCreationParams(Builder builder) {
+ mRcsMessageGlobalId = builder.mRcsMessageGlobalId;
+ mSubId = builder.mSubId;
+ mMessageStatus = builder.mMessageStatus;
+ mOriginationTimestamp = builder.mOriginationTimestamp;
+ mText = builder.mText;
+ mLatitude = builder.mLatitude;
+ mLongitude = builder.mLongitude;
+ }
+
+ /**
+ * @hide
+ */
+ RcsMessageCreationParams(Parcel in) {
+ mRcsMessageGlobalId = in.readString();
+ mSubId = in.readInt();
+ mMessageStatus = in.readInt();
+ mOriginationTimestamp = in.readLong();
+ mText = in.readString();
+ mLatitude = in.readDouble();
+ mLongitude = in.readDouble();
+ }
+
+ /**
+ * @hide
+ */
+ public void writeToParcel(Parcel dest) {
+ dest.writeString(mRcsMessageGlobalId);
+ dest.writeInt(mSubId);
+ dest.writeInt(mMessageStatus);
+ dest.writeLong(mOriginationTimestamp);
+ dest.writeString(mText);
+ dest.writeDouble(mLatitude);
+ dest.writeDouble(mLongitude);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsGroupThread.aidl b/telephony/java/android/telephony/ims/RcsMessageQueryParams.aidl
index c4ce5299e512..e9cbd9cc4ebe 100644
--- a/telephony/java/android/telephony/ims/RcsGroupThread.aidl
+++ b/telephony/java/android/telephony/ims/RcsMessageQueryParams.aidl
@@ -17,4 +17,4 @@
package android.telephony.ims;
-parcelable RcsGroupThread;
+parcelable RcsMessageQueryParams;
diff --git a/telephony/java/android/telephony/ims/RcsMessageQueryParams.java b/telephony/java/android/telephony/ims/RcsMessageQueryParams.java
new file mode 100644
index 000000000000..535a597f5e1e
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsMessageQueryParams.java
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.annotation.CheckResult;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.security.InvalidParameterException;
+
+/**
+ * The parameters to pass into
+ * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} in order to select a
+ * subset of {@link RcsMessage}s present in the message store.
+ */
+public final class RcsMessageQueryParams implements Parcelable {
+ /**
+ * @hide - not meant for public use
+ */
+ public static final int THREAD_ID_NOT_SET = -1;
+
+ /**
+ * Flag to be used with {@link Builder#setSortProperty(int)} to denote that the results should
+ * be sorted in the same order of {@link RcsMessage}s that got persisted into storage for faster
+ * results.
+ */
+ public static final int SORT_BY_CREATION_ORDER = 0;
+
+ /**
+ * Flag to be used with {@link Builder#setSortProperty(int)} to denote that the results should
+ * be sorted according to the timestamp of {@link RcsMessage#getOriginationTimestamp()}
+ */
+ public static final int SORT_BY_TIMESTAMP = 1;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({SORT_BY_CREATION_ORDER, SORT_BY_TIMESTAMP})
+ public @interface SortingProperty {
+ }
+
+ /**
+ * Bitmask flag to be used with {@link Builder#setMessageType(int)} to make
+ * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} return
+ * {@link RcsIncomingMessage}s.
+ */
+ public static final int MESSAGE_TYPE_INCOMING = 0x0001;
+
+ /**
+ * Bitmask flag to be used with {@link Builder#setMessageType(int)} to make
+ * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} return
+ * {@link RcsOutgoingMessage}s.
+ */
+ public static final int MESSAGE_TYPE_OUTGOING = 0x0002;
+
+ /**
+ * Bitmask flag to be used with {@link Builder#setFileTransferPresence(int)} to make
+ * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} return {@link RcsMessage}s
+ * that have an {@link RcsFileTransferPart} attached.
+ */
+ public static final int MESSAGES_WITH_FILE_TRANSFERS = 0x0004;
+
+ /**
+ * Bitmask flag to be used with {@link Builder#setFileTransferPresence(int)} to make
+ * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} return {@link RcsMessage}s
+ * that don't have an {@link RcsFileTransferPart} attached.
+ */
+ public static final int MESSAGES_WITHOUT_FILE_TRANSFERS = 0x0008;
+
+ /**
+ * @hide - not meant for public use
+ */
+ public static final String MESSAGE_QUERY_PARAMETERS_KEY = "message_query_parameters";
+
+ // Whether the result should be filtered against incoming or outgoing messages
+ private int mMessageType;
+ // Whether the result should have file transfer messages attached or not
+ private int mFileTransferPresence;
+ // The SQL "Like" clause to filter messages
+ private String mMessageLike;
+ // The property the messages should be sorted against
+ private @SortingProperty int mSortingProperty;
+ // Whether the messages should be sorted in ascending order
+ private boolean mIsAscending;
+ // The number of results that should be returned with this query
+ private int mLimit;
+ // The thread that the results should be limited to
+ private int mThreadId;
+
+ RcsMessageQueryParams(int messageType, int fileTransferPresence, String messageLike,
+ int threadId, @SortingProperty int sortingProperty, boolean isAscending, int limit) {
+ mMessageType = messageType;
+ mFileTransferPresence = fileTransferPresence;
+ mMessageLike = messageLike;
+ mSortingProperty = sortingProperty;
+ mIsAscending = isAscending;
+ mLimit = limit;
+ mThreadId = threadId;
+ }
+
+ /**
+ * @return Returns the type of {@link RcsMessage}s that this {@link RcsMessageQueryParams}
+ * is set to query for.
+ */
+ public int getMessageType() {
+ return mMessageType;
+ }
+
+ /**
+ * @return Returns whether the result query should return {@link RcsMessage}s with
+ * {@link RcsFileTransferPart}s or not
+ */
+ public int getFileTransferPresence() {
+ return mFileTransferPresence;
+ }
+
+ /**
+ * @return Returns the SQL-inspired "LIKE" clause that will be used to match {@link RcsMessage}s
+ */
+ public String getMessageLike() {
+ return mMessageLike;
+ }
+
+ /**
+ * @return Returns the number of {@link RcsThread}s to be returned from the query. A value of
+ * 0 means there is no set limit.
+ */
+ public int getLimit() {
+ return mLimit;
+ }
+
+ /**
+ * @return Returns the property that will be used to sort the result against.
+ * @see SortingProperty
+ */
+ public @SortingProperty int getSortingProperty() {
+ return mSortingProperty;
+ }
+
+ /**
+ * @return Returns {@code true} if the result set will be sorted in ascending order,
+ * {@code false} if it will be sorted in descending order.
+ */
+ public boolean getSortDirection() {
+ return mIsAscending;
+ }
+
+ /**
+ * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get
+ * the thread that the result query should be limited to.
+ *
+ * As we do not expose any sort of integer ID's to public usage, this should be hidden.
+ *
+ * @hide - not meant for public use
+ */
+ public int getThreadId() {
+ return mThreadId;
+ }
+
+ /**
+ * A helper class to build the {@link RcsMessageQueryParams}.
+ */
+ public static class Builder {
+ private @SortingProperty int mSortingProperty;
+ private int mMessageType;
+ private int mFileTransferPresence;
+ private String mMessageLike;
+ private boolean mIsAscending;
+ private int mLimit = 100;
+ private int mThreadId = THREAD_ID_NOT_SET;
+
+ /**
+ * Creates a new builder for {@link RcsMessageQueryParams} to be used in
+ * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)}
+ *
+ */
+ public Builder() {
+ // empty implementation
+ }
+
+ /**
+ * Desired number of threads to be returned from the query. Passing in 0 will return all
+ * existing threads at once. The limit defaults to 100.
+ *
+ * @param limit The number to limit the query result to.
+ * @return The same instance of the builder to chain parameters.
+ * @throws InvalidParameterException If the given limit is negative.
+ */
+ @CheckResult
+ public Builder setResultLimit(@IntRange(from = 0) int limit)
+ throws InvalidParameterException {
+ if (limit < 0) {
+ throw new InvalidParameterException("The query limit must be non-negative");
+ }
+
+ mLimit = limit;
+ return this;
+ }
+
+ /**
+ * Sets the type of messages to be returned from the query.
+ *
+ * @param messageType The type of message to be returned.
+ * @return The same instance of the builder to chain parameters.
+ * @see RcsMessageQueryParams#MESSAGE_TYPE_INCOMING
+ * @see RcsMessageQueryParams#MESSAGE_TYPE_OUTGOING
+ */
+ @CheckResult
+ public Builder setMessageType(int messageType) {
+ mMessageType = messageType;
+ return this;
+ }
+
+ /**
+ * Sets whether file transfer messages should be included in the query result or not.
+ *
+ * @param fileTransferPresence Whether file transfers should be included in the result
+ * @return The same instance of the builder to chain parameters.
+ * @see RcsMessageQueryParams#MESSAGES_WITH_FILE_TRANSFERS
+ * @see RcsMessageQueryParams#MESSAGES_WITHOUT_FILE_TRANSFERS
+ */
+ @CheckResult
+ public Builder setFileTransferPresence(int fileTransferPresence) {
+ mFileTransferPresence = fileTransferPresence;
+ return this;
+ }
+
+ /**
+ * Sets an SQL-inspired "like" clause to match with messages. Using a percent sign ('%')
+ * wildcard matches any sequence of zero or more characters. Using an underscore ('_')
+ * wildcard matches any single character. Not using any wildcards would only perform a
+ * string match. The input string is case-insensitive.
+ *
+ * The input "Wh%" would match messages "who", "where" and "what", while the input "Wh_"
+ * would only match "who"
+ *
+ * @param messageLike The "like" clause for matching {@link RcsMessage}s.
+ * @return The same instance of the builder to chain parameters.
+ */
+ @CheckResult
+ public Builder setMessageLike(String messageLike) {
+ mMessageLike = messageLike;
+ return this;
+ }
+
+ /**
+ * Sets the property where the results should be sorted against. Defaults to
+ * {@link RcsMessageQueryParams.SortingProperty#SORT_BY_CREATION_ORDER}
+ *
+ * @param sortingProperty against which property the results should be sorted
+ * @return The same instance of the builder to chain parameters.
+ */
+ @CheckResult
+ public Builder setSortProperty(@SortingProperty int sortingProperty) {
+ mSortingProperty = sortingProperty;
+ return this;
+ }
+
+ /**
+ * Sets whether the results should be sorted ascending or descending
+ *
+ * @param isAscending whether the results should be sorted ascending
+ * @return The same instance of the builder to chain parameters.
+ */
+ @CheckResult
+ public Builder setSortDirection(boolean isAscending) {
+ mIsAscending = isAscending;
+ return this;
+ }
+
+ /**
+ * Limits the results to the given thread.
+ *
+ * @param thread the {@link RcsThread} that results should be limited to. If set to
+ * {@code null}, messages on all threads will be queried
+ * @return The same instance of the builder to chain parameters.
+ */
+ @CheckResult
+ public Builder setThread(@Nullable RcsThread thread) {
+ if (thread == null) {
+ mThreadId = THREAD_ID_NOT_SET;
+ } else {
+ mThreadId = thread.getThreadId();
+ }
+ return this;
+ }
+
+ /**
+ * Builds the {@link RcsMessageQueryParams} to use in
+ * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)}
+ *
+ * @return An instance of {@link RcsMessageQueryParams} to use with the message
+ * query.
+ */
+ public RcsMessageQueryParams build() {
+ return new RcsMessageQueryParams(mMessageType, mFileTransferPresence, mMessageLike,
+ mThreadId, mSortingProperty, mIsAscending, mLimit);
+ }
+ }
+
+ /**
+ * Parcelable boilerplate below.
+ */
+ private RcsMessageQueryParams(Parcel in) {
+ mMessageType = in.readInt();
+ mFileTransferPresence = in.readInt();
+ mMessageLike = in.readString();
+ mSortingProperty = in.readInt();
+ mIsAscending = in.readBoolean();
+ mLimit = in.readInt();
+ mThreadId = in.readInt();
+ }
+
+ public static final Creator<RcsMessageQueryParams> CREATOR =
+ new Creator<RcsMessageQueryParams>() {
+ @Override
+ public RcsMessageQueryParams createFromParcel(Parcel in) {
+ return new RcsMessageQueryParams(in);
+ }
+
+ @Override
+ public RcsMessageQueryParams[] newArray(int size) {
+ return new RcsMessageQueryParams[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mMessageType);
+ dest.writeInt(mFileTransferPresence);
+ dest.writeString(mMessageLike);
+ dest.writeInt(mSortingProperty);
+ dest.writeBoolean(mIsAscending);
+ dest.writeInt(mLimit);
+ dest.writeInt(mThreadId);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsMessageQueryResult.aidl b/telephony/java/android/telephony/ims/RcsMessageQueryResult.aidl
new file mode 100644
index 000000000000..a73ba50b6591
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsMessageQueryResult.aidl
@@ -0,0 +1,20 @@
+/*
+ *
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+parcelable RcsMessageQueryResult;
diff --git a/telephony/java/android/telephony/ims/RcsMessageQueryResult.java b/telephony/java/android/telephony/ims/RcsMessageQueryResult.java
new file mode 100644
index 000000000000..3514b48e80a1
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsMessageQueryResult.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import static android.provider.Telephony.RcsColumns.RcsUnifiedMessageColumns.MESSAGE_TYPE_INCOMING;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.ims.RcsTypeIdPair;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The result of a {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)}
+ * call. This class allows getting the token for querying the next batch of messages in order to
+ * prevent handling large amounts of data at once.
+ */
+public final class RcsMessageQueryResult implements Parcelable {
+ // The token to continue the query to get the next batch of results
+ private RcsQueryContinuationToken mContinuationToken;
+ // The message type and message ID pairs for all the messages in this query result
+ private List<RcsTypeIdPair> mMessageTypeIdPairs;
+
+ /**
+ * Internal constructor for {@link com.android.internal.telephony.ims.RcsMessageStoreController}
+ * to create query results
+ *
+ * @hide
+ */
+ public RcsMessageQueryResult(
+ RcsQueryContinuationToken continuationToken,
+ List<RcsTypeIdPair> messageTypeIdPairs) {
+ mContinuationToken = continuationToken;
+ mMessageTypeIdPairs = messageTypeIdPairs;
+ }
+
+ /**
+ * Returns a token to call
+ * {@link RcsMessageStore#getRcsMessages(RcsQueryContinuationToken)}
+ * to get the next batch of {@link RcsMessage}s.
+ */
+ @Nullable
+ public RcsQueryContinuationToken getContinuationToken() {
+ return mContinuationToken;
+ }
+
+ /**
+ * Returns all the {@link RcsMessage}s in the current query result. Call {@link
+ * RcsMessageStore#getRcsMessages(RcsQueryContinuationToken)} to get the next batch
+ * of {@link RcsMessage}s.
+ */
+ @NonNull
+ public List<RcsMessage> getMessages() {
+ List<RcsMessage> messages = new ArrayList<>();
+ for (RcsTypeIdPair typeIdPair : mMessageTypeIdPairs) {
+ if (typeIdPair.getType() == MESSAGE_TYPE_INCOMING) {
+ messages.add(new RcsIncomingMessage(typeIdPair.getId()));
+ } else {
+ messages.add(new RcsOutgoingMessage(typeIdPair.getId()));
+ }
+ }
+
+ return messages;
+ }
+
+ private RcsMessageQueryResult(Parcel in) {
+ mContinuationToken = in.readParcelable(
+ RcsQueryContinuationToken.class.getClassLoader());
+ in.readTypedList(mMessageTypeIdPairs, RcsTypeIdPair.CREATOR);
+ }
+
+ public static final Creator<RcsMessageQueryResult> CREATOR =
+ new Creator<RcsMessageQueryResult>() {
+ @Override
+ public RcsMessageQueryResult createFromParcel(Parcel in) {
+ return new RcsMessageQueryResult(in);
+ }
+
+ @Override
+ public RcsMessageQueryResult[] newArray(int size) {
+ return new RcsMessageQueryResult[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeParcelable(mContinuationToken, flags);
+ dest.writeTypedList(mMessageTypeIdPairs);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsManager.aidl b/telephony/java/android/telephony/ims/RcsMessageSnippet.aidl
index 63bc71c5ee46..99b8eb704e00 100644
--- a/telephony/java/android/telephony/ims/RcsManager.aidl
+++ b/telephony/java/android/telephony/ims/RcsMessageSnippet.aidl
@@ -17,4 +17,4 @@
package android.telephony.ims;
-parcelable RcsManager;
+parcelable RcsMessageSnippet;
diff --git a/telephony/java/android/telephony/ims/RcsMessageSnippet.java b/telephony/java/android/telephony/ims/RcsMessageSnippet.java
new file mode 100644
index 000000000000..b0b930c56e91
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsMessageSnippet.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.telephony.ims.RcsMessage.RcsMessageStatus;
+
+/**
+ * An immutable summary of the latest {@link RcsMessage} on an {@link RcsThread}
+ */
+public final class RcsMessageSnippet implements Parcelable {
+ private final String mText;
+ private final @RcsMessageStatus int mStatus;
+ private final long mTimestamp;
+
+ /**
+ * @hide
+ */
+ public RcsMessageSnippet(String text, @RcsMessageStatus int status, long timestamp) {
+ mText = text;
+ mStatus = status;
+ mTimestamp = timestamp;
+ }
+
+ /**
+ * @return Returns the text of the {@link RcsMessage} with highest origination timestamp value
+ * (i.e. latest) in this thread
+ */
+ @Nullable
+ public String getSnippetText() {
+ return mText;
+ }
+
+ /**
+ * @return Returns the status of the {@link RcsMessage} with highest origination timestamp value
+ * (i.e. latest) in this thread
+ */
+ public @RcsMessageStatus int getSnippetStatus() {
+ return mStatus;
+ }
+
+ /**
+ * @return Returns the timestamp of the {@link RcsMessage} with highest origination timestamp
+ * value (i.e. latest) in this thread
+ */
+ public long getSnippetTimestamp() {
+ return mTimestamp;
+ }
+
+ private RcsMessageSnippet(Parcel in) {
+ mText = in.readString();
+ mStatus = in.readInt();
+ mTimestamp = in.readLong();
+ }
+
+ public static final Creator<RcsMessageSnippet> CREATOR =
+ new Creator<RcsMessageSnippet>() {
+ @Override
+ public RcsMessageSnippet createFromParcel(Parcel in) {
+ return new RcsMessageSnippet(in);
+ }
+
+ @Override
+ public RcsMessageSnippet[] newArray(int size) {
+ return new RcsMessageSnippet[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mText);
+ dest.writeInt(mStatus);
+ dest.writeLong(mTimestamp);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsMessageStore.java b/telephony/java/android/telephony/ims/RcsMessageStore.java
index 1bf6ffd81ca0..d811c6e93a56 100644
--- a/telephony/java/android/telephony/ims/RcsMessageStore.java
+++ b/telephony/java/android/telephony/ims/RcsMessageStore.java
@@ -16,106 +16,221 @@
package android.telephony.ims;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.WorkerThread;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.telephony.Rlog;
-import android.telephony.ims.aidl.IRcs;
+import android.net.Uri;
+
+import java.util.List;
/**
* RcsMessageStore is the application interface to RcsProvider and provides access methods to
* RCS related database tables.
- * @hide - TODO make this public
*/
public class RcsMessageStore {
- static final String TAG = "RcsMessageStore";
-
/**
* Returns the first chunk of existing {@link RcsThread}s in the common storage.
+ *
* @param queryParameters Parameters to specify to return a subset of all RcsThreads.
* Passing a value of null will return all threads.
+ * @throws RcsMessageStoreException if the query could not be completed on the storage
*/
@WorkerThread
- public RcsThreadQueryResult getRcsThreads(@Nullable RcsThreadQueryParameters queryParameters) {
- try {
- IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
- if (iRcs != null) {
- return iRcs.getRcsThreads(queryParameters);
- }
- } catch (RemoteException re) {
- Rlog.e(TAG, "RcsMessageStore: Exception happened during getRcsThreads", re);
- }
-
- return null;
+ @NonNull
+ public RcsThreadQueryResult getRcsThreads(@Nullable RcsThreadQueryParams queryParameters)
+ throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getRcsThreads(queryParameters));
}
/**
* Returns the next chunk of {@link RcsThread}s in the common storage.
+ *
* @param continuationToken A token to continue the query to get the next chunk. This is
- * obtained through {@link RcsThreadQueryResult#nextChunkToken}.
+ * obtained through {@link RcsThreadQueryResult#getContinuationToken}.
+ * @throws RcsMessageStoreException if the query could not be completed on the storage
*/
@WorkerThread
- public RcsThreadQueryResult getRcsThreads(RcsThreadQueryContinuationToken continuationToken) {
- try {
- IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
- if (iRcs != null) {
- return iRcs.getRcsThreadsWithToken(continuationToken);
- }
- } catch (RemoteException re) {
- Rlog.e(TAG, "RcsMessageStore: Exception happened during getRcsThreads", re);
- }
+ @NonNull
+ public RcsThreadQueryResult getRcsThreads(@NonNull RcsQueryContinuationToken continuationToken)
+ throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getRcsThreadsWithToken(continuationToken));
+ }
+
+ /**
+ * Returns the first chunk of existing {@link RcsParticipant}s in the common storage.
+ *
+ * @param queryParameters Parameters to specify to return a subset of all RcsParticipants.
+ * Passing a value of null will return all participants.
+ * @throws RcsMessageStoreException if the query could not be completed on the storage
+ */
+ @WorkerThread
+ @NonNull
+ public RcsParticipantQueryResult getRcsParticipants(
+ @Nullable RcsParticipantQueryParams queryParameters)
+ throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getParticipants(queryParameters));
+ }
+
+ /**
+ * Returns the next chunk of {@link RcsParticipant}s in the common storage.
+ *
+ * @param continuationToken A token to continue the query to get the next chunk. This is
+ * obtained through
+ * {@link RcsParticipantQueryResult#getContinuationToken}
+ * @throws RcsMessageStoreException if the query could not be completed on the storage
+ */
+ @WorkerThread
+ @NonNull
+ public RcsParticipantQueryResult getRcsParticipants(
+ @NonNull RcsQueryContinuationToken continuationToken)
+ throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getParticipantsWithToken(continuationToken));
+ }
+
+ /**
+ * Returns the first chunk of existing {@link RcsMessage}s in the common storage.
+ *
+ * @param queryParameters Parameters to specify to return a subset of all RcsMessages.
+ * Passing a value of null will return all messages.
+ * @throws RcsMessageStoreException if the query could not be completed on the storage
+ */
+ @WorkerThread
+ @NonNull
+ public RcsMessageQueryResult getRcsMessages(
+ @Nullable RcsMessageQueryParams queryParameters) throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getMessages(queryParameters));
+ }
+
+ /**
+ * Returns the next chunk of {@link RcsMessage}s in the common storage.
+ *
+ * @param continuationToken A token to continue the query to get the next chunk. This is
+ * obtained through {@link RcsMessageQueryResult#getContinuationToken}
+ * @throws RcsMessageStoreException if the query could not be completed on the storage
+ */
+ @WorkerThread
+ @NonNull
+ public RcsMessageQueryResult getRcsMessages(
+ @NonNull RcsQueryContinuationToken continuationToken) throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getMessagesWithToken(continuationToken));
+ }
+
+ /**
+ * Returns the first chunk of existing {@link RcsEvent}s in the common storage.
+ *
+ * @param queryParameters Parameters to specify to return a subset of all RcsEvents.
+ * Passing a value of null will return all events.
+ * @throws RcsMessageStoreException if the query could not be completed on the storage
+ */
+ @WorkerThread
+ @NonNull
+ public RcsEventQueryResult getRcsEvents(
+ @Nullable RcsEventQueryParams queryParameters) throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getEvents(queryParameters));
+ }
- return null;
+ /**
+ * Returns the next chunk of {@link RcsEvent}s in the common storage.
+ *
+ * @param continuationToken A token to continue the query to get the next chunk. This is
+ * obtained through {@link RcsEventQueryResult#getContinuationToken}.
+ * @throws RcsMessageStoreException if the query could not be completed on the storage
+ */
+ @WorkerThread
+ @NonNull
+ public RcsEventQueryResult getRcsEvents(
+ @NonNull RcsQueryContinuationToken continuationToken) throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getEventsWithToken(continuationToken));
+ }
+
+ /**
+ * Persists an {@link RcsEvent} to common storage.
+ *
+ * @param persistableEvent The {@link RcsEvent} to persist into storage.
+ * @throws RcsMessageStoreException if the query could not be completed on the storage
+ *
+ * @see RcsGroupThreadNameChangedEvent
+ * @see RcsGroupThreadIconChangedEvent
+ * @see RcsGroupThreadParticipantJoinedEvent
+ * @see RcsGroupThreadParticipantLeftEvent
+ * @see RcsParticipantAliasChangedEvent
+ */
+ @WorkerThread
+ @NonNull
+ public void persistRcsEvent(RcsEvent persistableEvent) throws RcsMessageStoreException {
+ persistableEvent.persist();
}
/**
* Creates a new 1 to 1 thread with the given participant and persists it in the storage.
+ *
+ * @param recipient The {@link RcsParticipant} that will receive the messages in this thread.
+ * @return The newly created {@link Rcs1To1Thread}
+ * @throws RcsMessageStoreException if the thread could not be persisted in the storage
*/
@WorkerThread
- public Rcs1To1Thread createRcs1To1Thread(RcsParticipant recipient) {
- try {
- IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
- if (iRcs != null) {
- return iRcs.createRcs1To1Thread(recipient);
+ @NonNull
+ public Rcs1To1Thread createRcs1To1Thread(@NonNull RcsParticipant recipient)
+ throws RcsMessageStoreException {
+ return new Rcs1To1Thread(
+ RcsControllerCall.call(iRcs -> iRcs.createRcs1To1Thread(recipient.getId())));
+ }
+
+ /**
+ * Creates a new group thread with the given participants and persists it in the storage.
+ *
+ * @throws RcsMessageStoreException if the thread could not be persisted in the storage
+ */
+ @WorkerThread
+ @NonNull
+ public RcsGroupThread createGroupThread(@Nullable List<RcsParticipant> recipients,
+ @Nullable String groupName, @Nullable Uri groupIcon) throws RcsMessageStoreException {
+ int[] recipientIds = null;
+ if (recipients != null) {
+ recipientIds = new int[recipients.size()];
+
+ for (int i = 0; i < recipients.size(); i++) {
+ recipientIds[i] = recipients.get(i).getId();
}
- } catch (RemoteException re) {
- Rlog.e(TAG, "RcsMessageStore: Exception happened during createRcs1To1Thread", re);
}
- return null;
+ int[] finalRecipientIds = recipientIds;
+ return new RcsGroupThread(RcsControllerCall.call(
+ iRcs -> iRcs.createGroupThread(finalRecipientIds, groupName, groupIcon)));
}
/**
- * Delete the {@link RcsThread} identified by the given threadId.
- * @param threadId threadId of the thread to be deleted.
+ * Delete the given {@link RcsThread} from the storage.
+ *
+ * @param thread The thread to be deleted.
+ * @throws RcsMessageStoreException if the thread could not be deleted from the storage
*/
@WorkerThread
- public void deleteThread(int threadId) {
- try {
- IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
- if (iRcs != null) {
- iRcs.deleteThread(threadId);
- }
- } catch (RemoteException re) {
- Rlog.e(TAG, "RcsMessageStore: Exception happened during deleteThread", re);
+ public void deleteThread(@NonNull RcsThread thread) throws RcsMessageStoreException {
+ if (thread == null) {
+ return;
+ }
+
+ boolean isDeleteSucceeded = RcsControllerCall.call(
+ iRcs -> iRcs.deleteThread(thread.getThreadId(), thread.getThreadType()));
+
+ if (!isDeleteSucceeded) {
+ throw new RcsMessageStoreException("Could not delete RcsThread");
}
}
/**
* Creates a new participant and persists it in the storage.
+ *
* @param canonicalAddress The defining address (e.g. phone number) of the participant.
+ * @param alias The RCS alias for the participant.
+ * @throws RcsMessageStoreException if the participant could not be created on the storage
*/
- public RcsParticipant createRcsParticipant(String canonicalAddress) {
- try {
- IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
- if (iRcs != null) {
- return iRcs.createRcsParticipant(canonicalAddress);
- }
- } catch (RemoteException re) {
- Rlog.e(TAG, "RcsMessageStore: Exception happened during createRcsParticipant", re);
- }
-
- return null;
+ @WorkerThread
+ @NonNull
+ public RcsParticipant createRcsParticipant(String canonicalAddress, @Nullable String alias)
+ throws RcsMessageStoreException {
+ return new RcsParticipant(
+ RcsControllerCall.call(iRcs -> iRcs.createRcsParticipant(canonicalAddress, alias)));
}
}
diff --git a/telephony/java/android/telephony/ims/RcsThreadEvent.java b/telephony/java/android/telephony/ims/RcsMessageStoreException.java
index e10baab9d8c5..f25bb173be37 100644
--- a/telephony/java/android/telephony/ims/RcsThreadEvent.java
+++ b/telephony/java/android/telephony/ims/RcsMessageStoreException.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2019 The Android Open Source Project
+ * Copyright (c) 2019 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.
@@ -13,13 +13,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package android.telephony.ims;
-import android.os.Parcelable;
+package android.telephony.ims;
/**
- * An event that happened on an {@link RcsThread}.
- * @hide - TODO(sahinc) make this public
+ * An exception that happened on {@link RcsMessageStore} or one of the derived storage classes in
+ * {@link android.telephony.ims}
*/
-public abstract class RcsThreadEvent implements Parcelable {
+public class RcsMessageStoreException extends Exception {
+
+ /**
+ * Constructs an {@link RcsMessageStoreException} with the specified detail message.
+ * @param message The detail message
+ * @see Throwable#getMessage()
+ */
+ public RcsMessageStoreException(String message) {
+ super(message);
+ }
}
diff --git a/telephony/java/android/telephony/ims/RcsMultiMediaPart.java b/telephony/java/android/telephony/ims/RcsMultiMediaPart.java
deleted file mode 100644
index d295fba365f0..000000000000
--- a/telephony/java/android/telephony/ims/RcsMultiMediaPart.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import android.os.Parcel;
-
-/**
- * A part of a composite {@link RcsMessage} that holds a media that is rendered on the screen
- * (i.e. image, video etc)
- * @hide - TODO(sahinc) make this public
- */
-public class RcsMultiMediaPart extends RcsFileTransferPart {
- public static final Creator<RcsMultiMediaPart> CREATOR = new Creator<RcsMultiMediaPart>() {
- @Override
- public RcsMultiMediaPart createFromParcel(Parcel in) {
- return new RcsMultiMediaPart(in);
- }
-
- @Override
- public RcsMultiMediaPart[] newArray(int size) {
- return new RcsMultiMediaPart[size];
- }
- };
-
- protected RcsMultiMediaPart(Parcel in) {
- super(in);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsMultimediaPart.aidl b/telephony/java/android/telephony/ims/RcsMultimediaPart.aidl
deleted file mode 100644
index 5992d95c3b9c..000000000000
--- a/telephony/java/android/telephony/ims/RcsMultimediaPart.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-parcelable RcsMultimediaPart;
diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessage.aidl b/telephony/java/android/telephony/ims/RcsOutgoingMessage.aidl
deleted file mode 100644
index 6e0c80f3af81..000000000000
--- a/telephony/java/android/telephony/ims/RcsOutgoingMessage.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-parcelable RcsOutgoingMessage;
diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessage.java b/telephony/java/android/telephony/ims/RcsOutgoingMessage.java
index bfb161133618..06fb83268afb 100644
--- a/telephony/java/android/telephony/ims/RcsOutgoingMessage.java
+++ b/telephony/java/android/telephony/ims/RcsOutgoingMessage.java
@@ -15,34 +15,51 @@
*/
package android.telephony.ims;
-import android.os.Parcel;
+import android.annotation.NonNull;
+import android.annotation.WorkerThread;
+
+import java.util.ArrayList;
+import java.util.List;
/**
* This is a single instance of a message sent over RCS.
- * @hide - TODO(sahinc) make this public
*/
public class RcsOutgoingMessage extends RcsMessage {
- public static final Creator<RcsOutgoingMessage> CREATOR = new Creator<RcsOutgoingMessage>() {
- @Override
- public RcsOutgoingMessage createFromParcel(Parcel in) {
- return new RcsOutgoingMessage(in);
- }
+ RcsOutgoingMessage(int id) {
+ super(id);
+ }
- @Override
- public RcsOutgoingMessage[] newArray(int size) {
- return new RcsOutgoingMessage[size];
- }
- };
+ /**
+ * @return Returns the {@link RcsOutgoingMessageDelivery}s associated with this message. Please
+ * note that the deliveries returned for the {@link RcsOutgoingMessage} may not always match the
+ * {@link RcsParticipant}s on the {@link RcsGroupThread} as the group recipients may have
+ * changed.
+ * @throws RcsMessageStoreException if the outgoing deliveries could not be read from storage.
+ */
+ @NonNull
+ @WorkerThread
+ public List<RcsOutgoingMessageDelivery> getOutgoingDeliveries()
+ throws RcsMessageStoreException {
+ int[] deliveryParticipants;
+ List<RcsOutgoingMessageDelivery> messageDeliveries = new ArrayList<>();
- protected RcsOutgoingMessage(Parcel in) {
- }
+ deliveryParticipants = RcsControllerCall.call(
+ iRcs -> iRcs.getMessageRecipients(mId));
- @Override
- public int describeContents() {
- return 0;
+ if (deliveryParticipants != null) {
+ for (Integer deliveryParticipant : deliveryParticipants) {
+ messageDeliveries.add(new RcsOutgoingMessageDelivery(deliveryParticipant, mId));
+ }
+ }
+
+ return messageDeliveries;
}
+ /**
+ * @return Returns {@code false} as this is not an incoming message.
+ */
@Override
- public void writeToParcel(Parcel dest, int flags) {
+ public boolean isIncoming() {
+ return false;
}
}
diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.aidl b/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.aidl
new file mode 100644
index 000000000000..0c38d9f5766b
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.aidl
@@ -0,0 +1,20 @@
+/*
+ *
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+parcelable RcsOutgoingMessageCreationParams;
diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.java b/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.java
new file mode 100644
index 000000000000..979634a069df
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * {@link RcsOutgoingMessageCreationParams} is a collection of parameters that should be passed
+ * into {@link RcsThread#addOutgoingMessage(RcsOutgoingMessageCreationParams)} to generate an
+ * {@link RcsOutgoingMessage} on that {@link RcsThread}
+ */
+public final class RcsOutgoingMessageCreationParams extends RcsMessageCreationParams
+ implements Parcelable {
+ /**
+ * A builder to instantiate and persist an {@link RcsOutgoingMessage}
+ */
+ public static class Builder extends RcsMessageCreationParams.Builder {
+
+ /**
+ * Creates a new {@link Builder} to create an instance of
+ * {@link RcsOutgoingMessageCreationParams}.
+ *
+ * @param originationTimestamp The timestamp of {@link RcsMessage} creation. The origination
+ * timestamp value in milliseconds passed after midnight,
+ * January 1, 1970 UTC
+ * @param subscriptionId The subscription ID that was used to send or receive this
+ * {@link RcsMessage}
+ * @see android.telephony.SubscriptionInfo#getSubscriptionId()
+ */
+ public Builder(long originationTimestamp, int subscriptionId) {
+ super(originationTimestamp, subscriptionId);
+ }
+
+ /**
+ * Creates configuration parameters for a new message.
+ */
+ public RcsOutgoingMessageCreationParams build() {
+ return new RcsOutgoingMessageCreationParams(this);
+ }
+ }
+
+ private RcsOutgoingMessageCreationParams(Builder builder) {
+ super(builder);
+ }
+
+ private RcsOutgoingMessageCreationParams(Parcel in) {
+ super(in);
+ }
+
+ public static final Creator<RcsOutgoingMessageCreationParams> CREATOR =
+ new Creator<RcsOutgoingMessageCreationParams>() {
+ @Override
+ public RcsOutgoingMessageCreationParams createFromParcel(Parcel in) {
+ return new RcsOutgoingMessageCreationParams(in);
+ }
+
+ @Override
+ public RcsOutgoingMessageCreationParams[] newArray(int size) {
+ return new RcsOutgoingMessageCreationParams[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessageDelivery.java b/telephony/java/android/telephony/ims/RcsOutgoingMessageDelivery.java
new file mode 100644
index 000000000000..1c87b13f0dfb
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsOutgoingMessageDelivery.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.telephony.ims;
+
+import android.annotation.NonNull;
+import android.annotation.WorkerThread;
+
+/**
+ * This class holds the delivery information of an {@link RcsOutgoingMessage} for each
+ * {@link RcsParticipant} that the message was intended for.
+ */
+public class RcsOutgoingMessageDelivery {
+ // The participant that this delivery is intended for
+ private final int mRecipientId;
+ // The message this delivery is associated with
+ private final int mRcsOutgoingMessageId;
+
+ /**
+ * Constructor to be used with RcsOutgoingMessage.getDelivery()
+ *
+ * @hide
+ */
+ RcsOutgoingMessageDelivery(int recipientId, int messageId) {
+ mRecipientId = recipientId;
+ mRcsOutgoingMessageId = messageId;
+ }
+
+ /**
+ * Sets the delivery time of this outgoing delivery and persists into storage.
+ *
+ * @param deliveredTimestamp The timestamp to set to delivery. It is defined as milliseconds
+ * passed after midnight, January 1, 1970 UTC
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setDeliveredTimestamp(long deliveredTimestamp) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setOutgoingDeliveryDeliveredTimestamp(
+ mRcsOutgoingMessageId, mRecipientId, deliveredTimestamp));
+ }
+
+ /**
+ * @return Returns the delivered timestamp of the associated message to the associated
+ * participant. Timestamp is defined as milliseconds passed after midnight, January 1, 1970 UTC.
+ * Returns 0 if the {@link RcsOutgoingMessage} is not delivered yet.
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public long getDeliveredTimestamp() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getOutgoingDeliveryDeliveredTimestamp(
+ mRcsOutgoingMessageId, mRecipientId));
+ }
+
+ /**
+ * Sets the seen time of this outgoing delivery and persists into storage.
+ *
+ * @param seenTimestamp The timestamp to set to delivery. It is defined as milliseconds
+ * passed after midnight, January 1, 1970 UTC
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setSeenTimestamp(long seenTimestamp) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setOutgoingDeliverySeenTimestamp(
+ mRcsOutgoingMessageId, mRecipientId, seenTimestamp));
+ }
+
+ /**
+ * @return Returns the seen timestamp of the associated message by the associated
+ * participant. Timestamp is defined as milliseconds passed after midnight, January 1, 1970 UTC.
+ * Returns 0 if the {@link RcsOutgoingMessage} is not seen yet.
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public long getSeenTimestamp() throws RcsMessageStoreException {
+ return RcsControllerCall.call(
+ iRcs -> iRcs.getOutgoingDeliverySeenTimestamp(mRcsOutgoingMessageId, mRecipientId));
+ }
+
+ /**
+ * Sets the status of this outgoing delivery and persists into storage.
+ *
+ * @param status The status of the associated {@link RcsMessage}s delivery to the associated
+ * {@link RcsParticipant}
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
+ */
+ @WorkerThread
+ public void setStatus(@RcsMessage.RcsMessageStatus int status) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setOutgoingDeliveryStatus(
+ mRcsOutgoingMessageId, mRecipientId, status));
+ }
+
+ /**
+ * @return Returns the status of this outgoing delivery.
+ * @throws RcsMessageStoreException if the value could not be read from the storage
+ */
+ @WorkerThread
+ public @RcsMessage.RcsMessageStatus int getStatus() throws RcsMessageStoreException {
+ return RcsControllerCall.call(
+ iRcs -> iRcs.getOutgoingDeliveryStatus(mRcsOutgoingMessageId, mRecipientId));
+ }
+
+ /**
+ * @return Returns the recipient associated with this delivery.
+ */
+ @NonNull
+ public RcsParticipant getRecipient() {
+ return new RcsParticipant(mRecipientId);
+ }
+
+ /**
+ * @return Returns the {@link RcsOutgoingMessage} associated with this delivery.
+ */
+ @NonNull
+ public RcsOutgoingMessage getMessage() {
+ return new RcsOutgoingMessage(mRcsOutgoingMessageId);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsParticipant.aidl b/telephony/java/android/telephony/ims/RcsParticipant.aidl
deleted file mode 100644
index 1c4436367e54..000000000000
--- a/telephony/java/android/telephony/ims/RcsParticipant.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-parcelable RcsParticipant;
diff --git a/telephony/java/android/telephony/ims/RcsParticipant.java b/telephony/java/android/telephony/ims/RcsParticipant.java
index f678ec7e435b..7ba5d8e65f76 100644
--- a/telephony/java/android/telephony/ims/RcsParticipant.java
+++ b/telephony/java/android/telephony/ims/RcsParticipant.java
@@ -15,33 +15,15 @@
*/
package android.telephony.ims;
-import static android.telephony.ims.RcsMessageStore.TAG;
-
-import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.WorkerThread;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.telephony.Rlog;
-import android.telephony.ims.aidl.IRcs;
-import android.text.TextUtils;
-
-import com.android.internal.util.Preconditions;
/**
* RcsParticipant is an RCS capable contact that can participate in {@link RcsThread}s.
- * @hide - TODO(sahinc) make this public
*/
-public class RcsParticipant implements Parcelable {
+public class RcsParticipant {
// The row ID of this participant in the database
private int mId;
- // The phone number of this participant
- private String mCanonicalAddress;
- // The RCS alias of this participant. This is different than the name of the contact in the
- // Contacts app - i.e. RCS protocol allows users to define aliases for themselves that doesn't
- // require other users to add them as contacts and give them a name.
- private String mAlias;
/**
* Constructor for {@link com.android.internal.telephony.ims.RcsMessageStoreController}
@@ -49,106 +31,95 @@ public class RcsParticipant implements Parcelable {
*
* @hide
*/
- public RcsParticipant(int id, @NonNull String canonicalAddress) {
+ public RcsParticipant(int id) {
mId = id;
- mCanonicalAddress = canonicalAddress;
}
/**
- * @return Returns the canonical address (i.e. normalized phone number) for this participant
+ * @return Returns the canonical address (i.e. normalized phone number) for this
+ * {@link RcsParticipant}
+ * @throws RcsMessageStoreException if the value could not be read from the storage
*/
- public String getCanonicalAddress() {
- return mCanonicalAddress;
+ @Nullable
+ @WorkerThread
+ public String getCanonicalAddress() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getRcsParticipantCanonicalAddress(mId));
}
/**
- * Sets the canonical address for this participant and updates it in storage.
- * @param canonicalAddress the canonical address to update to.
+ * @return Returns the alias for this {@link RcsParticipant}. Alias is usually the real name of
+ * the person themselves. Please see US5-15 - GSMA RCC.71 (RCS Universal Profile Service
+ * Definition Document)
+ * @throws RcsMessageStoreException if the value could not be read from the storage
*/
+ @Nullable
@WorkerThread
- public void setCanonicalAddress(@NonNull String canonicalAddress) {
- Preconditions.checkNotNull(canonicalAddress);
- if (canonicalAddress.equals(mCanonicalAddress)) {
- return;
- }
-
- mCanonicalAddress = canonicalAddress;
-
- try {
- IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
- if (iRcs != null) {
- iRcs.updateRcsParticipantCanonicalAddress(mId, mCanonicalAddress);
- }
- } catch (RemoteException re) {
- Rlog.e(TAG, "RcsParticipant: Exception happened during setCanonicalAddress", re);
- }
+ public String getAlias() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getRcsParticipantAlias(mId));
}
/**
- * @return Returns the alias for this participant. Alias is usually the real name of the person
- * themselves.
+ * Sets the alias for this {@link RcsParticipant} and persists it in storage. Alias is usually
+ * the real name of the person themselves. Please see US5-15 - GSMA RCC.71 (RCS Universal
+ * Profile Service Definition Document)
+ *
+ * @param alias The alias to set to.
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
*/
- public String getAlias() {
- return mAlias;
+ @WorkerThread
+ public void setAlias(String alias) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setRcsParticipantAlias(mId, alias));
}
/**
- * Sets the alias for this participant and persists it in storage. Alias is usually the real
- * name of the person themselves.
+ * @return Returns the contact ID for this {@link RcsParticipant}. Contact ID is a unique ID for
+ * an {@link RcsParticipant} that is RCS provisioned. Please see 4.4.5 - GSMA RCC.53 (RCS Device
+ * API 1.6 Specification)
+ * @throws RcsMessageStoreException if the value could not be read from the storage
*/
+ @Nullable
@WorkerThread
- public void setAlias(String alias) {
- if (TextUtils.equals(mAlias, alias)) {
- return;
- }
- mAlias = alias;
-
- try {
- IRcs iRcs = IRcs.Stub.asInterface(ServiceManager.getService("ircs"));
- if (iRcs != null) {
- iRcs.updateRcsParticipantAlias(mId, mAlias);
- }
- } catch (RemoteException re) {
- Rlog.e(TAG, "RcsParticipant: Exception happened during setCanonicalAddress", re);
- }
+ public String getContactId() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getRcsParticipantContactId(mId));
}
/**
- * Returns the row id of this participant. This is not meant to be part of the SDK
+ * Sets the contact ID for this {@link RcsParticipant}. Contact ID is a unique ID for
+ * an {@link RcsParticipant} that is RCS provisioned. Please see 4.4.5 - GSMA RCC.53 (RCS Device
+ * API 1.6 Specification)
*
- * @hide
+ * @param contactId The contact ID to set to.
+ * @throws RcsMessageStoreException if the value could not be persisted into storage
*/
- public int getId() {
- return mId;
+ @WorkerThread
+ public void setContactId(String contactId) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(iRcs -> iRcs.setRcsParticipantContactId(mId, contactId));
}
- public static final Creator<RcsParticipant> CREATOR = new Creator<RcsParticipant>() {
- @Override
- public RcsParticipant createFromParcel(Parcel in) {
- return new RcsParticipant(in);
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
}
-
- @Override
- public RcsParticipant[] newArray(int size) {
- return new RcsParticipant[size];
+ if (!(obj instanceof RcsParticipant)) {
+ return false;
}
- };
+ RcsParticipant other = (RcsParticipant) obj;
- protected RcsParticipant(Parcel in) {
- mId = in.readInt();
- mCanonicalAddress = in.readString();
- mAlias = in.readString();
+ return mId == other.mId;
}
@Override
- public int describeContents() {
- return 0;
+ public int hashCode() {
+ return mId;
}
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mId);
- dest.writeString(mCanonicalAddress);
- dest.writeString(mAlias);
+ /**
+ * Returns the row id of this participant. This is not meant to be part of the SDK
+ *
+ * @hide
+ */
+ public int getId() {
+ return mId;
}
}
diff --git a/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java b/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java
index b9ca5a86f84d..c9a2b0d07bc8 100644
--- a/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java
+++ b/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java
@@ -15,27 +15,92 @@
*/
package android.telephony.ims;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Parcel;
+import android.os.Parcelable;
/**
- * An event that indicates an {@link RcsParticipant}'s alias was changed.
- * @hide - TODO(sahinc) make this public
+ * An event that indicates an {@link RcsParticipant}'s alias was changed. Please see US18-2 - GSMA
+ * RCC.71 (RCS Universal Profile Service Definition Document)
*/
-public class RcsParticipantAliasChangedEvent extends RcsParticipantEvent {
+public final class RcsParticipantAliasChangedEvent extends RcsEvent implements Parcelable {
+ // The ID of the participant that changed their alias
+ private final int mParticipantId;
+ // The new alias of the above participant
+ private final String mNewAlias;
+
+ /**
+ * Creates a new {@link RcsParticipantAliasChangedEvent}. This event is not persisted into
+ * storage until {@link RcsMessageStore#persistRcsEvent(RcsEvent)} is called.
+ *
+ * @param timestamp The timestamp of when this event happened, in milliseconds passed after
+ * midnight, January 1st, 1970 UTC
+ * @param participant The {@link RcsParticipant} that got their alias changed
+ * @param newAlias The new alias the {@link RcsParticipant} has.
+ * @see RcsMessageStore#persistRcsEvent(RcsEvent)
+ */
+ public RcsParticipantAliasChangedEvent(long timestamp, @NonNull RcsParticipant participant,
+ @Nullable String newAlias) {
+ super(timestamp);
+ mParticipantId = participant.getId();
+ mNewAlias = newAlias;
+ }
+
+ /**
+ * @hide - internal constructor for queries
+ */
+ public RcsParticipantAliasChangedEvent(long timestamp, int participantId,
+ @Nullable String newAlias) {
+ super(timestamp);
+ mParticipantId = participantId;
+ mNewAlias = newAlias;
+ }
+
+ /**
+ * @return Returns the {@link RcsParticipant} whose alias was changed.
+ */
+ @NonNull
+ public RcsParticipant getParticipantId() {
+ return new RcsParticipant(mParticipantId);
+ }
+
+ /**
+ * @return Returns the alias of the associated {@link RcsParticipant} after this event happened
+ */
+ @Nullable
+ public String getNewAlias() {
+ return mNewAlias;
+ }
+
+ /**
+ * Persists the event to the data store.
+ *
+ * @hide - not meant for public use.
+ */
+ @Override
+ public void persist() throws RcsMessageStoreException {
+ RcsControllerCall.call(iRcs -> iRcs.createParticipantAliasChangedEvent(
+ getTimestamp(), getParticipantId().getId(), getNewAlias()));
+ }
+
public static final Creator<RcsParticipantAliasChangedEvent> CREATOR =
new Creator<RcsParticipantAliasChangedEvent>() {
- @Override
- public RcsParticipantAliasChangedEvent createFromParcel(Parcel in) {
- return new RcsParticipantAliasChangedEvent(in);
- }
+ @Override
+ public RcsParticipantAliasChangedEvent createFromParcel(Parcel in) {
+ return new RcsParticipantAliasChangedEvent(in);
+ }
- @Override
- public RcsParticipantAliasChangedEvent[] newArray(int size) {
- return new RcsParticipantAliasChangedEvent[size];
- }
- };
+ @Override
+ public RcsParticipantAliasChangedEvent[] newArray(int size) {
+ return new RcsParticipantAliasChangedEvent[size];
+ }
+ };
- protected RcsParticipantAliasChangedEvent(Parcel in) {
+ private RcsParticipantAliasChangedEvent(Parcel in) {
+ super(in);
+ mNewAlias = in.readString();
+ mParticipantId = in.readInt();
}
@Override
@@ -45,5 +110,8 @@ public class RcsParticipantAliasChangedEvent extends RcsParticipantEvent {
@Override
public void writeToParcel(Parcel dest, int flags) {
+ super.writeToParcel(dest, flags);
+ dest.writeString(mNewAlias);
+ dest.writeInt(mParticipantId);
}
}
diff --git a/telephony/java/android/telephony/ims/RcsParticipantEvent.aidl b/telephony/java/android/telephony/ims/RcsParticipantEvent.aidl
deleted file mode 100644
index c0a77897abd5..000000000000
--- a/telephony/java/android/telephony/ims/RcsParticipantEvent.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-parcelable RcsParticipantEvent;
diff --git a/telephony/java/android/telephony/ims/RcsParticipantQueryParams.aidl b/telephony/java/android/telephony/ims/RcsParticipantQueryParams.aidl
new file mode 100644
index 000000000000..b7c0f93c8c5f
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsParticipantQueryParams.aidl
@@ -0,0 +1,20 @@
+/*
+ *
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+parcelable RcsParticipantQueryParams;
diff --git a/telephony/java/android/telephony/ims/RcsParticipantQueryParams.java b/telephony/java/android/telephony/ims/RcsParticipantQueryParams.java
new file mode 100644
index 000000000000..d24d079d7038
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsParticipantQueryParams.java
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.annotation.CheckResult;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.security.InvalidParameterException;
+
+/**
+ * The parameters to pass into
+ * {@link RcsMessageStore#getRcsParticipants(RcsParticipantQueryParams)} in order to select a
+ * subset of {@link RcsThread}s present in the message store.
+ */
+public final class RcsParticipantQueryParams implements Parcelable {
+ /**
+ * Flag to set with {@link Builder#setSortProperty(int)} to sort the results in the order of
+ * creation time for faster query results
+ */
+ public static final int SORT_BY_CREATION_ORDER = 0;
+
+ /**
+ * Flag to set with {@link Builder#setSortProperty(int)} to sort depending on the
+ * {@link RcsParticipant} aliases
+ */
+ public static final int SORT_BY_ALIAS = 1;
+
+ /**
+ * Flag to set with {@link Builder#setSortProperty(int)} to sort depending on the
+ * {@link RcsParticipant} canonical addresses
+ */
+ public static final int SORT_BY_CANONICAL_ADDRESS = 2;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({SORT_BY_CREATION_ORDER, SORT_BY_ALIAS, SORT_BY_CANONICAL_ADDRESS})
+ public @interface SortingProperty {
+ }
+
+ // The SQL "like" statement to filter against participant aliases
+ private String mAliasLike;
+ // The SQL "like" statement to filter against canonical addresses
+ private String mCanonicalAddressLike;
+ // The property to sort the result against
+ private @SortingProperty int mSortingProperty;
+ // Whether to sort the result in ascending order
+ private boolean mIsAscending;
+ // The number of results to be returned from the query
+ private int mLimit;
+ // Used to limit the results to participants of a single thread
+ private int mThreadId;
+
+ /**
+ * @hide
+ */
+ public static final String PARTICIPANT_QUERY_PARAMETERS_KEY = "participant_query_parameters";
+
+ RcsParticipantQueryParams(int rcsThreadId, String aliasLike, String canonicalAddressLike,
+ @SortingProperty int sortingProperty, boolean isAscending,
+ int limit) {
+ mThreadId = rcsThreadId;
+ mAliasLike = aliasLike;
+ mCanonicalAddressLike = canonicalAddressLike;
+ mSortingProperty = sortingProperty;
+ mIsAscending = isAscending;
+ mLimit = limit;
+ }
+
+ /**
+ * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get
+ * the thread that the result query should be limited to.
+ *
+ * As we do not expose any sort of integer ID's to public usage, this should be hidden.
+ *
+ * @hide - not meant for public use
+ */
+ public int getThreadId() {
+ return mThreadId;
+ }
+
+ /**
+ * @return Returns the SQL-inspired "LIKE" clause that will be used to match
+ * {@link RcsParticipant}s with respect to their aliases
+ *
+ * @see RcsParticipant#getAlias()
+ */
+ public String getAliasLike() {
+ return mAliasLike;
+ }
+
+ /**
+ * @return Returns the SQL-inspired "LIKE" clause that will be used to match
+ * {@link RcsParticipant}s with respect to their canonical addresses.
+ *
+ * @see RcsParticipant#getCanonicalAddress()
+ */
+ public String getCanonicalAddressLike() {
+ return mCanonicalAddressLike;
+ }
+
+ /**
+ * @return Returns the number of {@link RcsParticipant}s to be returned from the query. A value
+ * of 0 means there is no set limit.
+ */
+ public int getLimit() {
+ return mLimit;
+ }
+
+ /**
+ * @return Returns the property that will be used to sort the result against.
+ * @see SortingProperty
+ */
+ public int getSortingProperty() {
+ return mSortingProperty;
+ }
+
+ /**
+ * @return Returns {@code true} if the result set will be sorted in ascending order,
+ * {@code false} if it will be sorted in descending order.
+ */
+ public boolean getSortDirection() {
+ return mIsAscending;
+ }
+
+ /**
+ * A helper class to build the {@link RcsParticipantQueryParams}.
+ */
+ public static class Builder {
+ private String mAliasLike;
+ private String mCanonicalAddressLike;
+ private @SortingProperty int mSortingProperty;
+ private boolean mIsAscending;
+ private int mLimit = 100;
+ private int mThreadId;
+
+ /**
+ * Creates a new builder for {@link RcsParticipantQueryParams} to be used in
+ * {@link RcsMessageStore#getRcsParticipants(RcsParticipantQueryParams)}
+ */
+ public Builder() {
+ // empty implementation
+ }
+
+ /**
+ * Limits the resulting {@link RcsParticipant}s to only the given {@link RcsThread}
+ *
+ * @param rcsThread The thread that the participants should be searched in.
+ * @return The same {@link Builder} to chain methods.
+ */
+ @CheckResult
+ public Builder setThread(RcsThread rcsThread) {
+ mThreadId = rcsThread.getThreadId();
+ return this;
+ }
+
+ /**
+ * Sets an SQL-inspired "like" clause to match with participant aliases. Using a percent
+ * sign ('%') wildcard matches any sequence of zero or more characters. Using an underscore
+ * ('_') wildcard matches any single character. Not using any wildcards would only perform a
+ * string match.The input string is case-insensitive.
+ *
+ * The input "An%e" would match {@link RcsParticipant}s with names Anne, Annie, Antonie,
+ * while the input "An_e" would only match Anne.
+ *
+ * @param likeClause The like clause to use for matching {@link RcsParticipant} aliases.
+ * @return The same {@link Builder} to chain methods
+ */
+ @CheckResult
+ public Builder setAliasLike(String likeClause) {
+ mAliasLike = likeClause;
+ return this;
+ }
+
+ /**
+ * Sets an SQL-inspired "like" clause to match with participant addresses. Using a percent
+ * sign ('%') wildcard matches any sequence of zero or more characters. Using an underscore
+ * ('_') wildcard matches any single character. Not using any wildcards would only perform a
+ * string match. The input string is case-insensitive.
+ *
+ * The input "+999%111" would match {@link RcsParticipant}s with addresses like "+9995111"
+ * or "+99955555111", while the input "+999_111" would only match "+9995111".
+ *
+ * @param likeClause The like clause to use for matching {@link RcsParticipant} canonical
+ * addresses.
+ * @return The same {@link Builder} to chain methods
+ */
+ @CheckResult
+ public Builder setCanonicalAddressLike(String likeClause) {
+ mCanonicalAddressLike = likeClause;
+ return this;
+ }
+
+ /**
+ * Desired number of threads to be returned from the query. Passing in 0 will return all
+ * existing threads at once. The limit defaults to 100.
+ *
+ * @param limit The number to limit the query result to.
+ * @return The same instance of the builder to chain parameters.
+ * @throws InvalidParameterException If the given limit is negative.
+ */
+ @CheckResult
+ public Builder setResultLimit(@IntRange(from = 0) int limit)
+ throws InvalidParameterException {
+ if (limit < 0) {
+ throw new InvalidParameterException("The query limit must be non-negative");
+ }
+
+ mLimit = limit;
+ return this;
+ }
+
+ /**
+ * Sets the property where the results should be sorted against. Defaults to
+ * {@link RcsParticipantQueryParams.SortingProperty#SORT_BY_CREATION_ORDER}
+ *
+ * @param sortingProperty against which property the results should be sorted
+ * @return The same instance of the builder to chain parameters.
+ */
+ @CheckResult
+ public Builder setSortProperty(@SortingProperty int sortingProperty) {
+ mSortingProperty = sortingProperty;
+ return this;
+ }
+
+ /**
+ * Sets whether the results should be sorted ascending or descending
+ *
+ * @param isAscending whether the results should be sorted ascending
+ * @return The same instance of the builder to chain parameters.
+ */
+ @CheckResult
+ public Builder setSortDirection(boolean isAscending) {
+ mIsAscending = isAscending;
+ return this;
+ }
+
+ /**
+ * Builds the {@link RcsParticipantQueryParams} to use in
+ * {@link RcsMessageStore#getRcsParticipants(RcsParticipantQueryParams)}
+ *
+ * @return An instance of {@link RcsParticipantQueryParams} to use with the participant
+ * query.
+ */
+ public RcsParticipantQueryParams build() {
+ return new RcsParticipantQueryParams(mThreadId, mAliasLike, mCanonicalAddressLike,
+ mSortingProperty, mIsAscending, mLimit);
+ }
+ }
+
+ /**
+ * Parcelable boilerplate below.
+ */
+ private RcsParticipantQueryParams(Parcel in) {
+ mAliasLike = in.readString();
+ mCanonicalAddressLike = in.readString();
+ mSortingProperty = in.readInt();
+ mIsAscending = in.readByte() == 1;
+ mLimit = in.readInt();
+ mThreadId = in.readInt();
+ }
+
+ public static final Creator<RcsParticipantQueryParams> CREATOR =
+ new Creator<RcsParticipantQueryParams>() {
+ @Override
+ public RcsParticipantQueryParams createFromParcel(Parcel in) {
+ return new RcsParticipantQueryParams(in);
+ }
+
+ @Override
+ public RcsParticipantQueryParams[] newArray(int size) {
+ return new RcsParticipantQueryParams[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeString(mAliasLike);
+ dest.writeString(mCanonicalAddressLike);
+ dest.writeInt(mSortingProperty);
+ dest.writeByte((byte) (mIsAscending ? 1 : 0));
+ dest.writeInt(mLimit);
+ dest.writeInt(mThreadId);
+ }
+
+}
diff --git a/telephony/java/android/telephony/ims/RcsParticipantQueryResult.aidl b/telephony/java/android/telephony/ims/RcsParticipantQueryResult.aidl
new file mode 100644
index 000000000000..db5c00c8ce00
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsParticipantQueryResult.aidl
@@ -0,0 +1,20 @@
+/*
+ *
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+parcelable RcsParticipantQueryResult;
diff --git a/telephony/java/android/telephony/ims/RcsParticipantQueryResult.java b/telephony/java/android/telephony/ims/RcsParticipantQueryResult.java
new file mode 100644
index 000000000000..505f1a55d1f0
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsParticipantQueryResult.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The result of a {@link RcsMessageStore#getRcsParticipants(RcsParticipantQueryParams)}
+ * call. This class allows getting the token for querying the next batch of participants in order to
+ * prevent handling large amounts of data at once.
+ */
+public final class RcsParticipantQueryResult implements Parcelable {
+ // A token for the caller to continue their query for the next batch of results
+ private RcsQueryContinuationToken mContinuationToken;
+ // The list of participant IDs returned with this query
+ private List<Integer> mParticipants;
+
+ /**
+ * Internal constructor for {@link com.android.internal.telephony.ims.RcsMessageStoreController}
+ * to create query results
+ *
+ * @hide
+ */
+ public RcsParticipantQueryResult(
+ RcsQueryContinuationToken continuationToken,
+ List<Integer> participants) {
+ mContinuationToken = continuationToken;
+ mParticipants = participants;
+ }
+
+ /**
+ * Returns a token to call
+ * {@link RcsMessageStore#getRcsParticipants(RcsQueryContinuationToken)}
+ * to get the next batch of {@link RcsParticipant}s.
+ */
+ @Nullable
+ public RcsQueryContinuationToken getContinuationToken() {
+ return mContinuationToken;
+ }
+
+ /**
+ * Returns all the {@link RcsParticipant}s in the current query result. Call {@link
+ * RcsMessageStore#getRcsParticipants(RcsQueryContinuationToken)} to get the next
+ * batch of {@link RcsParticipant}s.
+ */
+ @NonNull
+ public List<RcsParticipant> getParticipants() {
+ List<RcsParticipant> participantList = new ArrayList<>();
+ for (Integer participantId : mParticipants) {
+ participantList.add(new RcsParticipant(participantId));
+ }
+
+ return participantList;
+ }
+
+ private RcsParticipantQueryResult(Parcel in) {
+ mContinuationToken = in.readParcelable(
+ RcsQueryContinuationToken.class.getClassLoader());
+ }
+
+ public static final Creator<RcsParticipantQueryResult> CREATOR =
+ new Creator<RcsParticipantQueryResult>() {
+ @Override
+ public RcsParticipantQueryResult createFromParcel(Parcel in) {
+ return new RcsParticipantQueryResult(in);
+ }
+
+ @Override
+ public RcsParticipantQueryResult[] newArray(int size) {
+ return new RcsParticipantQueryResult[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeParcelable(mContinuationToken, flags);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsQueryContinuationToken.aidl b/telephony/java/android/telephony/ims/RcsQueryContinuationToken.aidl
new file mode 100644
index 000000000000..319379a462de
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsQueryContinuationToken.aidl
@@ -0,0 +1,20 @@
+/*
+ *
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+parcelable RcsQueryContinuationToken;
diff --git a/telephony/java/android/telephony/ims/RcsQueryContinuationToken.java b/telephony/java/android/telephony/ims/RcsQueryContinuationToken.java
new file mode 100644
index 000000000000..08643de51d40
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsQueryContinuationToken.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.annotation.IntDef;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A token for enabling continuation queries. Instances are acquired through
+ * {@code getContinuationToken} on result objects after initial query is done.
+ *
+ * @see RcsEventQueryResult#getContinuationToken()
+ * @see RcsMessageQueryResult#getContinuationToken()
+ * @see RcsParticipantQueryResult#getContinuationToken()
+ * @see RcsThreadQueryResult#getContinuationToken()
+ */
+public final class RcsQueryContinuationToken implements Parcelable {
+ /**
+ * Denotes that this {@link RcsQueryContinuationToken} token is meant to allow continuing
+ * {@link RcsEvent} queries
+ */
+ public static final int EVENT_QUERY_CONTINUATION_TOKEN_TYPE = 0;
+
+ /**
+ * Denotes that this {@link RcsQueryContinuationToken} token is meant to allow continuing
+ * {@link RcsMessage} queries
+ */
+ public static final int MESSAGE_QUERY_CONTINUATION_TOKEN_TYPE = 1;
+
+ /**
+ * Denotes that this {@link RcsQueryContinuationToken} token is meant to allow continuing
+ * {@link RcsParticipant} queries
+ */
+ public static final int PARTICIPANT_QUERY_CONTINUATION_TOKEN_TYPE = 2;
+
+ /**
+ * Denotes that this {@link RcsQueryContinuationToken} token is meant to allow continuing
+ * {@link RcsThread} queries
+ */
+ public static final int THREAD_QUERY_CONTINUATION_TOKEN_TYPE = 3;
+
+ /**
+ * @hide - not meant for public use
+ */
+ public static final String QUERY_CONTINUATION_TOKEN = "query_continuation_token";
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({EVENT_QUERY_CONTINUATION_TOKEN_TYPE, MESSAGE_QUERY_CONTINUATION_TOKEN_TYPE,
+ PARTICIPANT_QUERY_CONTINUATION_TOKEN_TYPE, THREAD_QUERY_CONTINUATION_TOKEN_TYPE})
+ public @interface ContinuationTokenType {}
+
+ // The type of query this token should allow to continue
+ private @ContinuationTokenType int mQueryType;
+ // The raw query string for the initial query
+ private final String mRawQuery;
+ // The number of results that is returned with each query
+ private final int mLimit;
+ // The offset value that this query should start the query from
+ private int mOffset;
+
+ /**
+ * @hide
+ */
+ public RcsQueryContinuationToken(@ContinuationTokenType int queryType, String rawQuery,
+ int limit, int offset) {
+ mQueryType = queryType;
+ mRawQuery = rawQuery;
+ mLimit = limit;
+ mOffset = offset;
+ }
+
+ /**
+ * Returns the original raw query used on {@link com.android.providers.telephony.RcsProvider}
+ * @hide
+ */
+ public String getRawQuery() {
+ return mRawQuery;
+ }
+
+ /**
+ * Returns which index this continuation query should start from
+ * @hide
+ */
+ public int getOffset() {
+ return mOffset;
+ }
+
+ /**
+ * Increments the offset by the amount of result rows returned with the continuation query for
+ * the next query.
+ * @hide
+ */
+ public void incrementOffset() {
+ mOffset += mLimit;
+ }
+
+ /**
+ * Returns the type of query that this {@link RcsQueryContinuationToken} is intended to be used
+ * to continue.
+ */
+ public @ContinuationTokenType int getQueryType() {
+ return mQueryType;
+ }
+
+ private RcsQueryContinuationToken(Parcel in) {
+ mQueryType = in.readInt();
+ mRawQuery = in.readString();
+ mLimit = in.readInt();
+ mOffset = in.readInt();
+ }
+
+ public static final Creator<RcsQueryContinuationToken> CREATOR =
+ new Creator<RcsQueryContinuationToken>() {
+ @Override
+ public RcsQueryContinuationToken createFromParcel(Parcel in) {
+ return new RcsQueryContinuationToken(in);
+ }
+
+ @Override
+ public RcsQueryContinuationToken[] newArray(int size) {
+ return new RcsQueryContinuationToken[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mQueryType);
+ dest.writeString(mRawQuery);
+ dest.writeInt(mLimit);
+ dest.writeInt(mOffset);
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsTextPart.aidl b/telephony/java/android/telephony/ims/RcsTextPart.aidl
deleted file mode 100644
index 4f9fe1fe26fe..000000000000
--- a/telephony/java/android/telephony/ims/RcsTextPart.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-parcelable RcsTextPart;
diff --git a/telephony/java/android/telephony/ims/RcsTextPart.java b/telephony/java/android/telephony/ims/RcsTextPart.java
deleted file mode 100644
index 2a72df17f32a..000000000000
--- a/telephony/java/android/telephony/ims/RcsTextPart.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import android.os.Parcel;
-
-/**
- * A part of a composite {@link RcsMessage} that holds a string
- * @hide - TODO(sahinc) make this public
- */
-public class RcsTextPart extends RcsPart {
- public static final Creator<RcsTextPart> CREATOR = new Creator<RcsTextPart>() {
- @Override
- public RcsTextPart createFromParcel(Parcel in) {
- return new RcsTextPart(in);
- }
-
- @Override
- public RcsTextPart[] newArray(int size) {
- return new RcsTextPart[size];
- }
- };
-
- protected RcsTextPart(Parcel in) {
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsThread.aidl b/telephony/java/android/telephony/ims/RcsThread.aidl
deleted file mode 100644
index d9cf6dbc0ff0..000000000000
--- a/telephony/java/android/telephony/ims/RcsThread.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 2019, 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;
-
-parcelable RcsThread; \ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/RcsThread.java b/telephony/java/android/telephony/ims/RcsThread.java
index c0a0d946d204..e015dd3e9c0a 100644
--- a/telephony/java/android/telephony/ims/RcsThread.java
+++ b/telephony/java/android/telephony/ims/RcsThread.java
@@ -16,60 +16,118 @@
package android.telephony.ims;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.util.Log;
+import static android.provider.Telephony.RcsColumns.RcsUnifiedThreadColumns.THREAD_TYPE_1_TO_1;
+import static android.provider.Telephony.RcsColumns.RcsUnifiedThreadColumns.THREAD_TYPE_GROUP;
+
+import android.annotation.NonNull;
+import android.annotation.WorkerThread;
+
+import com.android.internal.annotations.VisibleForTesting;
/**
* RcsThread represents a single RCS conversation thread. It holds messages that were sent and
* received and events that occurred on that thread.
- * @hide - TODO(sahinc) make this public
*/
-public abstract class RcsThread implements Parcelable {
- // Since this is an abstract class that gets parcelled, the sub-classes need to write these
- // magic values into the parcel so that we know which type to unparcel into.
- protected static final int RCS_1_TO_1_TYPE = 998;
- protected static final int RCS_GROUP_TYPE = 999;
-
+public abstract class RcsThread {
+ /**
+ * The rcs_participant_thread_id that represents this thread in the database
+ * @hide
+ */
protected int mThreadId;
+ /**
+ * @hide
+ */
protected RcsThread(int threadId) {
mThreadId = threadId;
}
- protected RcsThread(Parcel in) {
- mThreadId = in.readInt();
+ /**
+ * @return Returns the summary of the latest message in this {@link RcsThread} packaged in an
+ * {@link RcsMessageSnippet} object
+ */
+ @WorkerThread
+ @NonNull
+ public RcsMessageSnippet getSnippet() throws RcsMessageStoreException {
+ return RcsControllerCall.call(iRcs -> iRcs.getMessageSnippet(mThreadId));
+ }
+
+ /**
+ * Adds a new {@link RcsIncomingMessage} to this RcsThread and persists it in storage.
+ *
+ * @throws RcsMessageStoreException if the message could not be persisted into storage.
+ */
+ @WorkerThread
+ @NonNull
+ public RcsIncomingMessage addIncomingMessage(
+ @NonNull RcsIncomingMessageCreationParams rcsIncomingMessageCreationParams)
+ throws RcsMessageStoreException {
+ return new RcsIncomingMessage(RcsControllerCall.call(iRcs -> iRcs.addIncomingMessage(
+ mThreadId, rcsIncomingMessageCreationParams)));
}
- public static final Creator<RcsThread> CREATOR = new Creator<RcsThread>() {
- @Override
- public RcsThread createFromParcel(Parcel in) {
- int type = in.readInt();
+ /**
+ * Adds a new {@link RcsOutgoingMessage} to this RcsThread and persists it in storage.
+ *
+ * @throws RcsMessageStoreException if the message could not be persisted into storage.
+ */
+ @WorkerThread
+ @NonNull
+ public RcsOutgoingMessage addOutgoingMessage(
+ @NonNull RcsOutgoingMessageCreationParams rcsOutgoingMessageCreationParams)
+ throws RcsMessageStoreException {
+ int messageId = RcsControllerCall.call(iRcs -> iRcs.addOutgoingMessage(
+ mThreadId, rcsOutgoingMessageCreationParams));
- switch (type) {
- case RCS_1_TO_1_TYPE:
- return new Rcs1To1Thread(in);
- case RCS_GROUP_TYPE:
- return new RcsGroupThread(in);
- default:
- Log.e(RcsMessageStore.TAG, "Cannot unparcel RcsThread, wrong type: " + type);
- }
- return null;
- }
+ return new RcsOutgoingMessage(messageId);
+ }
+
+ /**
+ * Deletes an {@link RcsMessage} from this RcsThread and updates the storage.
+ *
+ * @param rcsMessage The message to delete from the thread
+ * @throws RcsMessageStoreException if the message could not be deleted
+ */
+ @WorkerThread
+ public void deleteMessage(@NonNull RcsMessage rcsMessage) throws RcsMessageStoreException {
+ RcsControllerCall.callWithNoReturn(
+ iRcs -> iRcs.deleteMessage(rcsMessage.getId(), rcsMessage.isIncoming(), mThreadId,
+ isGroup()));
+ }
+
+ /**
+ * Convenience function for loading all the {@link RcsMessage}s in this {@link RcsThread}. For
+ * a more detailed and paginated query, please use
+ * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)}
+ *
+ * @return Loads the {@link RcsMessage}s in this thread and returns them in an immutable list.
+ * @throws RcsMessageStoreException if the messages could not be read from the storage
+ */
+ @WorkerThread
+ @NonNull
+ public RcsMessageQueryResult getMessages() throws RcsMessageStoreException {
+ RcsMessageQueryParams queryParameters =
+ new RcsMessageQueryParams.Builder().setThread(this).build();
+ return RcsControllerCall.call(iRcs -> iRcs.getMessages(queryParameters));
+ }
- @Override
- public RcsThread[] newArray(int size) {
- return new RcsThread[0];
- }
- };
+ /**
+ * @return Returns whether this is a group thread or not
+ */
+ public abstract boolean isGroup();
- @Override
- public int describeContents() {
- return 0;
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public int getThreadId() {
+ return mThreadId;
}
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mThreadId);
+ /**
+ * @hide
+ */
+ public int getThreadType() {
+ return isGroup() ? THREAD_TYPE_GROUP : THREAD_TYPE_1_TO_1;
}
}
diff --git a/telephony/java/android/telephony/ims/RcsThreadEvent.aidl b/telephony/java/android/telephony/ims/RcsThreadEvent.aidl
deleted file mode 100644
index 4a40d8906bbb..000000000000
--- a/telephony/java/android/telephony/ims/RcsThreadEvent.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-parcelable RcsThreadEvent;
diff --git a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl b/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
deleted file mode 100644
index 82d985df4c6c..000000000000
--- a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-parcelable RcsThreadIconChangedEvent;
diff --git a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.java b/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.java
deleted file mode 100644
index b308fef46435..000000000000
--- a/telephony/java/android/telephony/ims/RcsThreadIconChangedEvent.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import android.os.Parcel;
-
-/**
- * An event that indicates an {@link RcsGroupThread}'s icon was changed.
- * @hide - TODO(sahinc) make this public
- */
-public class RcsThreadIconChangedEvent extends RcsThreadEvent {
- public static final Creator<RcsThreadIconChangedEvent> CREATOR =
- new Creator<RcsThreadIconChangedEvent>() {
- @Override
- public RcsThreadIconChangedEvent createFromParcel(Parcel in) {
- return new RcsThreadIconChangedEvent(in);
- }
-
- @Override
- public RcsThreadIconChangedEvent[] newArray(int size) {
- return new RcsThreadIconChangedEvent[size];
- }
- };
-
- protected RcsThreadIconChangedEvent(Parcel in) {
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.aidl b/telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.aidl
deleted file mode 100644
index 54a311d02958..000000000000
--- a/telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-parcelable RcsThreadNameChangedEvent;
diff --git a/telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.java b/telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.java
deleted file mode 100644
index 6f5cfdf3b4c4..000000000000
--- a/telephony/java/android/telephony/ims/RcsThreadNameChangedEvent.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import android.os.Parcel;
-
-/**
- * An event that indicates an {@link RcsGroupThread}'s name was changed.
- * @hide - TODO(sahinc) make this public
- */
-public class RcsThreadNameChangedEvent extends RcsThreadEvent {
- public static final Creator<RcsThreadNameChangedEvent> CREATOR =
- new Creator<RcsThreadNameChangedEvent>() {
- @Override
- public RcsThreadNameChangedEvent createFromParcel(Parcel in) {
- return new RcsThreadNameChangedEvent(in);
- }
-
- @Override
- public RcsThreadNameChangedEvent[] newArray(int size) {
- return new RcsThreadNameChangedEvent[size];
- }
- };
-
- protected RcsThreadNameChangedEvent(Parcel in) {
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.aidl b/telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.aidl
deleted file mode 100644
index 047a42466ee7..000000000000
--- a/telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-parcelable RcsThreadParticipantJoinedEvent;
diff --git a/telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.java b/telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.java
deleted file mode 100644
index 5c4073c430e7..000000000000
--- a/telephony/java/android/telephony/ims/RcsThreadParticipantJoinedEvent.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import android.os.Parcel;
-
-/**
- * An event that indicates an RCS participant has joined an {@link RcsGroupThread}.
- * @hide - TODO(sahinc) make this public
- */
-public class RcsThreadParticipantJoinedEvent extends RcsThreadEvent {
- public static final Creator<RcsThreadParticipantJoinedEvent> CREATOR =
- new Creator<RcsThreadParticipantJoinedEvent>() {
- @Override
- public RcsThreadParticipantJoinedEvent createFromParcel(Parcel in) {
- return new RcsThreadParticipantJoinedEvent(in);
- }
-
- @Override
- public RcsThreadParticipantJoinedEvent[] newArray(int size) {
- return new RcsThreadParticipantJoinedEvent[size];
- }
- };
-
- protected RcsThreadParticipantJoinedEvent(Parcel in) {
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.aidl b/telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.aidl
deleted file mode 100644
index 52f9bbd3cd93..000000000000
--- a/telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-parcelable RcsThreadParticipantLeftEvent;
diff --git a/telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.java b/telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.java
deleted file mode 100644
index 4bf86b90ebb7..000000000000
--- a/telephony/java/android/telephony/ims/RcsThreadParticipantLeftEvent.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import android.os.Parcel;
-
-/**
- * An event that indicates an RCS participant has left an {@link RcsGroupThread}.
- * @hide - TODO(sahinc) make this public
- */
-public class RcsThreadParticipantLeftEvent extends RcsThreadEvent {
- public static final Creator<RcsThreadParticipantLeftEvent> CREATOR =
- new Creator<RcsThreadParticipantLeftEvent>() {
- @Override
- public RcsThreadParticipantLeftEvent createFromParcel(Parcel in) {
- return new RcsThreadParticipantLeftEvent(in);
- }
-
- @Override
- public RcsThreadParticipantLeftEvent[] newArray(int size) {
- return new RcsThreadParticipantLeftEvent[size];
- }
- };
-
- protected RcsThreadParticipantLeftEvent(Parcel in) {
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.aidl b/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.aidl
deleted file mode 100644
index 7bcebfa08fcb..000000000000
--- a/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
-**
-** Copyright 2018, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-package android.telephony.ims;
-
-parcelable RcsThreadQueryContinuationToken;
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.java b/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.java
deleted file mode 100644
index 931e93dc8bf1..000000000000
--- a/telephony/java/android/telephony/ims/RcsThreadQueryContinuationToken.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * A continuation token to provide for {@link RcsMessageStore#getRcsThreads}. Use this token to
- * break large queries into manageable chunks
- * @hide - TODO make this public
- */
-public class RcsThreadQueryContinuationToken implements Parcelable {
- protected RcsThreadQueryContinuationToken(Parcel in) {
- }
-
- public static final Creator<RcsThreadQueryContinuationToken> CREATOR =
- new Creator<RcsThreadQueryContinuationToken>() {
- @Override
- public RcsThreadQueryContinuationToken createFromParcel(Parcel in) {
- return new RcsThreadQueryContinuationToken(in);
- }
-
- @Override
- public RcsThreadQueryContinuationToken[] newArray(int size) {
- return new RcsThreadQueryContinuationToken[size];
- }
- };
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryParameters.aidl b/telephony/java/android/telephony/ims/RcsThreadQueryParameters.aidl
deleted file mode 100644
index feb2d4dec094..000000000000
--- a/telephony/java/android/telephony/ims/RcsThreadQueryParameters.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
-**
-** Copyright 2018, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-package android.telephony.ims;
-
-parcelable RcsThreadQueryParameters;
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryParameters.java b/telephony/java/android/telephony/ims/RcsThreadQueryParameters.java
deleted file mode 100644
index f2c4ab1884ca..000000000000
--- a/telephony/java/android/telephony/ims/RcsThreadQueryParameters.java
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import android.annotation.CheckResult;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.security.InvalidParameterException;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * The parameters to pass into {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParameters)} in
- * order to select a subset of {@link RcsThread}s present in the message store.
- * @hide TODO - make the Builder and builder() public. The rest should stay internal only.
- */
-public class RcsThreadQueryParameters implements Parcelable {
- private final boolean mIsGroup;
- private final Set<RcsParticipant> mRcsParticipants;
- private final int mLimit;
- private final boolean mIsAscending;
-
- RcsThreadQueryParameters(boolean isGroup, Set<RcsParticipant> participants, int limit,
- boolean isAscending) {
- mIsGroup = isGroup;
- mRcsParticipants = participants;
- mLimit = limit;
- mIsAscending = isAscending;
- }
-
- /**
- * Returns a new builder to build a query with.
- * TODO - make public
- */
- public static Builder builder() {
- return new Builder();
- }
-
- /**
- * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get
- * the list of participants.
- * @hide
- */
- public Set<RcsParticipant> getRcsParticipants() {
- return mRcsParticipants;
- }
-
- /**
- * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get
- * whether group threads should be queried
- * @hide
- */
- public boolean isGroupThread() {
- return mIsGroup;
- }
-
- /**
- * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get
- * the number of tuples the result query should be limited to.
- */
- public int getLimit() {
- return mLimit;
- }
-
- /**
- * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to
- * determine the sort order.
- */
- public boolean isAscending() {
- return mIsAscending;
- }
-
- /**
- * A helper class to build the {@link RcsThreadQueryParameters}.
- */
- public static class Builder {
- private boolean mIsGroupThread;
- private Set<RcsParticipant> mParticipants;
- private int mLimit = 100;
- private boolean mIsAscending;
-
- /**
- * Package private constructor for {@link RcsThreadQueryParameters.Builder}. To obtain this,
- * {@link RcsThreadQueryParameters#builder()} needs to be called.
- */
- Builder() {
- mParticipants = new HashSet<>();
- }
-
- /**
- * Limits the query to only return group threads.
- * @param isGroupThread Whether to limit the query result to group threads.
- * @return The same instance of the builder to chain parameters.
- */
- @CheckResult
- public Builder isGroupThread(boolean isGroupThread) {
- mIsGroupThread = isGroupThread;
- return this;
- }
-
- /**
- * Limits the query to only return threads that contain the given participant.
- * @param participant The participant that must be included in all of the returned threads.
- * @return The same instance of the builder to chain parameters.
- */
- @CheckResult
- public Builder withParticipant(RcsParticipant participant) {
- mParticipants.add(participant);
- return this;
- }
-
- /**
- * Limits the query to only return threads that contain the given list of participants.
- * @param participants An iterable list of participants that must be included in all of the
- * returned threads.
- * @return The same instance of the builder to chain parameters.
- */
- @CheckResult
- public Builder withParticipants(Iterable<RcsParticipant> participants) {
- for (RcsParticipant participant : participants) {
- mParticipants.add(participant);
- }
- return this;
- }
-
- /**
- * Desired number of threads to be returned from the query. Passing in 0 will return all
- * existing threads at once. The limit defaults to 100.
- * @param limit The number to limit the query result to.
- * @return The same instance of the builder to chain parameters.
- * @throws InvalidParameterException If the given limit is negative.
- */
- @CheckResult
- public Builder limitResultsTo(int limit) throws InvalidParameterException {
- if (limit < 0) {
- throw new InvalidParameterException("The query limit must be non-negative");
- }
-
- mLimit = limit;
- return this;
- }
-
- /**
- * Sorts the results returned from the query via thread IDs.
- *
- * TODO - add sorting support for other fields
- *
- * @param isAscending whether to sort in ascending order or not
- * @return The same instance of the builder to chain parameters.
- */
- @CheckResult
- public Builder sort(boolean isAscending) {
- mIsAscending = isAscending;
- return this;
- }
-
- /**
- * Builds the {@link RcsThreadQueryParameters} to use in
- * {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParameters)}
- *
- * @return An instance of {@link RcsThreadQueryParameters} to use with the thread query.
- */
- public RcsThreadQueryParameters build() {
- return new RcsThreadQueryParameters(
- mIsGroupThread, mParticipants, mLimit, mIsAscending);
- }
- }
-
- /**
- * Parcelable boilerplate below.
- */
- protected RcsThreadQueryParameters(Parcel in) {
- mIsGroup = in.readBoolean();
-
- ArrayList<RcsParticipant> participantArrayList = new ArrayList<>();
- in.readTypedList(participantArrayList, RcsParticipant.CREATOR);
- mRcsParticipants = new HashSet<>(participantArrayList);
-
- mLimit = in.readInt();
- mIsAscending = in.readBoolean();
- }
-
- public static final Creator<RcsThreadQueryParameters> CREATOR =
- new Creator<RcsThreadQueryParameters>() {
- @Override
- public RcsThreadQueryParameters createFromParcel(Parcel in) {
- return new RcsThreadQueryParameters(in);
- }
-
- @Override
- public RcsThreadQueryParameters[] newArray(int size) {
- return new RcsThreadQueryParameters[size];
- }
- };
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeBoolean(mIsGroup);
- dest.writeTypedList(new ArrayList<>(mRcsParticipants));
- dest.writeInt(mLimit);
- dest.writeBoolean(mIsAscending);
- }
-
-}
diff --git a/telephony/java/android/telephony/ims/RcsFileTransferPart.aidl b/telephony/java/android/telephony/ims/RcsThreadQueryParams.aidl
index eaf312877deb..3f351dc5efee 100644
--- a/telephony/java/android/telephony/ims/RcsFileTransferPart.aidl
+++ b/telephony/java/android/telephony/ims/RcsThreadQueryParams.aidl
@@ -17,4 +17,4 @@
package android.telephony.ims;
-parcelable RcsFileTransferPart;
+parcelable RcsThreadQueryParams;
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryParams.java b/telephony/java/android/telephony/ims/RcsThreadQueryParams.java
new file mode 100644
index 000000000000..05a5a3917691
--- /dev/null
+++ b/telephony/java/android/telephony/ims/RcsThreadQueryParams.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony.ims;
+
+import android.annotation.CheckResult;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.security.InvalidParameterException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * The parameters to pass into {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParams)} in
+ * order to select a subset of {@link RcsThread}s present in the message store.
+ */
+public final class RcsThreadQueryParams implements Parcelable {
+ /**
+ * Bitmask flag to be used with {@link Builder#setThreadType(int)} to make
+ * {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParams)} return
+ * {@link RcsGroupThread}s.
+ */
+ public static final int THREAD_TYPE_GROUP = 0x0001;
+
+ /**
+ * Bitmask flag to be used with {@link Builder#setThreadType(int)} to make
+ * {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParams)} return
+ * {@link Rcs1To1Thread}s.
+ */
+ public static final int THREAD_TYPE_1_TO_1 = 0x0002;
+
+ // The type of threads to be filtered with the query
+ private final int mThreadType;
+ // The list of participants that are expected in the resulting threads
+ private final List<Integer> mRcsParticipantIds;
+ // The number of RcsThread's that should be returned with this query
+ private final int mLimit;
+ // The property which the result of the query should be sorted against
+ private final @SortingProperty int mSortingProperty;
+ // Whether the sorting should be done in ascending
+ private final boolean mIsAscending;
+
+ /**
+ * Flag to be used with {@link Builder#setSortProperty(int)} to denote that the results should
+ * be sorted in the order of {@link RcsThread} creation time for faster results.
+ */
+ public static final int SORT_BY_CREATION_ORDER = 0;
+
+ /**
+ * Flag to be used with {@link Builder#setSortProperty(int)} to denote that the results should
+ * be sorted according to the timestamp of {@link RcsThread#getSnippet()}
+ */
+ public static final int SORT_BY_TIMESTAMP = 1;
+
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({SORT_BY_CREATION_ORDER, SORT_BY_TIMESTAMP})
+ public @interface SortingProperty {
+ }
+
+ /**
+ * @hide
+ */
+ public static final String THREAD_QUERY_PARAMETERS_KEY = "thread_query_parameters";
+
+ RcsThreadQueryParams(int threadType, Set<RcsParticipant> participants,
+ int limit, int sortingProperty, boolean isAscending) {
+ mThreadType = threadType;
+ mRcsParticipantIds = convertParticipantSetToIdList(participants);
+ mLimit = limit;
+ mSortingProperty = sortingProperty;
+ mIsAscending = isAscending;
+ }
+
+ private static List<Integer> convertParticipantSetToIdList(Set<RcsParticipant> participants) {
+ List<Integer> ids = new ArrayList<>(participants.size());
+ for (RcsParticipant participant : participants) {
+ ids.add(participant.getId());
+ }
+ return ids;
+ }
+
+ /**
+ * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get
+ * the list of participant IDs.
+ *
+ * As we don't expose any integer ID's to API users, this should stay hidden
+ *
+ * @hide - not meant for public use
+ */
+ public List<Integer> getRcsParticipantsIds() {
+ return Collections.unmodifiableList(mRcsParticipantIds);
+ }
+
+ /**
+ * @return Returns the bitmask flag for types of {@link RcsThread}s that this query should
+ * return.
+ */
+ public int getThreadType() {
+ return mThreadType;
+ }
+
+ /**
+ * @return Returns the number of {@link RcsThread}s to be returned from the query. A value
+ * of 0 means there is no set limit.
+ */
+ public int getLimit() {
+ return mLimit;
+ }
+
+ /**
+ * @return Returns the property that will be used to sort the result against.
+ * @see SortingProperty
+ */
+ public @SortingProperty int getSortingProperty() {
+ return mSortingProperty;
+ }
+
+ /**
+ * @return Returns {@code true} if the result set will be sorted in ascending order,
+ * {@code false} if it will be sorted in descending order.
+ */
+ public boolean getSortDirection() {
+ return mIsAscending;
+ }
+
+ /**
+ * A helper class to build the {@link RcsThreadQueryParams}.
+ */
+ public static class Builder {
+ private int mThreadType;
+ private Set<RcsParticipant> mParticipants;
+ private int mLimit = 100;
+ private @SortingProperty int mSortingProperty;
+ private boolean mIsAscending;
+
+ /**
+ * Constructs a {@link RcsThreadQueryParams.Builder} to help build an
+ * {@link RcsThreadQueryParams}
+ */
+ public Builder() {
+ mParticipants = new HashSet<>();
+ }
+
+ /**
+ * Limits the query to only return group threads.
+ *
+ * @param threadType Whether to limit the query result to group threads.
+ * @return The same instance of the builder to chain parameters.
+ * @see RcsThreadQueryParams#THREAD_TYPE_GROUP
+ * @see RcsThreadQueryParams#THREAD_TYPE_1_TO_1
+ */
+ @CheckResult
+ public Builder setThreadType(int threadType) {
+ mThreadType = threadType;
+ return this;
+ }
+
+ /**
+ * Limits the query to only return threads that contain the given participant. If this
+ * property was not set, participants will not be taken into account while querying for
+ * threads.
+ *
+ * @param participant The participant that must be included in all of the returned threads.
+ * @return The same instance of the builder to chain parameters.
+ */
+ @CheckResult
+ public Builder setParticipant(@NonNull RcsParticipant participant) {
+ mParticipants.add(participant);
+ return this;
+ }
+
+ /**
+ * Limits the query to only return threads that contain the given list of participants. If
+ * this property was not set, participants will not be taken into account while querying
+ * for threads.
+ *
+ * @param participants An iterable list of participants that must be included in all of the
+ * returned threads.
+ * @return The same instance of the builder to chain parameters.
+ */
+ @CheckResult
+ public Builder setParticipants(@NonNull List<RcsParticipant> participants) {
+ mParticipants.addAll(participants);
+ return this;
+ }
+
+ /**
+ * Desired number of threads to be returned from the query. Passing in 0 will return all
+ * existing threads at once. The limit defaults to 100.
+ *
+ * @param limit The number to limit the query result to.
+ * @return The same instance of the builder to chain parameters.
+ * @throws InvalidParameterException If the given limit is negative.
+ */
+ @CheckResult
+ public Builder setResultLimit(@IntRange(from = 0) int limit)
+ throws InvalidParameterException {
+ if (limit < 0) {
+ throw new InvalidParameterException("The query limit must be non-negative");
+ }
+
+ mLimit = limit;
+ return this;
+ }
+
+ /**
+ * Sets the property where the results should be sorted against. Defaults to
+ * {@link SortingProperty#SORT_BY_CREATION_ORDER}
+ *
+ * @param sortingProperty whether to sort in ascending order or not
+ * @return The same instance of the builder to chain parameters.
+ */
+ @CheckResult
+ public Builder setSortProperty(@SortingProperty int sortingProperty) {
+ mSortingProperty = sortingProperty;
+ return this;
+ }
+
+ /**
+ * Sets whether the results should be sorted ascending or descending
+ *
+ * @param isAscending whether the results should be sorted ascending
+ * @return The same instance of the builder to chain parameters.
+ */
+ @CheckResult
+ public Builder setSortDirection(boolean isAscending) {
+ mIsAscending = isAscending;
+ return this;
+ }
+
+ /**
+ * Builds the {@link RcsThreadQueryParams} to use in
+ * {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParams)}
+ *
+ * @return An instance of {@link RcsThreadQueryParams} to use with the thread query.
+ */
+ public RcsThreadQueryParams build() {
+ return new RcsThreadQueryParams(mThreadType, mParticipants, mLimit,
+ mSortingProperty, mIsAscending);
+ }
+ }
+
+ /**
+ * Parcelable boilerplate below.
+ */
+ private RcsThreadQueryParams(Parcel in) {
+ mThreadType = in.readInt();
+ mRcsParticipantIds = new ArrayList<>();
+ in.readList(mRcsParticipantIds, Integer.class.getClassLoader());
+ mLimit = in.readInt();
+ mSortingProperty = in.readInt();
+ mIsAscending = in.readByte() == 1;
+ }
+
+ public static final Creator<RcsThreadQueryParams> CREATOR =
+ new Creator<RcsThreadQueryParams>() {
+ @Override
+ public RcsThreadQueryParams createFromParcel(Parcel in) {
+ return new RcsThreadQueryParams(in);
+ }
+
+ @Override
+ public RcsThreadQueryParams[] newArray(int size) {
+ return new RcsThreadQueryParams[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mThreadType);
+ dest.writeList(mRcsParticipantIds);
+ dest.writeInt(mLimit);
+ dest.writeInt(mSortingProperty);
+ dest.writeByte((byte) (mIsAscending ? 1 : 0));
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryResult.aidl b/telephony/java/android/telephony/ims/RcsThreadQueryResult.aidl
index 4b06529d1294..b1d5cf4c7211 100644
--- a/telephony/java/android/telephony/ims/RcsThreadQueryResult.aidl
+++ b/telephony/java/android/telephony/ims/RcsThreadQueryResult.aidl
@@ -1,19 +1,19 @@
/*
-**
-** Copyright 2018, 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.
-*/
+ *
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
package android.telephony.ims;
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryResult.java b/telephony/java/android/telephony/ims/RcsThreadQueryResult.java
index 47715f8410d6..1cac61d1aa64 100644
--- a/telephony/java/android/telephony/ims/RcsThreadQueryResult.java
+++ b/telephony/java/android/telephony/ims/RcsThreadQueryResult.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2018 The Android Open Source Project
+ * Copyright (C) 2019 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.
@@ -16,22 +16,28 @@
package android.telephony.ims;
+import static android.provider.Telephony.RcsColumns.RcsUnifiedThreadColumns.THREAD_TYPE_1_TO_1;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
+import com.android.ims.RcsTypeIdPair;
+
+import java.util.ArrayList;
import java.util.List;
/**
- * The result of a {@link RcsMessageStore#getRcsThreads(RcsThreadQueryContinuationToken,
- * RcsThreadQueryParameters)}
+ * The result of a {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParams)}
* call. This class allows getting the token for querying the next batch of threads in order to
* prevent handling large amounts of data at once.
- *
- * @hide
*/
-public class RcsThreadQueryResult implements Parcelable {
- private RcsThreadQueryContinuationToken mContinuationToken;
- private List<RcsThread> mRcsThreads;
+public final class RcsThreadQueryResult implements Parcelable {
+ // A token for the caller to continue their query for the next batch of results
+ private RcsQueryContinuationToken mContinuationToken;
+ // The list of thread IDs returned with this query
+ private List<RcsTypeIdPair> mRcsThreadIds;
/**
* Internal constructor for {@link com.android.internal.telephony.ims.RcsMessageStoreController}
@@ -40,31 +46,47 @@ public class RcsThreadQueryResult implements Parcelable {
* @hide
*/
public RcsThreadQueryResult(
- RcsThreadQueryContinuationToken continuationToken, List<RcsThread> rcsThreads) {
+ RcsQueryContinuationToken continuationToken,
+ List<RcsTypeIdPair> rcsThreadIds) {
mContinuationToken = continuationToken;
- mRcsThreads = rcsThreads;
+ mRcsThreadIds = rcsThreadIds;
}
/**
* Returns a token to call
- * {@link RcsMessageStore#getRcsThreads(RcsThreadQueryContinuationToken)}
+ * {@link RcsMessageStore#getRcsThreads(RcsQueryContinuationToken)}
* to get the next batch of {@link RcsThread}s.
*/
- public RcsThreadQueryContinuationToken nextChunkToken() {
+ @Nullable
+ public RcsQueryContinuationToken getContinuationToken() {
return mContinuationToken;
}
/**
* Returns all the RcsThreads in the current query result. Call {@link
- * RcsMessageStore#getRcsThreads(RcsThreadQueryContinuationToken)} to get the next batch of
+ * RcsMessageStore#getRcsThreads(RcsQueryContinuationToken)} to get the next batch of
* {@link RcsThread}s.
*/
+ @NonNull
public List<RcsThread> getThreads() {
- return mRcsThreads;
+ List<RcsThread> rcsThreads = new ArrayList<>();
+
+ for (RcsTypeIdPair typeIdPair : mRcsThreadIds) {
+ if (typeIdPair.getType() == THREAD_TYPE_1_TO_1) {
+ rcsThreads.add(new Rcs1To1Thread(typeIdPair.getId()));
+ } else {
+ rcsThreads.add(new RcsGroupThread(typeIdPair.getId()));
+ }
+ }
+
+ return rcsThreads;
}
- protected RcsThreadQueryResult(Parcel in) {
- // TODO - implement
+ private RcsThreadQueryResult(Parcel in) {
+ mContinuationToken = in.readParcelable(
+ RcsQueryContinuationToken.class.getClassLoader());
+ mRcsThreadIds = new ArrayList<>();
+ in.readList(mRcsThreadIds, Integer.class.getClassLoader());
}
public static final Creator<RcsThreadQueryResult> CREATOR =
@@ -87,6 +109,7 @@ public class RcsThreadQueryResult implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
- // TODO - implement
+ dest.writeParcelable(mContinuationToken, flags);
+ dest.writeList(mRcsThreadIds);
}
}
diff --git a/telephony/java/android/telephony/ims/aidl/IRcs.aidl b/telephony/java/android/telephony/ims/aidl/IRcs.aidl
index 0c958ba719f3..2478f8cff6b7 100644
--- a/telephony/java/android/telephony/ims/aidl/IRcs.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IRcs.aidl
@@ -16,10 +16,19 @@
package android.telephony.ims.aidl;
-import android.telephony.ims.RcsParticipant;
-import android.telephony.ims.Rcs1To1Thread;
-import android.telephony.ims.RcsThreadQueryContinuationToken;
-import android.telephony.ims.RcsThreadQueryParameters;
+import android.net.Uri;
+import android.telephony.ims.RcsEventQueryParams;
+import android.telephony.ims.RcsEventQueryResult;
+import android.telephony.ims.RcsFileTransferCreationParams;
+import android.telephony.ims.RcsIncomingMessageCreationParams;
+import android.telephony.ims.RcsMessageSnippet;
+import android.telephony.ims.RcsMessageQueryParams;
+import android.telephony.ims.RcsMessageQueryResult;
+import android.telephony.ims.RcsOutgoingMessageCreationParams;
+import android.telephony.ims.RcsParticipantQueryParams;
+import android.telephony.ims.RcsParticipantQueryResult;
+import android.telephony.ims.RcsQueryContinuationToken;
+import android.telephony.ims.RcsThreadQueryParams;
import android.telephony.ims.RcsThreadQueryResult;
/**
@@ -27,23 +36,231 @@ import android.telephony.ims.RcsThreadQueryResult;
* {@hide}
*/
interface IRcs {
+ /////////////////////////
// RcsMessageStore APIs
- RcsThreadQueryResult getRcsThreads(in RcsThreadQueryParameters queryParameters);
+ /////////////////////////
+ RcsThreadQueryResult getRcsThreads(in RcsThreadQueryParams queryParams);
RcsThreadQueryResult getRcsThreadsWithToken(
- in RcsThreadQueryContinuationToken continuationToken);
+ in RcsQueryContinuationToken continuationToken);
- void deleteThread(int threadId);
+ RcsParticipantQueryResult getParticipants(in RcsParticipantQueryParams queryParams);
- Rcs1To1Thread createRcs1To1Thread(in RcsParticipant participant);
+ RcsParticipantQueryResult getParticipantsWithToken(
+ in RcsQueryContinuationToken continuationToken);
+ RcsMessageQueryResult getMessages(in RcsMessageQueryParams queryParams);
+
+ RcsMessageQueryResult getMessagesWithToken(
+ in RcsQueryContinuationToken continuationToken);
+
+ RcsEventQueryResult getEvents(in RcsEventQueryParams queryParams);
+
+ RcsEventQueryResult getEventsWithToken(
+ in RcsQueryContinuationToken continuationToken);
+
+ // returns true if the thread was successfully deleted
+ boolean deleteThread(int threadId, int threadType);
+
+ // Creates an Rcs1To1Thread and returns its row ID
+ int createRcs1To1Thread(int participantId);
+
+ // Creates an RcsGroupThread and returns its row ID
+ int createGroupThread(in int[] participantIds, String groupName, in Uri groupIcon);
+
+ /////////////////////////
// RcsThread APIs
- int getMessageCount(int rcsThreadId);
+ /////////////////////////
+
+ // Creates a new RcsIncomingMessage on the given thread and returns its row ID
+ int addIncomingMessage(int rcsThreadId,
+ in RcsIncomingMessageCreationParams rcsIncomingMessageCreationParams);
+
+ // Creates a new RcsOutgoingMessage on the given thread and returns its row ID
+ int addOutgoingMessage(int rcsThreadId,
+ in RcsOutgoingMessageCreationParams rcsOutgoingMessageCreationParams);
+
+ // TODO: modify RcsProvider URI's to allow deleting a message without specifying its thread
+ void deleteMessage(int rcsMessageId, boolean isIncoming, int rcsThreadId, boolean isGroup);
+
+ RcsMessageSnippet getMessageSnippet(int rcsThreadId);
+
+ /////////////////////////
+ // Rcs1To1Thread APIs
+ /////////////////////////
+ void set1To1ThreadFallbackThreadId(int rcsThreadId, long fallbackId);
+
+ long get1To1ThreadFallbackThreadId(int rcsThreadId);
+
+ int get1To1ThreadOtherParticipantId(int rcsThreadId);
+
+ /////////////////////////
+ // RcsGroupThread APIs
+ /////////////////////////
+ void setGroupThreadName(int rcsThreadId, String groupName);
+
+ String getGroupThreadName(int rcsThreadId);
+
+ void setGroupThreadIcon(int rcsThreadId, in Uri groupIcon);
+
+ Uri getGroupThreadIcon(int rcsThreadId);
+
+ void setGroupThreadOwner(int rcsThreadId, int participantId);
+
+ int getGroupThreadOwner(int rcsThreadId);
+
+ void setGroupThreadConferenceUri(int rcsThreadId, in Uri conferenceUri);
+
+ Uri getGroupThreadConferenceUri(int rcsThreadId);
+ void addParticipantToGroupThread(int rcsThreadId, int participantId);
+
+ void removeParticipantFromGroupThread(int rcsThreadId, int participantId);
+
+ /////////////////////////
// RcsParticipant APIs
- RcsParticipant createRcsParticipant(String canonicalAddress);
+ /////////////////////////
+
+ // Creates a new RcsParticipant and returns its rowId
+ int createRcsParticipant(String canonicalAddress, String alias);
+
+ String getRcsParticipantCanonicalAddress(int participantId);
+
+ String getRcsParticipantAlias(int participantId);
+
+ void setRcsParticipantAlias(int id, String alias);
+
+ String getRcsParticipantContactId(int participantId);
+
+ void setRcsParticipantContactId(int participantId, String contactId);
+
+ /////////////////////////
+ // RcsMessage APIs
+ /////////////////////////
+ void setMessageSubId(int messageId, boolean isIncoming, int subId);
+
+ int getMessageSubId(int messageId, boolean isIncoming);
+
+ void setMessageStatus(int messageId, boolean isIncoming, int status);
+
+ int getMessageStatus(int messageId, boolean isIncoming);
+
+ void setMessageOriginationTimestamp(int messageId, boolean isIncoming, long originationTimestamp);
+
+ long getMessageOriginationTimestamp(int messageId, boolean isIncoming);
+
+ void setGlobalMessageIdForMessage(int messageId, boolean isIncoming, String globalId);
+
+ String getGlobalMessageIdForMessage(int messageId, boolean isIncoming);
+
+ void setMessageArrivalTimestamp(int messageId, boolean isIncoming, long arrivalTimestamp);
+
+ long getMessageArrivalTimestamp(int messageId, boolean isIncoming);
+
+ void setMessageSeenTimestamp(int messageId, boolean isIncoming, long seenTimestamp);
+
+ long getMessageSeenTimestamp(int messageId, boolean isIncoming);
+
+ void setTextForMessage(int messageId, boolean isIncoming, String text);
+
+ String getTextForMessage(int messageId, boolean isIncoming);
+
+ void setLatitudeForMessage(int messageId, boolean isIncoming, double latitude);
+
+ double getLatitudeForMessage(int messageId, boolean isIncoming);
+
+ void setLongitudeForMessage(int messageId, boolean isIncoming, double longitude);
+
+ double getLongitudeForMessage(int messageId, boolean isIncoming);
+
+ // Returns the ID's of the file transfers attached to the given message
+ int[] getFileTransfersAttachedToMessage(int messageId, boolean isIncoming);
+
+ int getSenderParticipant(int messageId);
+
+ /////////////////////////
+ // RcsOutgoingMessageDelivery APIs
+ /////////////////////////
+
+ // Returns the participant ID's that this message is intended to be delivered to
+ int[] getMessageRecipients(int messageId);
+
+ long getOutgoingDeliveryDeliveredTimestamp(int messageId, int participantId);
+
+ void setOutgoingDeliveryDeliveredTimestamp(int messageId, int participantId, long deliveredTimestamp);
+
+ long getOutgoingDeliverySeenTimestamp(int messageId, int participantId);
+
+ void setOutgoingDeliverySeenTimestamp(int messageId, int participantId, long seenTimestamp);
+
+ int getOutgoingDeliveryStatus(int messageId, int participantId);
+
+ void setOutgoingDeliveryStatus(int messageId, int participantId, int status);
+
+ /////////////////////////
+ // RcsFileTransferPart APIs
+ /////////////////////////
+
+ // Performs the initial write to storage and returns the row ID.
+ int storeFileTransfer(int messageId, boolean isIncoming,
+ in RcsFileTransferCreationParams fileTransferCreationParams);
+
+ void deleteFileTransfer(int partId);
+
+ void setFileTransferSessionId(int partId, String sessionId);
+
+ String getFileTransferSessionId(int partId);
+
+ void setFileTransferContentUri(int partId, in Uri contentUri);
+
+ Uri getFileTransferContentUri(int partId);
+
+ void setFileTransferContentType(int partId, String contentType);
+
+ String getFileTransferContentType(int partId);
+
+ void setFileTransferFileSize(int partId, long fileSize);
+
+ long getFileTransferFileSize(int partId);
+
+ void setFileTransferTransferOffset(int partId, long transferOffset);
+
+ long getFileTransferTransferOffset(int partId);
+
+ void setFileTransferStatus(int partId, int transferStatus);
+
+ int getFileTransferStatus(int partId);
+
+ void setFileTransferWidth(int partId, int width);
+
+ int getFileTransferWidth(int partId);
+
+ void setFileTransferHeight(int partId, int height);
+
+ int getFileTransferHeight(int partId);
+
+ void setFileTransferLength(int partId, long length);
+
+ long getFileTransferLength(int partId);
+
+ void setFileTransferPreviewUri(int partId, in Uri uri);
+
+ Uri getFileTransferPreviewUri(int partId);
+
+ void setFileTransferPreviewType(int partId, String type);
+
+ String getFileTransferPreviewType(int partId);
+
+ /////////////////////////
+ // RcsEvent APIs
+ /////////////////////////
+ int createGroupThreadNameChangedEvent(long timestamp, int threadId, int originationParticipantId, String newName);
+
+ int createGroupThreadIconChangedEvent(long timestamp, int threadId, int originationParticipantId, in Uri newIcon);
+
+ int createGroupThreadParticipantJoinedEvent(long timestamp, int threadId, int originationParticipantId, int participantId);
- void updateRcsParticipantCanonicalAddress(int id, String canonicalAddress);
+ int createGroupThreadParticipantLeftEvent(long timestamp, int threadId, int originationParticipantId, int participantId);
- void updateRcsParticipantAlias(int id, String alias);
+ int createParticipantAliasChangedEvent(long timestamp, int participantId, String newAlias);
} \ No newline at end of file
diff --git a/telephony/java/android/telephony/mbms/GroupCallCallback.java b/telephony/java/android/telephony/mbms/GroupCallCallback.java
index 77e36bbcf2ae..603f4e6d2030 100644
--- a/telephony/java/android/telephony/mbms/GroupCallCallback.java
+++ b/telephony/java/android/telephony/mbms/GroupCallCallback.java
@@ -57,7 +57,7 @@ public interface GroupCallCallback {
* @param errorCode The error code.
* @param message A human-readable message generated by the middleware for debugging purposes.
*/
- void onError(@GroupCallError int errorCode, @Nullable String message);
+ default void onError(@GroupCallError int errorCode, @Nullable String message) {}
/**
* Called to indicate this call has changed state.
@@ -65,8 +65,8 @@ public interface GroupCallCallback {
* See {@link GroupCall#STATE_STOPPED}, {@link GroupCall#STATE_STARTED}
* and {@link GroupCall#STATE_STALLED}.
*/
- void onGroupCallStateChanged(@GroupCall.GroupCallState int state,
- @GroupCall.GroupCallStateChangeReason int reason);
+ default void onGroupCallStateChanged(@GroupCall.GroupCallState int state,
+ @GroupCall.GroupCallStateChangeReason int reason) {}
/**
* Broadcast Signal Strength updated.
@@ -78,5 +78,6 @@ public interface GroupCallCallback {
* {@link #SIGNAL_STRENGTH_UNAVAILABLE} if broadcast is not available
* for this call due to timing, geography or popularity.
*/
- void onBroadcastSignalStrengthUpdated(@IntRange(from = -1, to = 4) int signalStrength);
+ default void onBroadcastSignalStrengthUpdated(
+ @IntRange(from = -1, to = 4) int signalStrength) {}
}
diff --git a/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java b/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java
index 04e7ba1af372..ac7e17271e34 100644
--- a/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java
+++ b/telephony/java/android/telephony/mbms/MbmsGroupCallSessionCallback.java
@@ -57,7 +57,7 @@ public interface MbmsGroupCallSessionCallback {
* @param errorCode The error code.
* @param message A human-readable message generated by the middleware for debugging purposes.
*/
- void onError(@GroupCallError int errorCode, @Nullable String message);
+ default void onError(@GroupCallError int errorCode, @Nullable String message) {}
/**
* Indicates that the list of currently available SAIs has been updated. The app may use this
@@ -70,8 +70,8 @@ public interface MbmsGroupCallSessionCallback {
* @param availableSais A list of lists of available SAIS in neighboring cells, where each list
* contains the available SAIs in an individual cell.
*/
- void onAvailableSaisUpdated(@NonNull List<Integer> currentSais,
- @NonNull List<List<Integer>> availableSais);
+ default void onAvailableSaisUpdated(@NonNull List<Integer> currentSais,
+ @NonNull List<List<Integer>> availableSais) {}
/**
* Called soon after the app calls {@link MbmsGroupCallSession#create}. The information supplied
@@ -85,7 +85,7 @@ public interface MbmsGroupCallSessionCallback {
* @param interfaceName The interface name for the data link.
* @param index The index for the data link.
*/
- void onServiceInterfaceAvailable(@NonNull String interfaceName, int index);
+ default void onServiceInterfaceAvailable(@NonNull String interfaceName, int index) {}
/**
* Called to indicate that the middleware has been initialized and is ready.
@@ -95,5 +95,5 @@ public interface MbmsGroupCallSessionCallback {
* delivered via {@link #onError(int, String)} with error code
* {@link MbmsErrors.GeneralErrors#ERROR_MIDDLEWARE_NOT_YET_READY}.
*/
- void onMiddlewareReady();
+ default void onMiddlewareReady() {}
}
diff --git a/telephony/java/com/android/ims/ImsException.java b/telephony/java/com/android/ims/ImsException.java
index f35e88672a23..fea763ed5785 100644
--- a/telephony/java/com/android/ims/ImsException.java
+++ b/telephony/java/com/android/ims/ImsException.java
@@ -21,8 +21,10 @@ import android.telephony.ims.ImsReasonInfo;
/**
* This class defines a general IMS-related exception.
*
+ * @deprecated Use {@link android.telephony.ims.ImsException} instead.
* @hide
*/
+@Deprecated
public class ImsException extends Exception {
/**
diff --git a/telephony/java/com/android/ims/RcsTypeIdPair.java b/telephony/java/com/android/ims/RcsTypeIdPair.java
new file mode 100644
index 000000000000..a5177354002e
--- /dev/null
+++ b/telephony/java/com/android/ims/RcsTypeIdPair.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.ims;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A utility class to pass RCS IDs and types in RPC calls
+ *
+ * @hide
+ */
+public class RcsTypeIdPair implements Parcelable {
+ private int mType;
+ private int mId;
+
+ public RcsTypeIdPair(int type, int id) {
+ mType = type;
+ mId = id;
+ }
+
+ public int getType() {
+ return mType;
+ }
+
+ public void setType(int type) {
+ mType = type;
+ }
+
+ public int getId() {
+ return mId;
+ }
+
+ public void setId(int id) {
+ mId = id;
+ }
+
+ public RcsTypeIdPair(Parcel in) {
+ mType = in.readInt();
+ mId = in.readInt();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mType);
+ dest.writeInt(mId);
+ }
+
+ public static final Creator<RcsTypeIdPair> CREATOR =
+ new Creator<RcsTypeIdPair>() {
+ @Override
+ public RcsTypeIdPair createFromParcel(Parcel in) {
+ return new RcsTypeIdPair(in);
+ }
+
+ @Override
+ public RcsTypeIdPair[] newArray(int size) {
+ return new RcsTypeIdPair[size];
+ }
+ };
+}
diff --git a/telephony/java/com/android/internal/telephony/EncodeException.java b/telephony/java/com/android/internal/telephony/EncodeException.java
index 4e3fac1a2aea..cdc853e09895 100644
--- a/telephony/java/com/android/internal/telephony/EncodeException.java
+++ b/telephony/java/com/android/internal/telephony/EncodeException.java
@@ -22,6 +22,12 @@ import android.annotation.UnsupportedAppUsage;
* {@hide}
*/
public class EncodeException extends Exception {
+
+ private int mError = ERROR_UNENCODABLE;
+
+ public static final int ERROR_UNENCODABLE = 0;
+ public static final int ERROR_EXCEED_SIZE = 1;
+
public EncodeException() {
super();
}
@@ -31,9 +37,18 @@ public class EncodeException extends Exception {
super(s);
}
+ public EncodeException(String s, int error) {
+ super(s);
+ mError = error;
+ }
+
@UnsupportedAppUsage
public EncodeException(char c) {
super("Unencodable char: '" + c + "'");
}
+
+ public int getError() {
+ return mError;
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/GsmAlphabet.java b/telephony/java/com/android/internal/telephony/GsmAlphabet.java
index 84c0e6453104..a774cdc8a280 100644
--- a/telephony/java/com/android/internal/telephony/GsmAlphabet.java
+++ b/telephony/java/com/android/internal/telephony/GsmAlphabet.java
@@ -388,7 +388,7 @@ public class GsmAlphabet {
* GSM extension table
* @return the encoded message
*
- * @throws EncodeException if String is too large to encode
+ * @throws EncodeException if String is too large to encode or any characters are unencodable
*/
@UnsupportedAppUsage
public static byte[] stringToGsm7BitPacked(String data, int startingSeptetOffset,
@@ -402,7 +402,8 @@ public class GsmAlphabet {
}
septetCount += startingSeptetOffset;
if (septetCount > 255) {
- throw new EncodeException("Payload cannot exceed 255 septets");
+ throw new EncodeException(
+ "Payload cannot exceed 255 septets", EncodeException.ERROR_EXCEED_SIZE);
}
int byteCount = ((septetCount * 7) + 7) / 8;
byte[] ret = new byte[byteCount + 1]; // Include space for one byte length prefix.
diff --git a/telephony/java/com/android/internal/telephony/ISub.aidl b/telephony/java/com/android/internal/telephony/ISub.aidl
index 04ec3d1a3df3..a49d2d976d16 100755
--- a/telephony/java/com/android/internal/telephony/ISub.aidl
+++ b/telephony/java/com/android/internal/telephony/ISub.aidl
@@ -276,6 +276,11 @@ interface ISub {
String getSubscriptionProperty(int subId, String propKey, String callingPackage);
+ boolean setSubscriptionEnabled(boolean enable, int subId);
+
+ boolean isSubscriptionEnabled(int subId);
+
+ int getEnabledSubscriptionId(int slotIndex);
/**
* Get the SIM state for the slot index
* @return SIM state as the ordinal of IccCardConstants.State
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index a12792a8cfa6..e6a55585506c 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -765,7 +765,7 @@ interface ITelephony {
* @param subId the id of the subscription.
* @return CellNetworkScanResult containing status of scan and networks.
*/
- CellNetworkScanResult getCellNetworkScanResults(int subId);
+ CellNetworkScanResult getCellNetworkScanResults(int subId, String callingPackage);
/**
* Perform a radio network scan and return the id of this scan.
@@ -774,10 +774,11 @@ interface ITelephony {
* @param request Defines all the configs for network scan.
* @param messenger Callback messages will be sent using this messenger.
* @param binder the binder object instantiated in TelephonyManager.
+ * @param callingPackage the calling package
* @return An id for this scan.
*/
int requestNetworkScan(int subId, in NetworkScanRequest request, in Messenger messenger,
- in IBinder binder);
+ in IBinder binder, in String callingPackage);
/**
* Stop an existing radio network scan.
@@ -1829,4 +1830,26 @@ interface ITelephony {
* @hide
*/
boolean isMultisimCarrierRestricted();
+
+ /**
+ * Switch configs to enable multi-sim or switch back to single-sim
+ * @hide
+ */
+ void switchMultiSimConfig(int numOfSims);
+ /**
+ * Get how many modems have been activated on the phone
+ * @hide
+ */
+ int getNumOfActiveSims();
+
+ /**
+ * Get if reboot is required upon altering modems configurations
+ * @hide
+ */
+ boolean isRebootRequiredForModemConfigChange();
+
+ /**
+ * Get the mapping from logical slots to physical slots.
+ */
+ int[] getSlotsMapping();
}
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index f901c0e29f9b..77b797956cf5 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -128,32 +128,108 @@ public interface RILConstants {
int OEM_ERROR_25 = 525;
/* NETWORK_MODE_* See ril.h RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE */
- int NETWORK_MODE_WCDMA_PREF = 0; /* GSM/WCDMA (WCDMA preferred) */
- int NETWORK_MODE_GSM_ONLY = 1; /* GSM only */
- int NETWORK_MODE_WCDMA_ONLY = 2; /* WCDMA only */
- int NETWORK_MODE_GSM_UMTS = 3; /* GSM/WCDMA (auto mode, according to PRL)
- AVAILABLE Application Settings menu*/
- int NETWORK_MODE_CDMA = 4; /* CDMA and EvDo (auto mode, according to PRL)
- AVAILABLE Application Settings menu*/
- int NETWORK_MODE_CDMA_NO_EVDO = 5; /* CDMA only */
- int NETWORK_MODE_EVDO_NO_CDMA = 6; /* EvDo only */
- int NETWORK_MODE_GLOBAL = 7; /* GSM/WCDMA, CDMA, and EvDo (auto mode, according to PRL)
- AVAILABLE Application Settings menu*/
- int NETWORK_MODE_LTE_CDMA_EVDO = 8; /* LTE, CDMA and EvDo */
- int NETWORK_MODE_LTE_GSM_WCDMA = 9; /* LTE, GSM/WCDMA */
- int NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA = 10; /* LTE, CDMA, EvDo, GSM/WCDMA */
- int NETWORK_MODE_LTE_ONLY = 11; /* LTE Only mode. */
- int NETWORK_MODE_LTE_WCDMA = 12; /* LTE/WCDMA */
- int NETWORK_MODE_TDSCDMA_ONLY = 13; /* TD-SCDMA only */
- int NETWORK_MODE_TDSCDMA_WCDMA = 14; /* TD-SCDMA and WCDMA */
- int NETWORK_MODE_LTE_TDSCDMA = 15; /* TD-SCDMA and LTE */
- int NETWORK_MODE_TDSCDMA_GSM = 16; /* TD-SCDMA and GSM */
- int NETWORK_MODE_LTE_TDSCDMA_GSM = 17; /* TD-SCDMA,GSM and LTE */
- int NETWORK_MODE_TDSCDMA_GSM_WCDMA = 18; /* TD-SCDMA, GSM/WCDMA */
- int NETWORK_MODE_LTE_TDSCDMA_WCDMA = 19; /* TD-SCDMA, WCDMA and LTE */
- int NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA = 20; /* TD-SCDMA, GSM/WCDMA and LTE */
- int NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = 21; /*TD-SCDMA,EvDo,CDMA,GSM/WCDMA*/
- int NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = 22; /* TD-SCDMA/LTE/GSM/WCDMA, CDMA, and EvDo */
+ /** GSM, WCDMA (WCDMA preferred) */
+ int NETWORK_MODE_WCDMA_PREF = 0;
+
+ /** GSM only */
+ int NETWORK_MODE_GSM_ONLY = 1;
+
+ /** WCDMA only */
+ int NETWORK_MODE_WCDMA_ONLY = 2;
+
+ /** GSM, WCDMA (auto mode, according to PRL) */
+ int NETWORK_MODE_GSM_UMTS = 3;
+
+ /** CDMA and EvDo (auto mode, according to PRL) */
+ int NETWORK_MODE_CDMA = 4;
+
+ /** CDMA only */
+ int NETWORK_MODE_CDMA_NO_EVDO = 5;
+
+ /** EvDo only */
+ int NETWORK_MODE_EVDO_NO_CDMA = 6;
+
+ /** GSM, WCDMA, CDMA, and EvDo (auto mode, according to PRL) */
+ int NETWORK_MODE_GLOBAL = 7;
+
+ /** LTE, CDMA and EvDo */
+ int NETWORK_MODE_LTE_CDMA_EVDO = 8;
+
+ /** LTE, GSM and WCDMA */
+ int NETWORK_MODE_LTE_GSM_WCDMA = 9;
+
+ /** LTE, CDMA, EvDo, GSM, and WCDMA */
+ int NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA = 10;
+
+ /** LTE only mode. */
+ int NETWORK_MODE_LTE_ONLY = 11;
+
+ /** LTE and WCDMA */
+ int NETWORK_MODE_LTE_WCDMA = 12;
+
+ /** TD-SCDMA only */
+ int NETWORK_MODE_TDSCDMA_ONLY = 13;
+
+ /** TD-SCDMA and WCDMA */
+ int NETWORK_MODE_TDSCDMA_WCDMA = 14;
+
+ /** LTE and TD-SCDMA*/
+ int NETWORK_MODE_LTE_TDSCDMA = 15;
+
+ /** TD-SCDMA and GSM */
+ int NETWORK_MODE_TDSCDMA_GSM = 16;
+
+ /** TD-SCDMA, GSM and LTE */
+ int NETWORK_MODE_LTE_TDSCDMA_GSM = 17;
+
+ /** TD-SCDMA, GSM and WCDMA */
+ int NETWORK_MODE_TDSCDMA_GSM_WCDMA = 18;
+
+ /** LTE, TD-SCDMA and WCDMA */
+ int NETWORK_MODE_LTE_TDSCDMA_WCDMA = 19;
+
+ /** LTE, TD-SCDMA, GSM, and WCDMA */
+ int NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA = 20;
+
+ /** TD-SCDMA, CDMA, EVDO, GSM and WCDMA */
+ int NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = 21;
+
+ /** LTE, TDCSDMA, CDMA, EVDO, GSM and WCDMA */
+ int NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = 22;
+
+ /** NR 5G only mode */
+ int NETWORK_MODE_NR_ONLY = 23;
+
+ /** NR 5G, LTE */
+ int NETWORK_MODE_NR_LTE = 24;
+
+ /** NR 5G, LTE, CDMA and EvDo */
+ int NETWORK_MODE_NR_LTE_CDMA_EVDO = 25;
+
+ /** NR 5G, LTE, GSM and WCDMA */
+ int NETWORK_MODE_NR_LTE_GSM_WCDMA = 26;
+
+ /** NR 5G, LTE, CDMA, EvDo, GSM and WCDMA */
+ int NETWORK_MODE_NR_LTE_CDMA_EVDO_GSM_WCDMA = 27;
+
+ /** NR 5G, LTE and WCDMA */
+ int NETWORK_MODE_NR_LTE_WCDMA = 28;
+
+ /** NR 5G, LTE and TDSCDMA */
+ int NETWORK_MODE_NR_LTE_TDSCDMA = 29;
+
+ /** NR 5G, LTE, TD-SCDMA and GSM */
+ int NETWORK_MODE_NR_LTE_TDSCDMA_GSM = 30;
+
+ /** NR 5G, LTE, TD-SCDMA, WCDMA */
+ int NETWORK_MODE_NR_LTE_TDSCDMA_WCDMA = 31;
+
+ /** NR 5G, LTE, TD-SCDMA, GSM and WCDMA */
+ int NETWORK_MODE_NR_LTE_TDSCDMA_GSM_WCDMA = 32;
+
+ /** NR 5G, LTE, TD-SCDMA, CDMA, EVDO, GSM and WCDMA */
+ int NETWORK_MODE_NR_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = 33;
+
int PREFERRED_NETWORK_MODE = Integer.parseInt(TelephonyManager.getTelephonyProperty(0,
"ro.telephony.default_network", Integer.toString(NETWORK_MODE_WCDMA_PREF)));
@@ -404,6 +480,7 @@ public interface RILConstants {
int RIL_REQUEST_SET_PREFERRED_DATA_MODEM = 204;
int RIL_REQUEST_EMERGENCY_DIAL = 205;
int RIL_REQUEST_GET_PHONE_CAPABILITY = 206;
+ int RIL_REQUEST_SWITCH_DUAL_SIM_CONFIG = 207;
/* Responses begin */
int RIL_RESPONSE_ACKNOWLEDGEMENT = 800;
diff --git a/telephony/java/com/android/internal/telephony/SmsMessageBase.java b/telephony/java/com/android/internal/telephony/SmsMessageBase.java
index 190eac4d8c02..ffdc4b676f90 100644
--- a/telephony/java/com/android/internal/telephony/SmsMessageBase.java
+++ b/telephony/java/com/android/internal/telephony/SmsMessageBase.java
@@ -41,6 +41,9 @@ public abstract class SmsMessageBase {
@UnsupportedAppUsage
protected SmsAddress mOriginatingAddress;
+ /** {@hide} The address of the receiver */
+ protected SmsAddress mRecipientAddress;
+
/** {@hide} The message body as a string. May be null if the message isn't text */
@UnsupportedAppUsage
protected String mMessageBody;
@@ -457,4 +460,17 @@ public abstract class SmsMessageBase {
return ted;
}
+
+ /**
+ * {@hide}
+ * Returns the receiver address of this SMS message in String
+ * form or null if unavailable
+ */
+ public String getRecipientAddress() {
+ if (mRecipientAddress == null) {
+ return null;
+ }
+
+ return mRecipientAddress.getAddressString();
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/TelephonyProperties.java b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
index 6567ea764b50..603c4c2870d7 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyProperties.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyProperties.java
@@ -194,6 +194,13 @@ public interface TelephonyProperties
*/
static final String PROPERTY_MULTI_SIM_CONFIG = "persist.radio.multisim.config";
+ /**
+ * Property to indicate if reboot is required when changing modems configurations
+ * Type: String(true, false) default is false; most devices don't need reboot
+ */
+ String PROPERTY_REBOOT_REQUIRED_ON_MODEM_CHANGE =
+ "persist.radio.reboot_on_modem_change";
+
/**
* Property to store default subscription.
*/
diff --git a/telephony/java/com/android/internal/telephony/cdma/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/BearerData.java
index a4cd56b9e3e2..694cc69c2b3f 100644
--- a/telephony/java/com/android/internal/telephony/cdma/BearerData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/BearerData.java
@@ -596,6 +596,45 @@ public final class BearerData {
System.arraycopy(payload, 0, uData.payload, udhBytes, payload.length);
}
+ private static void encode7bitAsciiEms(UserData uData, byte[] udhData, boolean force)
+ throws CodingException
+ {
+ try {
+ Rlog.d(LOG_TAG, "encode7bitAsciiEms");
+ int udhBytes = udhData.length + 1; // Add length octet.
+ int udhSeptets = ((udhBytes * 8) + 6) / 7;
+ int paddingBits = (udhSeptets * 7) - (udhBytes * 8);
+ String msg = uData.payloadStr;
+ byte[] payload ;
+ int msgLen = msg.length();
+ BitwiseOutputStream outStream = new BitwiseOutputStream(msgLen +
+ (paddingBits > 0 ? 1 : 0));
+ outStream.write(paddingBits, 0);
+ for (int i = 0; i < msgLen; i++) {
+ int charCode = UserData.charToAscii.get(msg.charAt(i), -1);
+ if (charCode == -1) {
+ if (force) {
+ outStream.write(7, UserData.UNENCODABLE_7_BIT_CHAR);
+ } else {
+ throw new CodingException("cannot ASCII encode (" + msg.charAt(i) + ")");
+ }
+ } else {
+ outStream.write(7, charCode);
+ }
+ }
+ payload = outStream.toByteArray();
+ uData.msgEncoding = UserData.ENCODING_7BIT_ASCII;
+ uData.msgEncodingSet = true;
+ uData.numFields = udhSeptets + uData.payloadStr.length();
+ uData.payload = new byte[udhBytes + payload.length];
+ uData.payload[0] = (byte)udhData.length;
+ System.arraycopy(udhData, 0, uData.payload, 1, udhData.length);
+ System.arraycopy(payload, 0, uData.payload, udhBytes, payload.length);
+ } catch (BitwiseOutputStream.AccessException ex) {
+ throw new CodingException("7bit ASCII encode failed: " + ex);
+ }
+ }
+
private static void encodeEmsUserDataPayload(UserData uData)
throws CodingException
{
@@ -605,6 +644,8 @@ public final class BearerData {
encode7bitEms(uData, headerData, true);
} else if (uData.msgEncoding == UserData.ENCODING_UNICODE_16) {
encode16bitEms(uData, headerData);
+ } else if (uData.msgEncoding == UserData.ENCODING_7BIT_ASCII) {
+ encode7bitAsciiEms(uData, headerData, true);
} else {
throw new CodingException("unsupported EMS user data encoding (" +
uData.msgEncoding + ")");
@@ -1056,15 +1097,18 @@ public final class BearerData {
throws CodingException
{
try {
- offset *= 8;
+ int offsetBits = offset * 8;
+ int offsetSeptets = (offsetBits + 6) / 7;
+ numFields -= offsetSeptets;
+
StringBuffer strBuf = new StringBuffer(numFields);
BitwiseInputStream inStream = new BitwiseInputStream(data);
- int wantedBits = (offset * 8) + (numFields * 7);
+ int wantedBits = (offsetSeptets * 7) + (numFields * 7);
if (inStream.available() < wantedBits) {
throw new CodingException("insufficient data (wanted " + wantedBits +
" bits, but only have " + inStream.available() + ")");
}
- inStream.skip(offset);
+ inStream.skip(offsetSeptets * 7);
for (int i = 0; i < numFields; i++) {
int charCode = inStream.read(7);
if ((charCode >= UserData.ASCII_MAP_BASE_INDEX) &&
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index 964a31304db5..a31fa0b6a725 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -21,7 +21,6 @@ import android.os.SystemProperties;
import android.telephony.PhoneNumberUtils;
import android.telephony.SmsCbLocation;
import android.telephony.SmsCbMessage;
-import android.telephony.TelephonyManager;
import android.telephony.cdma.CdmaSmsCbProgramData;
import android.telephony.Rlog;
import android.util.Log;
@@ -602,18 +601,24 @@ public class SmsMessage extends SmsMessageBase {
} else if (addr.numberMode == CdmaSmsAddress.NUMBER_MODE_DATA_NETWORK) {
if (numberType == 2)
- Rlog.e(LOG_TAG, "TODO: Originating Addr is email id");
+ Rlog.e(LOG_TAG, "TODO: Addr is email id");
else
Rlog.e(LOG_TAG,
- "TODO: Originating Addr is data network address");
+ "TODO: Addr is data network address");
} else {
- Rlog.e(LOG_TAG, "Originating Addr is of incorrect type");
+ Rlog.e(LOG_TAG, "Addr is of incorrect type");
}
} else {
Rlog.e(LOG_TAG, "Incorrect Digit mode");
}
addr.origBytes = data;
- Rlog.i(LOG_TAG, "Originating Addr=" + addr.toString());
+ Rlog.pii(LOG_TAG, "Addr=" + addr.toString());
+ mOriginatingAddress = addr;
+ if (parameterId == DESTINATION_ADDRESS) {
+ // Original address awlays indicates one sender's address for 3GPP2
+ // Here add recipient address support along with 3GPP
+ mRecipientAddress = addr;
+ }
break;
case ORIGINATING_SUB_ADDRESS:
case DESTINATION_SUB_ADDRESS:
@@ -668,7 +673,7 @@ public class SmsMessage extends SmsMessageBase {
}
/**
- * Parses a SMS message from its BearerData stream. (mobile-terminated only)
+ * Parses a SMS message from its BearerData stream.
*/
public void parseSms() {
// Message Waiting Info Record defined in 3GPP2 C.S-0005, 3.7.5.6
@@ -698,16 +703,15 @@ public class SmsMessage extends SmsMessageBase {
}
if (mOriginatingAddress != null) {
- mOriginatingAddress.address = new String(mOriginatingAddress.origBytes);
- if (mOriginatingAddress.ton == CdmaSmsAddress.TON_INTERNATIONAL_OR_IP) {
- if (mOriginatingAddress.address.charAt(0) != '+') {
- mOriginatingAddress.address = "+" + mOriginatingAddress.address;
- }
- }
+ decodeSmsDisplayAddress(mOriginatingAddress);
if (VDBG) Rlog.v(LOG_TAG, "SMS originating address: "
+ mOriginatingAddress.address);
}
+ if (mRecipientAddress != null) {
+ decodeSmsDisplayAddress(mRecipientAddress);
+ }
+
if (mBearerData.msgCenterTimeStamp != null) {
mScTimeMillis = mBearerData.msgCenterTimeStamp.toMillis(true);
}
@@ -732,7 +736,8 @@ public class SmsMessage extends SmsMessageBase {
status = mBearerData.errorClass << 8;
status |= mBearerData.messageStatus;
}
- } else if (mBearerData.messageType != BearerData.MESSAGE_TYPE_DELIVER) {
+ } else if (mBearerData.messageType != BearerData.MESSAGE_TYPE_DELIVER
+ && mBearerData.messageType != BearerData.MESSAGE_TYPE_SUBMIT) {
throw new RuntimeException("Unsupported message type: " + mBearerData.messageType);
}
@@ -744,10 +749,22 @@ public class SmsMessage extends SmsMessageBase {
}
}
+ private void decodeSmsDisplayAddress(SmsAddress addr) {
+ addr.address = new String(addr.origBytes);
+ if (addr.ton == CdmaSmsAddress.TON_INTERNATIONAL_OR_IP) {
+ if (addr.address.charAt(0) != '+') {
+ addr.address = "+" + addr.address;
+ }
+ }
+ Rlog.pii(LOG_TAG, " decodeSmsDisplayAddress = " + addr.address);
+ }
+
/**
* Parses a broadcast SMS, possibly containing a CMAS alert.
+ *
+ * @param plmn the PLMN for a broadcast SMS
*/
- public SmsCbMessage parseBroadcastSms() {
+ public SmsCbMessage parseBroadcastSms(String plmn) {
BearerData bData = BearerData.decode(mEnvelope.bearerData, mEnvelope.serviceCategory);
if (bData == null) {
Rlog.w(LOG_TAG, "BearerData.decode() returned null");
@@ -758,7 +775,6 @@ public class SmsMessage extends SmsMessageBase {
Rlog.d(LOG_TAG, "MT raw BearerData = " + HexDump.toHexString(mEnvelope.bearerData));
}
- String plmn = TelephonyManager.getDefault().getNetworkOperator();
SmsCbLocation location = new SmsCbLocation(plmn);
return new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP2,
@@ -858,14 +874,15 @@ public class SmsMessage extends SmsMessageBase {
bearerData.userData = userData;
byte[] encodedBearerData = BearerData.encode(bearerData);
+ if (encodedBearerData == null) return null;
if (Rlog.isLoggable(LOGGABLE_TAG, Log.VERBOSE)) {
Rlog.d(LOG_TAG, "MO (encoded) BearerData = " + bearerData);
Rlog.d(LOG_TAG, "MO raw BearerData = '" + HexDump.toHexString(encodedBearerData) + "'");
}
- if (encodedBearerData == null) return null;
- int teleservice = bearerData.hasUserDataHeader ?
- SmsEnvelope.TELESERVICE_WEMT : SmsEnvelope.TELESERVICE_WMT;
+ int teleservice = (bearerData.hasUserDataHeader
+ && userData.msgEncoding != UserData.ENCODING_7BIT_ASCII)
+ ? SmsEnvelope.TELESERVICE_WEMT : SmsEnvelope.TELESERVICE_WMT;
SmsEnvelope envelope = new SmsEnvelope();
envelope.messageType = SmsEnvelope.MESSAGE_TYPE_POINT_TO_POINT;
diff --git a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
index 14a36c8c840d..20169152539e 100644
--- a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
@@ -31,7 +31,7 @@ interface IEuiccController {
String callingPackage, in PendingIntent callbackIntent);
oneway void getDefaultDownloadableSubscriptionList(int cardId,
String callingPackage, in PendingIntent callbackIntent);
- String getEid(int cardId);
+ String getEid(int cardId, String callingPackage);
int getOtaStatus(int cardId);
oneway void downloadSubscription(int cardId, in DownloadableSubscription subscription,
boolean switchAfterDownload, String callingPackage, in Bundle resolvedBundle,
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index 4f5bfa919135..19465a44e4e8 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -71,9 +71,6 @@ public class SmsMessage extends SmsMessageBase {
// e.g. 23.040 9.2.2.1
private boolean mReplyPathPresent = false;
- /** The address of the receiver. */
- private GsmSmsAddress mRecipientAddress;
-
/**
* TP-Status - status of a previously submitted SMS.
* This field applies to SMS-STATUS-REPORT messages. 0 indicates success;
@@ -384,16 +381,22 @@ public class SmsMessage extends SmsMessageBase {
}
}
} catch (EncodeException ex) {
- // Encoding to the 7-bit alphabet failed. Let's see if we can
- // send it as a UCS-2 encoded message
- try {
- userData = encodeUCS2(message, header);
- encoding = ENCODING_16BIT;
- } catch(UnsupportedEncodingException uex) {
- Rlog.e(LOG_TAG,
- "Implausible UnsupportedEncodingException ",
- uex);
+ if (ex.getError() == EncodeException.ERROR_EXCEED_SIZE) {
+ Rlog.e(LOG_TAG, "Exceed size limitation EncodeException", ex);
return null;
+ } else {
+ // Encoding to the 7-bit alphabet failed. Let's see if we can
+ // send it as a UCS-2 encoded message
+ try {
+ userData = encodeUCS2(message, header);
+ encoding = ENCODING_16BIT;
+ } catch (EncodeException ex1) {
+ Rlog.e(LOG_TAG, "Exceed size limitation EncodeException", ex1);
+ return null;
+ } catch (UnsupportedEncodingException uex) {
+ Rlog.e(LOG_TAG, "Implausible UnsupportedEncodingException ", uex);
+ return null;
+ }
}
}
@@ -438,9 +441,10 @@ public class SmsMessage extends SmsMessageBase {
*
* @return encoded message as UCS2
* @throws UnsupportedEncodingException
+ * @throws EncodeException if String is too large to encode
*/
private static byte[] encodeUCS2(String message, byte[] header)
- throws UnsupportedEncodingException {
+ throws UnsupportedEncodingException, EncodeException {
byte[] userData, textPart;
textPart = message.getBytes("utf-16be");
@@ -455,6 +459,10 @@ public class SmsMessage extends SmsMessageBase {
else {
userData = textPart;
}
+ if (userData.length > 255) {
+ throw new EncodeException(
+ "Payload cannot exceed 255 bytes", EncodeException.ERROR_EXCEED_SIZE);
+ }
byte[] ret = new byte[userData.length+1];
ret[0] = (byte) (userData.length & 0xff );
System.arraycopy(userData, 0, ret, 1, userData.length);
diff --git a/test-base/api/TEST_MAPPING b/test-base/api/TEST_MAPPING
new file mode 100644
index 000000000000..3535954bc019
--- /dev/null
+++ b/test-base/api/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsAndroidTestBase27ApiSignatureTestCases"
+ }
+ ]
+}
diff --git a/test-mock/api/TEST_MAPPING b/test-mock/api/TEST_MAPPING
new file mode 100644
index 000000000000..d1bd9afaf287
--- /dev/null
+++ b/test-mock/api/TEST_MAPPING
@@ -0,0 +1,10 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsAndroidTestMockCurrentApiSignatureTestCases"
+ },
+ {
+ "name": "CtsCurrentApiSignatureTestCases"
+ }
+ ]
+}
diff --git a/test-runner/Android.mk b/test-runner/Android.mk
deleted file mode 100644
index 18bde8517351..000000000000
--- a/test-runner/Android.mk
+++ /dev/null
@@ -1,20 +0,0 @@
-#
-# Copyright (C) 2008 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)
-
-# additionally, build unit tests in a separate .apk
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/test-runner/api/TEST_MAPPING b/test-runner/api/TEST_MAPPING
new file mode 100644
index 000000000000..76ade3c1e647
--- /dev/null
+++ b/test-runner/api/TEST_MAPPING
@@ -0,0 +1,10 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsAndroidTestRunnerCurrentApiSignatureTestCases"
+ },
+ {
+ "name": "CtsCurrentApiSignatureTestCases"
+ }
+ ]
+}
diff --git a/test-runner/tests/Android.bp b/test-runner/tests/Android.bp
new file mode 100644
index 000000000000..03c73986118d
--- /dev/null
+++ b/test-runner/tests/Android.bp
@@ -0,0 +1,40 @@
+// Copyright 2010, 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.
+
+android_test {
+ name: "FrameworkTestRunnerTests",
+
+ // We only want this apk build for tests.
+ //
+ // Run the tests using the following commands:
+ // adb install -r ${ANDROID_PRODUCT_OUT}/data/app/FrameworkTestRunnerTests/FrameworkTestRunnerTests.apk
+ // adb shell am instrument \
+ // -e notAnnotation android.test.suitebuilder.examples.error.RunAsPartOfSeparateTest \
+ // -w com.android.frameworks.testrunner.tests/android.test.InstrumentationTestRunner \
+ //
+
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ "android.test.mock",
+ ],
+ static_libs: ["junit"],
+
+ // Include all test java files.
+ srcs: ["src/**/*.java"],
+
+ // Because of android.test.mock.
+ platform_apis: true,
+
+}
diff --git a/test-runner/tests/Android.mk b/test-runner/tests/Android.mk
deleted file mode 100644
index f97d1c986b1c..000000000000
--- a/test-runner/tests/Android.mk
+++ /dev/null
@@ -1,39 +0,0 @@
-# Copyright 2010, 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)
-include $(CLEAR_VARS)
-
-# We only want this apk build for tests.
-#
-# Run the tests using the following commands:
-# adb install -r ${ANDROID_PRODUCT_OUT}/data/app/FrameworkTestRunnerTests/FrameworkTestRunnerTests.apk
-# adb shell am instrument \
- -e notAnnotation android.test.suitebuilder.examples.error.RunAsPartOfSeparateTest \
- -w com.android.frameworks.testrunner.tests/android.test.InstrumentationTestRunner
-#
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base android.test.mock
-LOCAL_STATIC_JAVA_LIBRARIES := junit
-
-# Include all test java files.
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := FrameworkTestRunnerTests
-# Because of android.test.mock.
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-include $(BUILD_PACKAGE)
-
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadIconChangedEventTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadIconChangedEventTest.java
new file mode 100644
index 000000000000..915a260f5c79
--- /dev/null
+++ b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadIconChangedEventTest.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2019 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.tests.ims;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.net.Uri;
+import android.os.Parcel;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.ims.RcsGroupThread;
+import android.telephony.ims.RcsGroupThreadIconChangedEvent;
+import android.telephony.ims.RcsParticipant;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class RcsGroupThreadIconChangedEventTest {
+
+ @Test
+ public void testCanUnparcel() {
+ RcsGroupThread rcsGroupThread = new RcsGroupThread(1);
+ RcsParticipant rcsParticipant = new RcsParticipant(2);
+ Uri newIconUri = Uri.parse("content://new_icon");
+
+ RcsGroupThreadIconChangedEvent iconChangedEvent =
+ new RcsGroupThreadIconChangedEvent(1234567890, rcsGroupThread, rcsParticipant,
+ newIconUri);
+
+ Parcel parcel = Parcel.obtain();
+ iconChangedEvent.writeToParcel(parcel, iconChangedEvent.describeContents());
+
+ parcel.setDataPosition(0);
+
+ iconChangedEvent = RcsGroupThreadIconChangedEvent.CREATOR.createFromParcel(parcel);
+
+ assertThat(iconChangedEvent.getNewIcon()).isEqualTo(newIconUri);
+ assertThat(iconChangedEvent.getRcsGroupThread().getThreadId()).isEqualTo(1);
+ assertThat(iconChangedEvent.getOriginatingParticipant().getId()).isEqualTo(2);
+ assertThat(iconChangedEvent.getTimestamp()).isEqualTo(1234567890);
+ }
+}
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadNameChangedEventTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadNameChangedEventTest.java
new file mode 100644
index 000000000000..1384c016daa8
--- /dev/null
+++ b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadNameChangedEventTest.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2019 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.tests.ims;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.ims.RcsGroupThread;
+import android.telephony.ims.RcsGroupThreadNameChangedEvent;
+import android.telephony.ims.RcsParticipant;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class RcsGroupThreadNameChangedEventTest {
+ @Test
+ public void testCanUnparcel() {
+ String newName = "new name";
+
+ RcsGroupThread rcsGroupThread = new RcsGroupThread(1);
+ RcsParticipant rcsParticipant = new RcsParticipant(2);
+
+ RcsGroupThreadNameChangedEvent nameChangedEvent =
+ new RcsGroupThreadNameChangedEvent(1234567890, rcsGroupThread, rcsParticipant,
+ newName);
+
+ Parcel parcel = Parcel.obtain();
+ nameChangedEvent.writeToParcel(parcel, nameChangedEvent.describeContents());
+
+ parcel.setDataPosition(0);
+
+ nameChangedEvent = RcsGroupThreadNameChangedEvent.CREATOR.createFromParcel(
+ parcel);
+
+ assertThat(nameChangedEvent.getNewName()).isEqualTo(newName);
+ assertThat(nameChangedEvent.getRcsGroupThread().getThreadId()).isEqualTo(1);
+ assertThat(nameChangedEvent.getOriginatingParticipant().getId()).isEqualTo(2);
+ assertThat(nameChangedEvent.getTimestamp()).isEqualTo(1234567890);
+ }
+}
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantJoinedEventTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantJoinedEventTest.java
new file mode 100644
index 000000000000..d0af7db90627
--- /dev/null
+++ b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantJoinedEventTest.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2019 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.tests.ims;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.ims.RcsGroupThread;
+import android.telephony.ims.RcsGroupThreadParticipantJoinedEvent;
+import android.telephony.ims.RcsParticipant;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class RcsGroupThreadParticipantJoinedEventTest {
+
+ @Test
+ public void testCanUnparcel() {
+ RcsGroupThread rcsGroupThread = new RcsGroupThread(1);
+ RcsParticipant rcsParticipant = new RcsParticipant(2);
+
+ RcsGroupThreadParticipantJoinedEvent participantJoinedEvent =
+ new RcsGroupThreadParticipantJoinedEvent(1234567890, rcsGroupThread, rcsParticipant,
+ rcsParticipant);
+
+ Parcel parcel = Parcel.obtain();
+ participantJoinedEvent.writeToParcel(parcel, participantJoinedEvent.describeContents());
+
+ parcel.setDataPosition(0);
+
+ participantJoinedEvent = RcsGroupThreadParticipantJoinedEvent.CREATOR.createFromParcel(
+ parcel);
+
+ assertThat(participantJoinedEvent.getJoinedParticipant().getId()).isEqualTo(2);
+ assertThat(participantJoinedEvent.getRcsGroupThread().getThreadId()).isEqualTo(1);
+ assertThat(participantJoinedEvent.getOriginatingParticipant().getId()).isEqualTo(2);
+ assertThat(participantJoinedEvent.getTimestamp()).isEqualTo(1234567890);
+ }
+}
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantLeftEventTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantLeftEventTest.java
new file mode 100644
index 000000000000..7ba5fa653258
--- /dev/null
+++ b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantLeftEventTest.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2019 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.tests.ims;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.ims.RcsGroupThread;
+import android.telephony.ims.RcsGroupThreadParticipantLeftEvent;
+import android.telephony.ims.RcsParticipant;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class RcsGroupThreadParticipantLeftEventTest {
+ @Test
+ public void testCanUnparcel() {
+ RcsGroupThread rcsGroupThread = new RcsGroupThread(1);
+ RcsParticipant rcsParticipant = new RcsParticipant(2);
+
+ RcsGroupThreadParticipantLeftEvent participantLeftEvent =
+ new RcsGroupThreadParticipantLeftEvent(1234567890, rcsGroupThread, rcsParticipant,
+ rcsParticipant);
+
+ Parcel parcel = Parcel.obtain();
+ participantLeftEvent.writeToParcel(parcel, participantLeftEvent.describeContents());
+
+ parcel.setDataPosition(0);
+
+ // create from parcel
+ parcel.setDataPosition(0);
+ participantLeftEvent = RcsGroupThreadParticipantLeftEvent.CREATOR.createFromParcel(
+ parcel);
+ assertThat(participantLeftEvent.getRcsGroupThread().getThreadId()).isEqualTo(1);
+ assertThat(participantLeftEvent.getLeavingParticipantId().getId()).isEqualTo(2);
+ assertThat(participantLeftEvent.getTimestamp()).isEqualTo(1234567890);
+ }
+}
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsMessageStoreTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsMessageStoreTest.java
deleted file mode 100644
index 44277edcdb8c..000000000000
--- a/tests/RcsTests/src/com/android/tests/ims/RcsMessageStoreTest.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2018 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.tests.ims;
-
-import android.support.test.runner.AndroidJUnit4;
-import android.telephony.ims.RcsMessageStore;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class RcsMessageStoreTest {
- //TODO(sahinc): Add meaningful tests once we have more of the implementation in place
- @Test
- public void testDeleteThreadDoesntCrash() {
- RcsMessageStore mRcsMessageStore = new RcsMessageStore();
- mRcsMessageStore.deleteThread(0);
- }
-}
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsParticipantAliasChangedEventTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsParticipantAliasChangedEventTest.java
new file mode 100644
index 000000000000..3e2bbbf8256c
--- /dev/null
+++ b/tests/RcsTests/src/com/android/tests/ims/RcsParticipantAliasChangedEventTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2019 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.tests.ims;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.ims.RcsParticipant;
+import android.telephony.ims.RcsParticipantAliasChangedEvent;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class RcsParticipantAliasChangedEventTest {
+ private static final String OLD_ALIAS = "old alias";
+ private static final String NEW_ALIAS = "new alias";
+ private RcsParticipant mParticipant;
+
+ @Before
+ public void setUp() {
+ mParticipant = new RcsParticipant(3);
+ }
+
+ @Test
+ public void testCanUnparcel() {
+ RcsParticipantAliasChangedEvent aliasChangedEvent =
+ new RcsParticipantAliasChangedEvent(1234567890, mParticipant, NEW_ALIAS);
+
+ Parcel parcel = Parcel.obtain();
+ aliasChangedEvent.writeToParcel(parcel, aliasChangedEvent.describeContents());
+
+ parcel.setDataPosition(0);
+
+ aliasChangedEvent = RcsParticipantAliasChangedEvent.CREATOR.createFromParcel(
+ parcel);
+
+ assertThat(aliasChangedEvent.getParticipantId().getId()).isEqualTo(3);
+ assertThat(aliasChangedEvent.getNewAlias()).isEqualTo(NEW_ALIAS);
+ assertThat(aliasChangedEvent.getTimestamp()).isEqualTo(1234567890);
+ }
+}
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsParticipantQueryParamsTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsParticipantQueryParamsTest.java
new file mode 100644
index 000000000000..6361a393187e
--- /dev/null
+++ b/tests/RcsTests/src/com/android/tests/ims/RcsParticipantQueryParamsTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2018 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.tests.ims;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.ims.RcsParticipantQueryParams;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class RcsParticipantQueryParamsTest {
+
+ @Test
+ public void testCanUnparcel() {
+ RcsParticipantQueryParams rcsParticipantQueryParams =
+ new RcsParticipantQueryParams.Builder()
+ .setAliasLike("%alias_")
+ .setCanonicalAddressLike("_canonical%")
+ .setSortProperty(RcsParticipantQueryParams.SORT_BY_CANONICAL_ADDRESS)
+ .setSortDirection(true)
+ .setResultLimit(432)
+ .build();
+
+
+ Parcel parcel = Parcel.obtain();
+ rcsParticipantQueryParams.writeToParcel(parcel,
+ rcsParticipantQueryParams.describeContents());
+
+ parcel.setDataPosition(0);
+ rcsParticipantQueryParams = RcsParticipantQueryParams.CREATOR.createFromParcel(
+ parcel);
+
+ assertThat(rcsParticipantQueryParams.getAliasLike()).isEqualTo("%alias_");
+ assertThat(rcsParticipantQueryParams.getCanonicalAddressLike()).contains("_canonical%");
+ assertThat(rcsParticipantQueryParams.getLimit()).isEqualTo(432);
+ assertThat(rcsParticipantQueryParams.getSortingProperty()).isEqualTo(
+ RcsParticipantQueryParams.SORT_BY_CANONICAL_ADDRESS);
+ assertThat(rcsParticipantQueryParams.getSortDirection()).isTrue();
+ }
+}
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsParticipantTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsParticipantTest.java
deleted file mode 100644
index c402dbffc84b..000000000000
--- a/tests/RcsTests/src/com/android/tests/ims/RcsParticipantTest.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2018 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.tests.ims;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.os.Bundle;
-import android.support.test.runner.AndroidJUnit4;
-import android.telephony.ims.RcsParticipant;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class RcsParticipantTest {
- private static final int ID = 123;
- private static final String ALIAS = "alias";
- private static final String CANONICAL_ADDRESS = "+1234567890";
-
- @Test
- public void testCanUnparcel() {
- RcsParticipant rcsParticipant = new RcsParticipant(ID, CANONICAL_ADDRESS);
- rcsParticipant.setAlias(ALIAS);
-
- Bundle bundle = new Bundle();
- bundle.putParcelable("Some key", rcsParticipant);
- rcsParticipant = bundle.getParcelable("Some key");
-
- assertThat(rcsParticipant.getId()).isEqualTo(ID);
- assertThat(rcsParticipant.getAlias()).isEqualTo(ALIAS);
- assertThat(rcsParticipant.getCanonicalAddress()).isEqualTo(CANONICAL_ADDRESS);
- }
-}
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParametersTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParametersTest.java
deleted file mode 100644
index a890a389bdfc..000000000000
--- a/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParametersTest.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2018 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.tests.ims;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.os.Bundle;
-import android.support.test.runner.AndroidJUnit4;
-import android.telephony.ims.RcsParticipant;
-import android.telephony.ims.RcsThreadQueryParameters;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-
-@RunWith(AndroidJUnit4.class)
-public class RcsThreadQueryParametersTest {
- private RcsThreadQueryParameters mRcsThreadQueryParameters;
- @Mock RcsParticipant mMockParticipant;
-
- @Test
- public void testUnparceling() {
- String key = "some key";
- mRcsThreadQueryParameters = RcsThreadQueryParameters.builder()
- .isGroupThread(true)
- .withParticipant(mMockParticipant)
- .limitResultsTo(50)
- .sort(true)
- .build();
-
- Bundle bundle = new Bundle();
- bundle.putParcelable(key, mRcsThreadQueryParameters);
- mRcsThreadQueryParameters = bundle.getParcelable(key);
-
- assertThat(mRcsThreadQueryParameters.isGroupThread()).isTrue();
- assertThat(mRcsThreadQueryParameters.getRcsParticipants()).contains(mMockParticipant);
- assertThat(mRcsThreadQueryParameters.getLimit()).isEqualTo(50);
- assertThat(mRcsThreadQueryParameters.isAscending()).isTrue();
- }
-}
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParamsTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParamsTest.java
new file mode 100644
index 000000000000..beb4f8ad28e2
--- /dev/null
+++ b/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParamsTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2018 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.tests.ims;
+
+import static android.telephony.ims.RcsThreadQueryParams.SORT_BY_TIMESTAMP;
+import static android.telephony.ims.RcsThreadQueryParams.THREAD_TYPE_GROUP;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Parcel;
+import android.support.test.runner.AndroidJUnit4;
+import android.telephony.ims.RcsParticipant;
+import android.telephony.ims.RcsThreadQueryParams;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class RcsThreadQueryParamsTest {
+
+ @Test
+ public void testCanUnparcel() {
+ RcsParticipant rcsParticipant = new RcsParticipant(1);
+ RcsThreadQueryParams rcsThreadQueryParams = new RcsThreadQueryParams.Builder()
+ .setThreadType(THREAD_TYPE_GROUP)
+ .setParticipant(rcsParticipant)
+ .setResultLimit(50)
+ .setSortProperty(SORT_BY_TIMESTAMP)
+ .setSortDirection(true)
+ .build();
+
+ Parcel parcel = Parcel.obtain();
+ rcsThreadQueryParams.writeToParcel(parcel, rcsThreadQueryParams.describeContents());
+
+ parcel.setDataPosition(0);
+ rcsThreadQueryParams = RcsThreadQueryParams.CREATOR.createFromParcel(parcel);
+
+ assertThat(rcsThreadQueryParams.getThreadType()).isEqualTo(THREAD_TYPE_GROUP);
+ assertThat(rcsThreadQueryParams.getRcsParticipantsIds())
+ .contains(rcsParticipant.getId());
+ assertThat(rcsThreadQueryParams.getLimit()).isEqualTo(50);
+ assertThat(rcsThreadQueryParams.getSortingProperty()).isEqualTo(SORT_BY_TIMESTAMP);
+ assertThat(rcsThreadQueryParams.getSortDirection()).isTrue();
+ }
+}
diff --git a/tests/net/java/android/net/LinkPropertiesTest.java b/tests/net/java/android/net/LinkPropertiesTest.java
index 299fbefc78e4..bdde0961909d 100644
--- a/tests/net/java/android/net/LinkPropertiesTest.java
+++ b/tests/net/java/android/net/LinkPropertiesTest.java
@@ -22,18 +22,15 @@ import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-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.os.Parcel;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.system.OsConstants;
import android.util.ArraySet;
+import com.android.internal.util.TestUtils;
+
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -849,18 +846,6 @@ public class LinkPropertiesTest {
assertEquals(new ArraySet<>(expectRemoved), (new ArraySet<>(result.removed)));
}
- private void assertParcelingIsLossless(LinkProperties source) {
- Parcel p = Parcel.obtain();
- source.writeToParcel(p, /* flags */ 0);
- p.setDataPosition(0);
- final byte[] marshalled = p.marshall();
- p = Parcel.obtain();
- p.unmarshall(marshalled, 0, marshalled.length);
- p.setDataPosition(0);
- LinkProperties dest = LinkProperties.CREATOR.createFromParcel(p);
- assertEquals(source, dest);
- }
-
@Test
public void testLinkPropertiesParcelable() throws Exception {
LinkProperties source = new LinkProperties();
@@ -882,12 +867,12 @@ public class LinkPropertiesTest {
source.setNat64Prefix(new IpPrefix("2001:db8:1:2:64:64::/96"));
- assertParcelingIsLossless(source);
+ TestUtils.assertParcelingIsLossless(source, LinkProperties.CREATOR);
}
@Test
public void testParcelUninitialized() throws Exception {
LinkProperties empty = new LinkProperties();
- assertParcelingIsLossless(empty);
+ TestUtils.assertParcelingIsLossless(empty, LinkProperties.CREATOR);
}
}
diff --git a/tests/net/java/android/net/TcpKeepalivePacketDataTest.java b/tests/net/java/android/net/TcpKeepalivePacketDataTest.java
new file mode 100644
index 000000000000..1f2dd275bb7b
--- /dev/null
+++ b/tests/net/java/android/net/TcpKeepalivePacketDataTest.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import android.net.SocketKeepalive.InvalidPacketException;
+import android.net.TcpKeepalivePacketData.TcpSocketInfo;
+
+import com.android.internal.util.TestUtils;
+
+import libcore.net.InetAddressUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.net.InetAddress;
+import java.nio.ByteBuffer;
+
+@RunWith(JUnit4.class)
+public final class TcpKeepalivePacketDataTest {
+
+ @Before
+ public void setUp() {}
+
+ @Test
+ public void testV4TcpKeepalivePacket() {
+ final InetAddress srcAddr = InetAddressUtils.parseNumericAddress("192.168.0.1");
+ final InetAddress dstAddr = InetAddressUtils.parseNumericAddress("192.168.0.10");
+ final int srcPort = 1234;
+ final int dstPort = 4321;
+ final int seq = 0x11111111;
+ final int ack = 0x22222222;
+ final int wnd = 8000;
+ final int wndScale = 2;
+ TcpKeepalivePacketData resultData = null;
+ TcpSocketInfo testInfo = new TcpSocketInfo(
+ srcAddr, srcPort, dstAddr, dstPort, seq, ack, wnd, wndScale);
+ try {
+ resultData = TcpKeepalivePacketData.tcpKeepalivePacket(testInfo);
+ } catch (InvalidPacketException e) {
+ fail("InvalidPacketException: " + e);
+ }
+
+ assertEquals(testInfo.srcAddress, resultData.srcAddress);
+ assertEquals(testInfo.dstAddress, resultData.dstAddress);
+ assertEquals(testInfo.srcPort, resultData.srcPort);
+ assertEquals(testInfo.dstPort, resultData.dstPort);
+ assertEquals(testInfo.seq, resultData.tcpSeq);
+ assertEquals(testInfo.ack, resultData.tcpAck);
+ assertEquals(testInfo.rcvWndScale, resultData.tcpWndScale);
+
+ TestUtils.assertParcelingIsLossless(resultData, TcpKeepalivePacketData.CREATOR);
+
+ final byte[] packet = resultData.getPacket();
+ // IP version and TOS.
+ ByteBuffer buf = ByteBuffer.wrap(packet);
+ assertEquals(buf.getShort(), 0x4500);
+ // Source IP address.
+ byte[] ip = new byte[4];
+ buf = ByteBuffer.wrap(packet, 12, 4);
+ buf.get(ip);
+ assertArrayEquals(ip, srcAddr.getAddress());
+ // Destination IP address.
+ buf = ByteBuffer.wrap(packet, 16, 4);
+ buf.get(ip);
+ assertArrayEquals(ip, dstAddr.getAddress());
+
+ buf = ByteBuffer.wrap(packet, 20, 12);
+ // Source port.
+ assertEquals(buf.getShort(), srcPort);
+ // Destination port.
+ assertEquals(buf.getShort(), dstPort);
+ // Sequence number.
+ assertEquals(buf.getInt(), seq);
+ // Ack.
+ assertEquals(buf.getInt(), ack);
+ // Window size.
+ buf = ByteBuffer.wrap(packet, 34, 2);
+ assertEquals(buf.getShort(), wnd >> wndScale);
+ }
+
+ //TODO: add ipv6 test when ipv6 supported
+
+ @Test
+ public void testParcel() throws Exception {
+ final InetAddress srcAddr = InetAddresses.parseNumericAddress("192.168.0.1");
+ final InetAddress dstAddr = InetAddresses.parseNumericAddress("192.168.0.10");
+ final int srcPort = 1234;
+ final int dstPort = 4321;
+ final int sequence = 0x11111111;
+ final int ack = 0x22222222;
+ final int wnd = 48_000;
+ final int wndScale = 2;
+ TcpKeepalivePacketData testData = null;
+ TcpKeepalivePacketDataParcelable resultData = null;
+ TcpSocketInfo testInfo = new TcpSocketInfo(
+ srcAddr, srcPort, dstAddr, dstPort, sequence, ack, wnd, wndScale);
+ testData = TcpKeepalivePacketData.tcpKeepalivePacket(testInfo);
+ resultData = testData.toStableParcelable();
+ assertArrayEquals(resultData.srcAddress, srcAddr.getAddress());
+ assertArrayEquals(resultData.dstAddress, dstAddr.getAddress());
+ assertEquals(resultData.srcPort, srcPort);
+ assertEquals(resultData.dstPort, dstPort);
+ assertEquals(resultData.seq, sequence);
+ assertEquals(resultData.ack, ack);
+ }
+}
diff --git a/tests/net/java/com/android/internal/util/TestUtils.java b/tests/net/java/com/android/internal/util/TestUtils.java
index 6db01d343756..7e5a1d3ad4d3 100644
--- a/tests/net/java/com/android/internal/util/TestUtils.java
+++ b/tests/net/java/com/android/internal/util/TestUtils.java
@@ -16,12 +16,15 @@
package com.android.internal.util;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
+import android.os.Parcel;
+import android.os.Parcelable;
public final class TestUtils {
private TestUtils() { }
@@ -50,4 +53,18 @@ public final class TestUtils {
fail(handler.toString() + " did not become idle after " + timeoutMs + " ms");
}
}
+
+ // TODO : fetch the creator through reflection or something instead of passing it
+ public static <T extends Parcelable, C extends Parcelable.Creator<T>>
+ void assertParcelingIsLossless(T source, C creator) {
+ Parcel p = Parcel.obtain();
+ source.writeToParcel(p, /* flags */ 0);
+ p.setDataPosition(0);
+ final byte[] marshalled = p.marshall();
+ p = Parcel.obtain();
+ p.unmarshall(marshalled, 0, marshalled.length);
+ p.setDataPosition(0);
+ T dest = creator.createFromParcel(p);
+ assertEquals(source, dest);
+ }
}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 036c5dcc8926..d1a06925a902 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -20,6 +20,7 @@ import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
+import static android.net.ConnectivityManager.NETID_UNSET;
import static android.net.ConnectivityManager.TYPE_ETHERNET;
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_MOBILE_FOTA;
@@ -123,7 +124,7 @@ import android.net.NetworkMisc;
import android.net.NetworkParcelable;
import android.net.NetworkRequest;
import android.net.NetworkSpecifier;
-import android.net.NetworkStack;
+import android.net.NetworkStackClient;
import android.net.NetworkUtils;
import android.net.ProxyInfo;
import android.net.RouteInfo;
@@ -194,6 +195,7 @@ import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
@@ -244,7 +246,7 @@ public class ConnectivityServiceTest {
@Mock INetworkStatsService mStatsService;
@Mock INetworkPolicyManager mNpm;
@Mock INetd mMockNetd;
- @Mock NetworkStack mNetworkStack;
+ @Mock NetworkStackClient mNetworkStack;
private ArgumentCaptor<String[]> mStringArrayCaptor = ArgumentCaptor.forClass(String[].class);
@@ -500,17 +502,17 @@ public class ConnectivityServiceTest {
public void unwanted() { mDisconnected.open(); }
@Override
- public void startPacketKeepalive(Message msg) {
+ public void startSocketKeepalive(Message msg) {
int slot = msg.arg1;
if (mExpectedKeepaliveSlot != null) {
assertEquals((int) mExpectedKeepaliveSlot, slot);
}
- onPacketKeepaliveEvent(slot, mStartKeepaliveError);
+ onSocketKeepaliveEvent(slot, mStartKeepaliveError);
}
@Override
- public void stopPacketKeepalive(Message msg) {
- onPacketKeepaliveEvent(msg.arg1, mStopKeepaliveError);
+ public void stopSocketKeepalive(Message msg) {
+ onSocketKeepaliveEvent(msg.arg1, mStopKeepaliveError);
}
@Override
@@ -553,7 +555,7 @@ public class ConnectivityServiceTest {
if (mNmValidationRedirectUrl != null) {
mNmCallbacks.showProvisioningNotification(
- "test_provisioning_notif_action");
+ "test_provisioning_notif_action", "com.android.test.package");
mNmProvNotificationRequested = true;
}
} catch (RemoteException e) {
@@ -884,11 +886,14 @@ public class ConnectivityServiceTest {
public void setUids(Set<UidRange> uids) {
mNetworkCapabilities.setUids(uids);
- updateCapabilities();
+ updateCapabilities(null /* defaultNetwork */);
}
@Override
public int getNetId() {
+ if (mMockNetworkAgent == null) {
+ return NETID_UNSET;
+ }
return mMockNetworkAgent.getNetwork().netId;
}
@@ -906,15 +911,17 @@ public class ConnectivityServiceTest {
mNetworkCapabilities.set(mMockNetworkAgent.getNetworkCapabilities());
mConnected = true;
mConfig = new VpnConfig();
+ mConfig.isMetered = false;
}
@Override
- public void updateCapabilities() {
- if (!mConnected) return;
- super.updateCapabilities();
- // Because super.updateCapabilities will update the capabilities of the agent but not
- // the mock agent, the mock agent needs to know about them.
+ public NetworkCapabilities updateCapabilities(Network defaultNetwork) {
+ if (!mConnected) return null;
+ super.updateCapabilities(defaultNetwork);
+ // Because super.updateCapabilities will update the capabilities of the agent but
+ // not the mock agent, the mock agent needs to know about them.
copyCapabilitiesToNetworkAgent();
+ return new NetworkCapabilities(mNetworkCapabilities);
}
private void copyCapabilitiesToNetworkAgent() {
@@ -1059,6 +1066,11 @@ public class ConnectivityServiceTest {
}
@Override
+ protected NetworkStackClient getNetworkStack() {
+ return mNetworkStack;
+ }
+
+ @Override
public WakeupMessage makeWakeupMessage(
Context context, Handler handler, String cmdName, int cmd, Object obj) {
return new FakeWakeupMessage(context, handler, cmdName, cmd, 0, 0, obj);
@@ -3789,11 +3801,21 @@ public class ConnectivityServiceTest {
}
@Test
- public void testNattSocketKeepalives() throws Exception {
+ public void testNattSocketKeepalives_SingleThreadExecutor() throws Exception {
+ final ExecutorService executorSingleThread = Executors.newSingleThreadExecutor();
+ doTestNattSocketKeepalivesWithExecutor(executorSingleThread);
+ executorSingleThread.shutdown();
+ }
+
+ @Test
+ public void testNattSocketKeepalives_InlineExecutor() throws Exception {
+ final Executor executorInline = (Runnable r) -> r.run();
+ doTestNattSocketKeepalivesWithExecutor(executorInline);
+ }
+
+ private void doTestNattSocketKeepalivesWithExecutor(Executor executor) throws Exception {
// TODO: 1. Move this outside of ConnectivityServiceTest.
- // 2. Add helper function to test against newSingleThreadExecutor as well as inline
- // executor.
- // 3. Make test to verify that Nat-T keepalive socket is created by IpSecService.
+ // 2. Make test to verify that Nat-T keepalive socket is created by IpSecService.
final int srcPort = 12345;
final InetAddress myIPv4 = InetAddress.getByName("192.0.2.129");
final InetAddress notMyIPv4 = InetAddress.getByName("192.0.2.35");
@@ -3807,8 +3829,6 @@ public class ConnectivityServiceTest {
final IpSecManager mIpSec = (IpSecManager) mContext.getSystemService(Context.IPSEC_SERVICE);
final UdpEncapsulationSocket testSocket = mIpSec.openUdpEncapsulationSocket(srcPort);
- final Executor executor = Executors.newSingleThreadExecutor();
-
LinkProperties lp = new LinkProperties();
lp.setInterfaceName("wlan12");
lp.addLinkAddress(new LinkAddress(myIPv6, 64));
@@ -3925,6 +3945,12 @@ public class ConnectivityServiceTest {
ka2.stop();
callback2.expectStopped();
+
+ testSocket.close();
+ testSocket2.close();
+
+ mWiFiNetworkAgent.disconnect();
+ waitFor(mWiFiNetworkAgent.getDisconnectedCV());
}
@Test
@@ -4648,6 +4674,7 @@ public class ConnectivityServiceTest {
vpnNetworkAgent.connect(false);
mMockVpn.connect();
+ mMockVpn.setUnderlyingNetworks(new Network[0]);
genericNetworkCallback.expectAvailableCallbacksUnvalidated(vpnNetworkAgent);
genericNotVpnNetworkCallback.assertNoCallback();
@@ -4680,6 +4707,7 @@ public class ConnectivityServiceTest {
ranges.add(new UidRange(uid, uid));
mMockVpn.setUids(ranges);
+ vpnNetworkAgent.setUids(ranges);
genericNetworkCallback.expectAvailableCallbacksValidated(vpnNetworkAgent);
genericNotVpnNetworkCallback.assertNoCallback();
@@ -4713,12 +4741,11 @@ public class ConnectivityServiceTest {
}
@Test
- public void testVpnWithAndWithoutInternet() {
+ public void testVpnWithoutInternet() {
final int uid = Process.myUid();
final TestNetworkCallback defaultCallback = new TestNetworkCallback();
mCm.registerDefaultNetworkCallback(defaultCallback);
- defaultCallback.assertNoCallback();
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(true);
@@ -4740,11 +4767,30 @@ public class ConnectivityServiceTest {
vpnNetworkAgent.disconnect();
defaultCallback.assertNoCallback();
- vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+ mCm.unregisterNetworkCallback(defaultCallback);
+ }
+
+ @Test
+ public void testVpnWithInternet() {
+ final int uid = Process.myUid();
+
+ final TestNetworkCallback defaultCallback = new TestNetworkCallback();
+ mCm.registerDefaultNetworkCallback(defaultCallback);
+
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.connect(true);
+
+ defaultCallback.expectAvailableThenValidatedCallbacks(mWiFiNetworkAgent);
+ assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
+
+ MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+ final ArraySet<UidRange> ranges = new ArraySet<>();
+ ranges.add(new UidRange(uid, uid));
mMockVpn.setNetworkAgent(vpnNetworkAgent);
mMockVpn.setUids(ranges);
vpnNetworkAgent.connect(true /* validated */, true /* hasInternet */);
mMockVpn.connect();
+
defaultCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
assertEquals(defaultCallback.getLastAvailableNetwork(), mCm.getActiveNetwork());
@@ -4752,14 +4798,6 @@ public class ConnectivityServiceTest {
defaultCallback.expectCallback(CallbackState.LOST, vpnNetworkAgent);
defaultCallback.expectAvailableCallbacksValidated(mWiFiNetworkAgent);
- vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
- ranges.clear();
- mMockVpn.setNetworkAgent(vpnNetworkAgent);
- mMockVpn.setUids(ranges);
- vpnNetworkAgent.connect(false /* validated */, true /* hasInternet */);
- mMockVpn.connect();
- defaultCallback.assertNoCallback();
-
mCm.unregisterNetworkCallback(defaultCallback);
}
@@ -4862,6 +4900,70 @@ public class ConnectivityServiceTest {
}
@Test
+ public void testNullUnderlyingNetworks() {
+ final int uid = Process.myUid();
+
+ final TestNetworkCallback vpnNetworkCallback = new TestNetworkCallback();
+ final NetworkRequest vpnNetworkRequest = new NetworkRequest.Builder()
+ .removeCapability(NET_CAPABILITY_NOT_VPN)
+ .addTransportType(TRANSPORT_VPN)
+ .build();
+ NetworkCapabilities nc;
+ mCm.registerNetworkCallback(vpnNetworkRequest, vpnNetworkCallback);
+ vpnNetworkCallback.assertNoCallback();
+
+ final MockNetworkAgent vpnNetworkAgent = new MockNetworkAgent(TRANSPORT_VPN);
+ final ArraySet<UidRange> ranges = new ArraySet<>();
+ ranges.add(new UidRange(uid, uid));
+ mMockVpn.setNetworkAgent(vpnNetworkAgent);
+ mMockVpn.connect();
+ mMockVpn.setUids(ranges);
+ vpnNetworkAgent.connect(true /* validated */, false /* hasInternet */);
+
+ vpnNetworkCallback.expectAvailableThenValidatedCallbacks(vpnNetworkAgent);
+ nc = mCm.getNetworkCapabilities(vpnNetworkAgent.getNetwork());
+ assertTrue(nc.hasTransport(TRANSPORT_VPN));
+ assertFalse(nc.hasTransport(TRANSPORT_CELLULAR));
+ assertFalse(nc.hasTransport(TRANSPORT_WIFI));
+ // By default, VPN is set to track default network (i.e. its underlying networks is null).
+ // In case of no default network, VPN is considered metered.
+ assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_METERED));
+
+ // Connect to Cell; Cell is the default network.
+ mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(true);
+
+ vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN)
+ && caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
+ && !caps.hasCapability(NET_CAPABILITY_NOT_METERED),
+ vpnNetworkAgent);
+
+ // Connect to WiFi; WiFi is the new default.
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.addCapability(NET_CAPABILITY_NOT_METERED);
+ mWiFiNetworkAgent.connect(true);
+
+ vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN)
+ && !caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
+ && caps.hasCapability(NET_CAPABILITY_NOT_METERED),
+ vpnNetworkAgent);
+
+ // Disconnect Cell. The default network did not change, so there shouldn't be any changes in
+ // the capabilities.
+ mCellNetworkAgent.disconnect();
+
+ // Disconnect wifi too. Now we have no default network.
+ mWiFiNetworkAgent.disconnect();
+
+ vpnNetworkCallback.expectCapabilitiesLike((caps) -> caps.hasTransport(TRANSPORT_VPN)
+ && !caps.hasTransport(TRANSPORT_CELLULAR) && !caps.hasTransport(TRANSPORT_WIFI)
+ && !caps.hasCapability(NET_CAPABILITY_NOT_METERED),
+ vpnNetworkAgent);
+
+ mMockVpn.disconnect();
+ }
+
+ @Test
public void testNetworkBlockedStatus() {
final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
final NetworkRequest cellRequest = new NetworkRequest.Builder()
diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
index 9578ded1a089..aa6cbda0479d 100644
--- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
@@ -37,7 +37,6 @@ import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.net.NetworkMisc;
-import android.net.NetworkStack;
import android.os.INetworkManagementService;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
@@ -74,16 +73,12 @@ public class LingerMonitorTest {
@Mock NetworkMisc mMisc;
@Mock NetworkNotificationManager mNotifier;
@Mock Resources mResources;
- @Mock NetworkStack mNetworkStack;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mCtx.getResources()).thenReturn(mResources);
when(mCtx.getPackageName()).thenReturn("com.android.server.connectivity");
- when(mCtx.getSystemServiceName(NetworkStack.class))
- .thenReturn(Context.NETWORK_STACK_SERVICE);
- when(mCtx.getSystemService(Context.NETWORK_STACK_SERVICE)).thenReturn(mNetworkStack);
mMonitor = new TestableLingerMonitor(mCtx, mNotifier, HIGH_DAILY_LIMIT, HIGH_RATE_LIMIT);
}
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index b6356076db60..533d7ad2a472 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -35,6 +35,7 @@ import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY;
import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED;
import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
import static android.provider.Settings.Global.TETHER_ENABLE_LEGACY_DHCP_SERVER;
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
@@ -194,10 +195,6 @@ public class TetheringTest {
}
public class MockIpServerDependencies extends IpServer.Dependencies {
- MockIpServerDependencies() {
- super(null);
- }
-
@Override
public RouterAdvertisementDaemon getRouterAdvertisementDaemon(
InterfaceParams ifParams) {
@@ -265,7 +262,7 @@ public class TetheringTest {
}
@Override
- public IpServer.Dependencies getIpServerDependencies(Context context) {
+ public IpServer.Dependencies getIpServerDependencies() {
return mIpServerDependencies;
}
@@ -274,6 +271,11 @@ public class TetheringTest {
isTetheringSupportedCalls++;
return true;
}
+
+ @Override
+ public int getDefaultDataSubscriptionId() {
+ return INVALID_SUBSCRIPTION_ID;
+ }
}
private static NetworkState buildMobileUpstreamState(boolean withIPv4, boolean withIPv6,
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index 5b17224e41e5..f169d6b5bee3 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -168,6 +168,8 @@ public class VpnTest {
ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.targetSdkVersion = VERSION_CODES.CUR_DEVELOPMENT;
when(mContext.getApplicationInfo()).thenReturn(applicationInfo);
+ when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
+ .thenReturn(applicationInfo);
doNothing().when(mNetService).registerObserver(any());
}
@@ -544,23 +546,28 @@ public class VpnTest {
final Network wifi = new Network(2);
final Map<Network, NetworkCapabilities> networks = new HashMap<>();
- networks.put(mobile, new NetworkCapabilities()
- .addTransportType(TRANSPORT_CELLULAR)
- .addCapability(NET_CAPABILITY_INTERNET)
- .addCapability(NET_CAPABILITY_NOT_METERED)
- .addCapability(NET_CAPABILITY_NOT_CONGESTED)
- .setLinkDownstreamBandwidthKbps(10));
- networks.put(wifi, new NetworkCapabilities()
- .addTransportType(TRANSPORT_WIFI)
- .addCapability(NET_CAPABILITY_INTERNET)
- .addCapability(NET_CAPABILITY_NOT_ROAMING)
- .addCapability(NET_CAPABILITY_NOT_CONGESTED)
- .setLinkUpstreamBandwidthKbps(20));
+ networks.put(
+ mobile,
+ new NetworkCapabilities()
+ .addTransportType(TRANSPORT_CELLULAR)
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .addCapability(NET_CAPABILITY_NOT_CONGESTED)
+ .setLinkDownstreamBandwidthKbps(10));
+ networks.put(
+ wifi,
+ new NetworkCapabilities()
+ .addTransportType(TRANSPORT_WIFI)
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .addCapability(NET_CAPABILITY_NOT_METERED)
+ .addCapability(NET_CAPABILITY_NOT_ROAMING)
+ .addCapability(NET_CAPABILITY_NOT_CONGESTED)
+ .setLinkUpstreamBandwidthKbps(20));
setMockedNetworks(networks);
final NetworkCapabilities caps = new NetworkCapabilities();
- Vpn.updateCapabilities(mConnectivityManager, new Network[] { }, caps);
+ Vpn.applyUnderlyingCapabilities(
+ mConnectivityManager, new Network[] {}, caps, false /* isAlwaysMetered */);
assertTrue(caps.hasTransport(TRANSPORT_VPN));
assertFalse(caps.hasTransport(TRANSPORT_CELLULAR));
assertFalse(caps.hasTransport(TRANSPORT_WIFI));
@@ -570,17 +577,33 @@ public class VpnTest {
assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
- Vpn.updateCapabilities(mConnectivityManager, new Network[] { mobile }, caps);
+ Vpn.applyUnderlyingCapabilities(
+ mConnectivityManager,
+ new Network[] {mobile},
+ caps,
+ false /* isAlwaysMetered */);
assertTrue(caps.hasTransport(TRANSPORT_VPN));
assertTrue(caps.hasTransport(TRANSPORT_CELLULAR));
assertFalse(caps.hasTransport(TRANSPORT_WIFI));
assertEquals(10, caps.getLinkDownstreamBandwidthKbps());
assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkUpstreamBandwidthKbps());
- assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
+ assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
- Vpn.updateCapabilities(mConnectivityManager, new Network[] { wifi }, caps);
+ Vpn.applyUnderlyingCapabilities(
+ mConnectivityManager, new Network[] {wifi}, caps, false /* isAlwaysMetered */);
+ assertTrue(caps.hasTransport(TRANSPORT_VPN));
+ assertFalse(caps.hasTransport(TRANSPORT_CELLULAR));
+ assertTrue(caps.hasTransport(TRANSPORT_WIFI));
+ assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkDownstreamBandwidthKbps());
+ assertEquals(20, caps.getLinkUpstreamBandwidthKbps());
+ assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
+ assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
+ assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
+
+ Vpn.applyUnderlyingCapabilities(
+ mConnectivityManager, new Network[] {wifi}, caps, true /* isAlwaysMetered */);
assertTrue(caps.hasTransport(TRANSPORT_VPN));
assertFalse(caps.hasTransport(TRANSPORT_CELLULAR));
assertTrue(caps.hasTransport(TRANSPORT_WIFI));
@@ -590,7 +613,11 @@ public class VpnTest {
assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_CONGESTED));
- Vpn.updateCapabilities(mConnectivityManager, new Network[] { mobile, wifi }, caps);
+ Vpn.applyUnderlyingCapabilities(
+ mConnectivityManager,
+ new Network[] {mobile, wifi},
+ caps,
+ false /* isAlwaysMetered */);
assertTrue(caps.hasTransport(TRANSPORT_VPN));
assertTrue(caps.hasTransport(TRANSPORT_CELLULAR));
assertTrue(caps.hasTransport(TRANSPORT_WIFI));
diff --git a/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java b/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java
index 0f72229d38e6..193f3806dbf6 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/EntitlementManagerTest.java
@@ -16,8 +16,17 @@
package com.android.server.connectivity.tethering;
+import static android.net.ConnectivityManager.TETHERING_USB;
+import static android.net.ConnectivityManager.TETHERING_WIFI;
+import static android.net.ConnectivityManager.TETHER_ERROR_ENTITLEMENT_UNKONWN;
+import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
+import static android.net.ConnectivityManager.TETHER_ERROR_PROVISION_FAILED;
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import static org.mockito.Matchers.anyBoolean;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
@@ -27,12 +36,22 @@ import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Resources;
import android.net.util.SharedLog;
+import android.os.Bundle;
+import android.os.Message;
import android.os.PersistableBundle;
+import android.os.ResultReceiver;
+import android.os.test.TestLooper;
+import android.provider.Settings;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.telephony.CarrierConfigManager;
+import android.test.mock.MockContentResolver;
import com.android.internal.R;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+import com.android.internal.util.test.BroadcastInterceptingContext;
+import com.android.internal.util.test.FakeSettingsProvider;
import com.android.server.connectivity.MockableSystemProperties;
import org.junit.After;
@@ -42,6 +61,10 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
public final class EntitlementManagerTest {
@@ -51,7 +74,6 @@ public final class EntitlementManagerTest {
@Mock private CarrierConfigManager mCarrierConfigManager;
@Mock private Context mContext;
- @Mock private ContentResolver mContent;
@Mock private MockableSystemProperties mSystemProperties;
@Mock private Resources mResources;
@Mock private SharedLog mLog;
@@ -59,15 +81,49 @@ public final class EntitlementManagerTest {
// Like so many Android system APIs, these cannot be mocked because it is marked final.
// We have to use the real versions.
private final PersistableBundle mCarrierConfig = new PersistableBundle();
+ private final TestLooper mLooper = new TestLooper();
+ private Context mMockContext;
+ private MockContentResolver mContentResolver;
+
+ private TestStateMachine mSM;
+ private WrappedEntitlementManager mEnMgr;
- private EntitlementManager mEnMgr;
+ private class MockContext extends BroadcastInterceptingContext {
+ MockContext(Context base) {
+ super(base);
+ }
+
+ @Override
+ public Resources getResources() {
+ return mResources;
+ }
+
+ @Override
+ public ContentResolver getContentResolver() {
+ return mContentResolver;
+ }
+ }
+
+ public class WrappedEntitlementManager extends EntitlementManager {
+ public int fakeEntitlementResult = TETHER_ERROR_ENTITLEMENT_UNKONWN;
+ public boolean everRunUiEntitlement = false;
+
+ public WrappedEntitlementManager(Context ctx, StateMachine target,
+ SharedLog log, MockableSystemProperties systemProperties) {
+ super(ctx, target, log, systemProperties);
+ }
+
+ @Override
+ protected void runUiTetherProvisioning(int type, ResultReceiver receiver) {
+ everRunUiEntitlement = true;
+ receiver.send(fakeEntitlementResult, null);
+ }
+ }
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- when(mContext.getResources()).thenReturn(mResources);
- when(mContext.getContentResolver()).thenReturn(mContent);
when(mResources.getStringArray(R.array.config_tether_dhcp_range))
.thenReturn(new String[0]);
when(mResources.getStringArray(R.array.config_tether_usb_regexs))
@@ -80,12 +136,22 @@ public final class EntitlementManagerTest {
.thenReturn(new int[0]);
when(mLog.forSubComponent(anyString())).thenReturn(mLog);
- mEnMgr = new EntitlementManager(mContext, mLog, mSystemProperties);
- mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+ mContentResolver = new MockContentResolver();
+ mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+ mMockContext = new MockContext(mContext);
+ mSM = new TestStateMachine();
+ mEnMgr = new WrappedEntitlementManager(mMockContext, mSM, mLog, mSystemProperties);
+ mEnMgr.updateConfiguration(
+ new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID));
}
@After
- public void tearDown() throws Exception {}
+ public void tearDown() throws Exception {
+ if (mSM != null) {
+ mSM.quit();
+ mSM = null;
+ }
+ }
private void setupForRequiredProvisioning() {
// Produce some acceptable looking provision app setting if requested.
@@ -104,7 +170,8 @@ public final class EntitlementManagerTest {
@Test
public void canRequireProvisioning() {
setupForRequiredProvisioning();
- mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+ mEnMgr.updateConfiguration(
+ new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID));
assertTrue(mEnMgr.isTetherProvisioningRequired());
}
@@ -113,7 +180,8 @@ public final class EntitlementManagerTest {
setupForRequiredProvisioning();
when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
.thenReturn(null);
- mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+ mEnMgr.updateConfiguration(
+ new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID));
// Couldn't get the CarrierConfigManager, but still had a declared provisioning app.
// Therefore provisioning still be required.
assertTrue(mEnMgr.isTetherProvisioningRequired());
@@ -123,7 +191,8 @@ public final class EntitlementManagerTest {
public void toleratesCarrierConfigMissing() {
setupForRequiredProvisioning();
when(mCarrierConfigManager.getConfig()).thenReturn(null);
- mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+ mEnMgr.updateConfiguration(
+ new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID));
// We still have a provisioning app configured, so still require provisioning.
assertTrue(mEnMgr.isTetherProvisioningRequired());
}
@@ -133,12 +202,146 @@ public final class EntitlementManagerTest {
setupForRequiredProvisioning();
when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
.thenReturn(null);
- mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+ mEnMgr.updateConfiguration(
+ new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID));
assertFalse(mEnMgr.isTetherProvisioningRequired());
when(mResources.getStringArray(R.array.config_mobile_hotspot_provision_app))
.thenReturn(new String[] {"malformedApp"});
- mEnMgr.updateConfiguration(new TetheringConfiguration(mContext, mLog));
+ mEnMgr.updateConfiguration(
+ new TetheringConfiguration(mMockContext, mLog, INVALID_SUBSCRIPTION_ID));
assertFalse(mEnMgr.isTetherProvisioningRequired());
}
+ @Test
+ public void testGetLastEntitlementCacheValue() throws Exception {
+ final CountDownLatch mCallbacklatch = new CountDownLatch(1);
+ // 1. Entitlement check is not required.
+ mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
+ mEnMgr.everRunUiEntitlement = false;
+ ResultReceiver receiver = new ResultReceiver(null) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ assertEquals(TETHER_ERROR_NO_ERROR, resultCode);
+ mCallbacklatch.countDown();
+ }
+ };
+ mEnMgr.getLatestTetheringEntitlementValue(TETHERING_WIFI, receiver, true);
+ callbackTimeoutHelper(mCallbacklatch);
+ assertFalse(mEnMgr.everRunUiEntitlement);
+
+ setupForRequiredProvisioning();
+ mEnMgr.updateConfiguration(new TetheringConfiguration(mMockContext, mLog,
+ INVALID_SUBSCRIPTION_ID));
+ // 2. No cache value and don't need to run entitlement check.
+ mEnMgr.everRunUiEntitlement = false;
+ receiver = new ResultReceiver(null) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ assertEquals(TETHER_ERROR_ENTITLEMENT_UNKONWN, resultCode);
+ mCallbacklatch.countDown();
+ }
+ };
+ mEnMgr.getLatestTetheringEntitlementValue(TETHERING_WIFI, receiver, false);
+ callbackTimeoutHelper(mCallbacklatch);
+ assertFalse(mEnMgr.everRunUiEntitlement);
+ // 3. No cache value and ui entitlement check is needed.
+ mEnMgr.fakeEntitlementResult = TETHER_ERROR_PROVISION_FAILED;
+ mEnMgr.everRunUiEntitlement = false;
+ receiver = new ResultReceiver(null) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ assertEquals(TETHER_ERROR_PROVISION_FAILED, resultCode);
+ mCallbacklatch.countDown();
+ }
+ };
+ mEnMgr.getLatestTetheringEntitlementValue(TETHERING_WIFI, receiver, true);
+ mLooper.dispatchAll();
+ callbackTimeoutHelper(mCallbacklatch);
+ assertTrue(mEnMgr.everRunUiEntitlement);
+ // 4. Cache value is TETHER_ERROR_PROVISION_FAILED and don't need to run entitlement check.
+ mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
+ mEnMgr.everRunUiEntitlement = false;
+ receiver = new ResultReceiver(null) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ assertEquals(TETHER_ERROR_PROVISION_FAILED, resultCode);
+ mCallbacklatch.countDown();
+ }
+ };
+ mEnMgr.getLatestTetheringEntitlementValue(TETHERING_WIFI, receiver, false);
+ callbackTimeoutHelper(mCallbacklatch);
+ assertFalse(mEnMgr.everRunUiEntitlement);
+ // 5. Cache value is TETHER_ERROR_PROVISION_FAILED and ui entitlement check is needed.
+ mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
+ mEnMgr.everRunUiEntitlement = false;
+ receiver = new ResultReceiver(null) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ assertEquals(TETHER_ERROR_NO_ERROR, resultCode);
+ mCallbacklatch.countDown();
+ }
+ };
+ mEnMgr.getLatestTetheringEntitlementValue(TETHERING_WIFI, receiver, true);
+ mLooper.dispatchAll();
+ callbackTimeoutHelper(mCallbacklatch);
+ assertTrue(mEnMgr.everRunUiEntitlement);
+ // 6. Cache value is TETHER_ERROR_NO_ERROR.
+ mEnMgr.fakeEntitlementResult = TETHER_ERROR_NO_ERROR;
+ mEnMgr.everRunUiEntitlement = false;
+ receiver = new ResultReceiver(null) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ assertEquals(TETHER_ERROR_NO_ERROR, resultCode);
+ mCallbacklatch.countDown();
+ }
+ };
+ mEnMgr.getLatestTetheringEntitlementValue(TETHERING_WIFI, receiver, true);
+ callbackTimeoutHelper(mCallbacklatch);
+ assertFalse(mEnMgr.everRunUiEntitlement);
+ // 7. Test get value for other downstream type.
+ mEnMgr.everRunUiEntitlement = false;
+ receiver = new ResultReceiver(null) {
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ assertEquals(TETHER_ERROR_ENTITLEMENT_UNKONWN, resultCode);
+ mCallbacklatch.countDown();
+ }
+ };
+ mEnMgr.getLatestTetheringEntitlementValue(TETHERING_USB, receiver, false);
+ callbackTimeoutHelper(mCallbacklatch);
+ assertFalse(mEnMgr.everRunUiEntitlement);
+ }
+
+ void callbackTimeoutHelper(final CountDownLatch latch) throws Exception {
+ if (!latch.await(1, TimeUnit.SECONDS)) {
+ fail("Timout, fail to recieve callback");
+ }
+ }
+ public class TestStateMachine extends StateMachine {
+ public final ArrayList<Message> messages = new ArrayList<>();
+ private final State mLoggingState =
+ new EntitlementManagerTest.TestStateMachine.LoggingState();
+
+ class LoggingState extends State {
+ @Override public void enter() {
+ messages.clear();
+ }
+
+ @Override public void exit() {
+ messages.clear();
+ }
+
+ @Override public boolean processMessage(Message msg) {
+ messages.add(msg);
+ return false;
+ }
+ }
+
+ public TestStateMachine() {
+ super("EntitlementManagerTest.TestStateMachine", mLooper.getLooper());
+ addState(mLoggingState);
+ setInitialState(mLoggingState);
+ super.start();
+ }
+ }
}
diff --git a/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java b/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
index 521778484d91..01b904d8f088 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java
@@ -22,10 +22,12 @@ import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.provider.Settings.Global.TETHER_ENABLE_LEGACY_DHCP_SERVER;
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static com.android.server.connectivity.tethering.TetheringConfiguration.DUN_NOT_REQUIRED;
import static com.android.server.connectivity.tethering.TetheringConfiguration.DUN_REQUIRED;
import static com.android.server.connectivity.tethering.TetheringConfiguration.DUN_UNSPECIFIED;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -44,26 +46,39 @@ import android.test.mock.MockContentResolver;
import com.android.internal.util.test.BroadcastInterceptingContext;
import com.android.internal.util.test.FakeSettingsProvider;
-import java.util.Iterator;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Iterator;
@RunWith(AndroidJUnit4.class)
@SmallTest
public class TetheringConfigurationTest {
private final SharedLog mLog = new SharedLog("TetheringConfigurationTest");
+
+ private static final String[] PROVISIONING_APP_NAME = {"some", "app"};
@Mock private Context mContext;
@Mock private TelephonyManager mTelephonyManager;
@Mock private Resources mResources;
+ @Mock private Resources mResourcesForSubId;
private MockContentResolver mContentResolver;
private Context mMockContext;
private boolean mHasTelephonyManager;
+ private class MockTetheringConfiguration extends TetheringConfiguration {
+ MockTetheringConfiguration(Context ctx, SharedLog log, int id) {
+ super(ctx, log, id);
+ }
+
+ @Override
+ protected Resources getResourcesForSubIdWrapper(Context ctx, int subId) {
+ return mResourcesForSubId;
+ }
+ }
+
private class MockContext extends BroadcastInterceptingContext {
MockContext(Context base) {
super(base);
@@ -99,6 +114,9 @@ public class TetheringConfigurationTest {
.thenReturn(new String[0]);
when(mResources.getIntArray(com.android.internal.R.array.config_tether_upstream_types))
.thenReturn(new int[0]);
+ when(mResources.getStringArray(
+ com.android.internal.R.array.config_mobile_hotspot_provision_app))
+ .thenReturn(new String[0]);
mContentResolver = new MockContentResolver();
mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
mMockContext = new MockContext(mContext);
@@ -111,7 +129,8 @@ public class TetheringConfigurationTest {
mHasTelephonyManager = true;
when(mTelephonyManager.getTetherApnRequired()).thenReturn(DUN_REQUIRED);
- final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog);
+ final TetheringConfiguration cfg = new TetheringConfiguration(
+ mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
assertTrue(cfg.isDunRequired);
assertTrue(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN));
assertFalse(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE));
@@ -127,7 +146,8 @@ public class TetheringConfigurationTest {
mHasTelephonyManager = true;
when(mTelephonyManager.getTetherApnRequired()).thenReturn(DUN_NOT_REQUIRED);
- final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog);
+ final TetheringConfiguration cfg = new TetheringConfiguration(
+ mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
assertFalse(cfg.isDunRequired);
assertFalse(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN));
assertTrue(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE));
@@ -143,7 +163,8 @@ public class TetheringConfigurationTest {
mHasTelephonyManager = false;
when(mTelephonyManager.getTetherApnRequired()).thenReturn(DUN_UNSPECIFIED);
- final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog);
+ final TetheringConfiguration cfg = new TetheringConfiguration(
+ mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
assertTrue(cfg.isDunRequired);
assertTrue(cfg.preferredUpstreamIfaceTypes.contains(TYPE_MOBILE_DUN));
// Just to prove we haven't clobbered Wi-Fi:
@@ -160,7 +181,8 @@ public class TetheringConfigurationTest {
mHasTelephonyManager = false;
when(mTelephonyManager.getTetherApnRequired()).thenReturn(DUN_UNSPECIFIED);
- final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog);
+ final TetheringConfiguration cfg = new TetheringConfiguration(
+ mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
final Iterator<Integer> upstreamIterator = cfg.preferredUpstreamIfaceTypes.iterator();
assertTrue(upstreamIterator.hasNext());
assertEquals(TYPE_ETHERNET, upstreamIterator.next().intValue());
@@ -181,7 +203,8 @@ public class TetheringConfigurationTest {
mHasTelephonyManager = false;
when(mTelephonyManager.getTetherApnRequired()).thenReturn(DUN_UNSPECIFIED);
- final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog);
+ final TetheringConfiguration cfg = new TetheringConfiguration(
+ mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
final Iterator<Integer> upstreamIterator = cfg.preferredUpstreamIfaceTypes.iterator();
assertTrue(upstreamIterator.hasNext());
assertEquals(TYPE_ETHERNET, upstreamIterator.next().intValue());
@@ -199,7 +222,8 @@ public class TetheringConfigurationTest {
mHasTelephonyManager = false;
when(mTelephonyManager.getTetherApnRequired()).thenReturn(DUN_UNSPECIFIED);
- final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog);
+ final TetheringConfiguration cfg = new TetheringConfiguration(
+ mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
final Iterator<Integer> upstreamIterator = cfg.preferredUpstreamIfaceTypes.iterator();
assertTrue(upstreamIterator.hasNext());
assertEquals(TYPE_WIFI, upstreamIterator.next().intValue());
@@ -214,7 +238,8 @@ public class TetheringConfigurationTest {
public void testNewDhcpServerDisabled() {
Settings.Global.putInt(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, 1);
- final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog);
+ final TetheringConfiguration cfg = new TetheringConfiguration(
+ mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
assertTrue(cfg.enableLegacyDhcpServer);
}
@@ -222,7 +247,41 @@ public class TetheringConfigurationTest {
public void testNewDhcpServerEnabled() {
Settings.Global.putInt(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, 0);
- final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog);
+ final TetheringConfiguration cfg = new TetheringConfiguration(
+ mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
assertFalse(cfg.enableLegacyDhcpServer);
}
+
+ @Test
+ public void testGetResourcesBySubId() {
+ setUpResourceForSubId();
+ final TetheringConfiguration cfg = new TetheringConfiguration(
+ mMockContext, mLog, INVALID_SUBSCRIPTION_ID);
+ assertTrue(cfg.provisioningApp.length == 0);
+ final int anyValidSubId = 1;
+ final MockTetheringConfiguration mockCfg =
+ new MockTetheringConfiguration(mMockContext, mLog, anyValidSubId);
+ assertEquals(mockCfg.provisioningApp[0], PROVISIONING_APP_NAME[0]);
+ assertEquals(mockCfg.provisioningApp[1], PROVISIONING_APP_NAME[1]);
+ }
+
+ private void setUpResourceForSubId() {
+ when(mResourcesForSubId.getStringArray(
+ com.android.internal.R.array.config_tether_dhcp_range)).thenReturn(new String[0]);
+ when(mResourcesForSubId.getStringArray(
+ com.android.internal.R.array.config_tether_usb_regexs)).thenReturn(new String[0]);
+ when(mResourcesForSubId.getStringArray(
+ com.android.internal.R.array.config_tether_wifi_regexs))
+ .thenReturn(new String[]{ "test_wlan\\d" });
+ when(mResourcesForSubId.getStringArray(
+ com.android.internal.R.array.config_tether_bluetooth_regexs))
+ .thenReturn(new String[0]);
+ when(mResourcesForSubId.getIntArray(
+ com.android.internal.R.array.config_tether_upstream_types))
+ .thenReturn(new int[0]);
+ when(mResourcesForSubId.getStringArray(
+ com.android.internal.R.array.config_mobile_hotspot_provision_app))
+ .thenReturn(PROVISIONING_APP_NAME);
+ }
+
}
diff --git a/tools/preload/Android.bp b/tools/preload/Android.bp
new file mode 100644
index 000000000000..809ee474969a
--- /dev/null
+++ b/tools/preload/Android.bp
@@ -0,0 +1,17 @@
+java_library_host {
+ name: "preload",
+ srcs: [
+ "Compile.java",
+ "LoadedClass.java",
+ "MemoryUsage.java",
+ "Operation.java",
+ "Policy.java",
+ "PrintCsv.java",
+ "PrintHtmlDiff.java",
+ "PrintPsTree.java",
+ "Proc.java",
+ "Record.java",
+ "Root.java",
+ "WritePreloadedClassFile.java",
+ ],
+}
diff --git a/tools/preload/Android.mk b/tools/preload/Android.mk
deleted file mode 100644
index 14a4547cccbf..000000000000
--- a/tools/preload/Android.mk
+++ /dev/null
@@ -1,23 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := \
- Compile.java \
- LoadedClass.java \
- MemoryUsage.java \
- Operation.java \
- Policy.java \
- PrintCsv.java \
- PrintHtmlDiff.java \
- PrintPsTree.java \
- Proc.java \
- Record.java \
- Root.java \
- WritePreloadedClassFile.java
-
-LOCAL_MODULE:= preload
-
-include $(BUILD_HOST_JAVA_LIBRARY)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tools/preload/loadclass/Android.bp b/tools/preload/loadclass/Android.bp
new file mode 100644
index 000000000000..6f12015dae2b
--- /dev/null
+++ b/tools/preload/loadclass/Android.bp
@@ -0,0 +1,4 @@
+java_test {
+ name: "loadclass",
+ srcs: ["**/*.java"],
+}
diff --git a/tools/preload/loadclass/Android.mk b/tools/preload/loadclass/Android.mk
deleted file mode 100644
index 65828be617df..000000000000
--- a/tools/preload/loadclass/Android.mk
+++ /dev/null
@@ -1,9 +0,0 @@
-LOCAL_PATH := $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_MODULE := loadclass
-
-include $(BUILD_JAVA_LIBRARY)